Getting an error with Twitter lib - javascript

In Google Apps Script I have this snippet of code in my project to send a tweet (also in jsBin):
function sendTweet(status) {
var twitterKeys= {
TWITTER_CONSUMER_KEY: "x",
TWITTER_CONSUMER_SECRET: "x",
TWITTER_ACCESS_TOKEN: "x",
TWITTER_ACCESS_SECRET: "x"
};
var props = PropertiesService.getScriptProperties();
props.setProperties(twitterKeys);
twit = new Twitter.OAuth(props);
var service = new Twitter.OAuth(props);
if ( service.hasAccess() ) {
var response = twit.sendTweet(status);
if (response) {
Logger.log("Tweet ID " + response.id_str);
} else {
// Tweet could not be sent
// Go to View -> Logs to see the error message
}
}
}
sendTweet("test");
But the problem I'm having is that I get this error:
TypeError: Cannot read property "text" from undefined. (line 293, file "twitter", project "Twitter lib")
Line 293 is from version 21 of the "Twitter lib" library (MKvHYYdYA4G5JJHj7hxIcoh8V4oX7X1M_).
The message "test" actually gets tweeted, despite that error. Does anyone know how to fix it?

Hi, author of Twitter Lib here. #Mogsdad pointed me here over Twitter. I think I know what's going on with your script, and it's a peculiarity of how Google Script works.
You have most of your code in a function that takes an argument, and then you have a call to the function at the top level of your script. What happens, when you go to the "Run" menu and select your sendTweet function, is that the script at the top level gets run before the selected function is executed, and the tweet would be sent at that time with the "test" text.
Then after that, sendTweet gets run with no arguments, meaning the status variable is undefined. You're sending an undefined value into twit.sendTweet(), causing the error you see.
What I'd recommend here is simply wrapping your last line of code into a function so you can call it from the Run menu, like this:
function sendTestTweet() {
sendTweet("test");
}

Just to recap, the error you've seen is:
TypeError: Cannot read property "text" from undefined. (line 293, file "twitter", project "Twitter lib")
That is in the sendTweet() method of the library, see below.
/**
* Upload a tweet to Twitter with optional media.
*
* #param {string | Tweet} tweet the status text to send as a Twitter update
* #param {optional object} params any additional parameters to send as part of the update post
* #return {object} the Twitter response as an object if successful, null otherwise
*/
OAuth.prototype.sendTweet = function(tweet, params) {
var i;
var payload = { //<=== 293
"status" : (tweet.text || tweet)
};
Your code invokes this method with a single string parameter, status, which is set to "test".
The author of the library allowed for the tweet parameter to be one of two things:
It can be an object with a text property containing the message to Tweet, or
It can be a string.
However, the way that's being handled checks for tweet.text first, then if that does not exist it checks for a string tweet. When tweet.text does not exist (i.e. when using just a string), that TypeError is thrown.
I've reached out to the library author so they can publish the fix. However, in the meantime you can send a Tweet object with a text property, or take a copy of the library and update it yourself.
Send Tweet object. The Tweet object is documented in the Twitter API v1.1 documentation, but since the only property involved in this operation is text, a simple change in your status function will do the trick. Just ensure that status is an object with a text property.
function sendTweet(status) {
if (typeof status === string)
status = {text:status};
...
Update the library yourself.
To avoid the error, and handle the parameter options properly, line 294 in the library should be:
"status" : (tweet.hasOwnProperty("text") ? tweet.text : tweet)
Or:
"status" : (typeof tweet === 'object' ? tweet.text : tweet)
You'll need to publish it, and update the library ID in your code, but that should take care of this problem. Once a library update is available, you can switch back.

Related

Extract from a NODE-RED string

I have my NODE-RED schemma:
following string result from my "Function" node my node:
msg.payload : string[63]
"{"random":{"date":"22:55","random":21},"time":{"time":"22:52"}}"
This is the code of my "Function Node":
msg.payload.random=context.global.randomandtime;
msg.payload.time=context.global.time;
return msg;
I need to put in "part of the string" (not all) like this =>{"date":"22:55","random":21} and show it in my browser like a webpage but not using html tags.
Like this:
22:55
21
Any help will be wellcome.
I have added template(Mustache) and I am traying to bring data to it,(Note:http response is already in schemme but not shown here)
I am traying to bring data here (template). But I get error.
The Mustache template body is:
This is the payload: {{#payload.randomandandtime.random}} !
But I have back this error back:
2017-5-18 16:18:00node: Mustachemsg : string[56]
"Unclosed section "payload.randomandandtime.random" at 59"
In browser I get
502 Bad Gateway: Registered endpoint failed to handle the request.
Even If I change it only payload.randomandandtime I get empty:
payload.randomandandtime
In browser & console:
Messsage received back: (empty)
This is the payload: !
Finally I solved in this way.
I make all in one Global varaible instead 2 global variables.
I passed it to mustache template and in Mustache I worked with context in order to get it.
General Scheme:
Then in recoverydata:
msg.payload = context.global.get("randomtime");
In My Mustache:
`{{#payload.random}}
Last random number request returned {{&payload.random}}, which was received
at {{&payload.randomtime.times}}{{/payload.random}}
{{/payload}}`
The resul of it is a Webservice not using HTML and this is:
url https://....../page
"Time last server time request received at 13:14 Last random number request returned 94, which was received at 13:14"

Reading from JSON returns undefined

I'm trying to read from a JSON that contains my error messages, so that in case I'd ever want to change what my error messages say, I'd just change the JSON instead of diving into my source code. Everything seems to be working fine...
var fs = require('fs')
console.log("Now reading from error messages configuration file...");
var errorMsgs = JSON.parse(fs.readFileSync('config/error_msgs.JSON'));
console.log(errorMsgs)
This is what's in errorMsgs.json:
{
"bad_password" : [
{
"error" : "Incorrect login credentials.",
"details" : "This happens if you either have an incorrect username or password. Please try again with different credentials."
}
],
"missing_fields" : [
{
"error" : "Credentials failed to parse.",
"details" : "This happens when one or more fields are missing (or have illegal characters in them), please try again with different credentials."
}
]
}
When I log to the console, errorMsgs displays fine. When I log one of the items that errorMsgs has (like bad_password), it also works fine, as in it displays the items that are nested inside. However, when I attempt to retrieve a specific value like errorMsgs.bad_password['error'], it returns undefined. I can't seem to figure it out. I tried dot notation (errorMsgs.bad_password.error), which returns undefined. I tried the method above (errorMsgs.bad_password['error']) which also returns undefined. Asking for the typeof of errorMsgs, it returns object, which I assume makes it not a string. Passing the value to a variable first and then logging the variable doesn't do anything either. Is node converting it to a string on-the-fly, causing it to return undefined, or am I just doing something wrong?
bad_password" : [
{
"error" : "Incorrect login credentials.",
"details" : "This happens if you either have an incorrect username or password. Please try again with different credentials."
}
],
Your nested object is contained in an array.
errorMsgs.bad_password[0]['error']
This is what you're looking for. Just grab the first value of the array

Avoid 'Cannot read property of undefined' error when mapping data

I'm receiving some data back from Google Books API calls and I'm mapping the data into a lighter array I can then work with. I'm using this line to map the data I'm interested into:
data.items.map(function(book) {
return { googleId : book.id, image : book.volumeInfo.imageLinks.thumbnail, authors : reduceAuthors(book.volumeInfo.authors), title : book.volumeInfo.title, subtitle : book.volumeInfo.subtitle, publishedDate : book.volumeInfo.publishedDate, publisher : book.volumeInfo.publisher}
});
Most of the times it works just fine, but some times one of the elements is missing, may it be imagelinks or authors therefore I get this error "Uncaught TypeError: Cannot read property of undefined".
What is the best way to avoid this error?
The only solution I have in mind is to first run a sanity check on the whole data and apply the map only after, but there maybe is a better solution? And wrapping everything in a try and catch throws me out of the mapping operation too early and doesn't complete with the rest of the data.
You can check it inside map() function, without handle previously the entire data. With a ternary conditional , something like this:
image : ((book.volumeInfo.imageLinks) ? book.volumeInfo.imageLinks.thumbnail : undefined)
So if book.volumeInfo.imageLinks is null/empty/undefined there aren't error and the value inserted will be undefined, otherwise catch the thumbnail value.

$.ajax responseText differs from responseJSON

I am seeing a difference in what is returned when comparing the responseJSON and responseText properties of my ajax request.
In the image below, you see that referencing the responseJSON excludes the "properties" property. When I parse and use the responseText, the "properties" property is included.
I've not encountered this kind of weirdness before, anyone have any suggestions or thoughts as to why this is occurring?
Edit 1: The properties object is undefined when attempting to access it directly from the responseJSON. See below image:
Edit 2: I am unable to replicate this issue in my jsFiddle (http://jsfiddle.net/madChemist/4hd9cz1g/). I am using jQuery version 2.1.4 incase that helps to diagnose things.
The responseJSON is equivalent to the parsed responseText in my fiddle:
Parsed responseText properties - {"border-color":"cyan","border-style":"dashed","border-width":"5px"}
Unoutched responseJSON properties - {"border-color":"cyan","border-style":"dashed","border-width":"5px"}
The two differences I see between my environment and this one are primarily that the ajax request type is a GET not POST. The data in the fiddle was trimmed compared to what I am loading in my environment. I tested both trimmed & untrimmed which produced no difference in my findings.
Edit 3: I've taken some screenshots of my code to demonstrate what I am attempting to do. The screenshots I took originally were logged while I had my breakpoint on the second assertion in the unit test module.
My tests are passing now. My issue may actually be with QUnit's scoping. The odd behavior above went away when I made these changes to my test:
QUnit.test("Method exportLayout", 2, function (assert) {
var done = assert.async();
$('body').append('<div id="application" style="display:none;"></div>');
var controller = new ApplicationController();
controller.createApplicationWorkspace();
var load = controller.loadLocalProject(); // Pulls in local JSON file
load.complete(function (request) {
var loaded = request.responseJSON; //$.parseJSON(request.responseText);
controller.setUpLayout($.parseJSON(request.responseText)); // Creates workspace from saved state
var result = controller.generateLayout(); // Generates Object. Data property should equal what was loaded in originally.
assert.ok(result !== undefined && result.data !== undefined, "Application controller generates layout response in a proper format.");
assert.deepEqual(loaded, result.data, "Freshly generated export is the same as initially loaded in import.");
// Teardown
setTimeout(function () {
controller.layout.destroy();
$('#application').remove();
done();
}, 0);
});
});
Specifically made this change:
controller.setUpLayout($.parseJSON(request.responseText));
The setUpLayout method is a few hundred lines long, but this is how it begins:
setUpLayout: function (loaded) {
if (typeof loaded !== 'object' || !loaded.hasOwnProperty('rows')) {
throw new Error('Invalid format for loaded paramater in setUpLayout method.');
}
var initial_mode = TE.Application.reqres.request('editor:mode');
TE.Application.vent.trigger('editor:toggle_mode', true);
var layout = $.extend({}, loaded);
var rowCollection = this.layout.editor.currentView.rows.currentView;
var rows = layout.rows; // Once stashed in a var,
...
// Doesn't make use of `loaded` parameter. Only modifications are to `layout` variable.
}
I feel like there may still be something else at work here, but I wanted to update this post with what I had just found. Hoping someone else may be able to notice something that I did not.

webOS/Ares : read JSON from URL, assign to label

I've used the webOS Ares tool to create a relatively simple App. It displays an image and underneath the image are two labels. One is static, and the other label should be updated with new information by tapping the image.
When I tap the image, I wish to obtain a JSON object via a URL (http://jonathanstark.com/card/api/latest). The typcial JSON that is returned looks like this:
{"balance":{"amount":"0","amount_formatted":"$0.00","balance_id":"28087","created_at":"2011-08-09T12:17:02-0700","message":"My balance is $0.00 as of Aug 9th at 3:17pm EDT (America\/New_York)"}}
I want to parse the JSON's "amount_formatted" field and assign the result to the dynamic label (called cardBalance in main-chrome.js). I know that the JSON should return a single object, per the API.
If that goes well, I will create an additional label and convert/assign the "created_at" field to an additional label, but I want to walk before I run.
I'm having some trouble using AJAX to get the JSON, parse the JSON, and assign a string to one of the labels.
After I get this working, I plan to see if I can load this result on the application's load instead of first requiring the user to tap.
So far, this is my code in the main-assistant.js file. jCard is the image.
Code:
function MainAssistant(argFromPusher) {}
MainAssistant.prototype = {
setup: function() {
Ares.setupSceneAssistant(this);
},
cleanup: function() {
Ares.cleanupSceneAssistant(this);
},
giveCoffeeTap: function(inSender, event) {
window.location = "http://jonathanstark.com/card/#give-a-coffee";
},
jcardImageTap: function(inSender, event) {
//get "amount_formatted" in JSON from http://jonathanstark.com/card/api/latest
//and assign it to the "updatedBalance" label.
// I need to use Ajax.Request here.
Mojo.Log.info("Requesting latest card balance from Jonathan's Card");
var balanceRequest = new Ajax.Request("http://jonathanstark.com/card/api/latest", {
method: 'get',
evalJSON: 'false',
onSuccess: this.balanceRequestSuccess.bind(this),
onFailure: this.balanceRequestFailure.bind(this)
});
//After I can get the balance working, also get "created_at", parse it, and reformat it in the local time prefs.
},
//Test
balanceRequestSuccess: function(balanceResponse) {
//Chrome says that the page is returning X-JSON.
balanceJSON = balanceResponse.headerJSON;
var balanceAmtFromWeb = balanceJSON.getElementsByTagName("amount_formatted");
Mojo.Log.info(balanceAmtFromWeb[0]);
//The label I wish to update is named "updatedBalance" in main-chrome.js
updatedBalance.label = balanceAmtFromWeb[0];
},
balanceRequestFailure: function(balanceResponse) {
Mojo.Log.info("Failed to get the card balance: " + balanceResponse.getAllHeaders());
Mojo.Log.info(balanceResponse.responseText);
Mojo.Controller.errorDialog("Failed to load the latest card balance.");
},
//End test
btnGiveCoffeeTap: function(inSender, event) {
window.location = "http://jonathanstark.com/card/#give-a-coffee";
}
};
Here is a screenshot of the application running in the Chrome browser:
In the browser, I get some additional errors that weren't present in the Ares log viewer:
XMLHttpRequest cannot load http://jonathanstark.com/card/api/latest. Origin https://ares.palm.com is not allowed by Access-Control-Allow-Origin.
and
Refused to get unsafe header "X-JSON"
Any assistance is appreciated.
Ajax is the right tool for the job. Since webOS comes packaged with the Prototype library, try using it's Ajax.Request function to do the job. To see some examples of it, you can check out the source code to a webOS app I wrote, Plogger, that accesses Blogger on webOS using Ajax calls. In particular, the source for my post-list-assistant is probably the cleanest to look at to get the idea.
Ajax is pretty much the way you want to get data, even if it sometimes feels like overkill, since it's one of the few ways you can get asynchronous behavior in JavaScript. Otherwise you'd end up with code that hangs the interface while waiting on a response from a server (JavaScript is single threaded).

Categories

Resources