typeof fails on trigger object property - google-apps-script

I am trying to add an dumpObject function to a Spreadsheet Container bound Script.
Ideally, it is for visibility into variables passed through triggers.
I can run it all day long from within the Script Editor, but when setup as either an onEdit event or onEdit Installible trigger, it dies with no error.
I did some trial and error toast messages and confirmed the code in dumpObject is being executed from the Trigger.
If you take this code below, setup onEdit2 as an installable trigger, you might see it.
To see it work as a Trigger, uncommment the first line //e of onEdit2.
Best I can figure, is something in the e object coming from the trigger that is not quite what is expected of an object?
This test should be limiting the maxDepth to 5, so I don't think I'm hitting the 1000 depth limit.
UPDATE: The problem is calling typeof on the trigger object properties. For example, "typeof e.user" reports the following error: Invalid JavaScript value of type
Thanks,
Jim
function onEdit2(e) {
//e = {fish:{a:"1",b:"2"},range:SpreadsheetApp.getActiveSpreadsheet().getActiveRange(),B:"2"};
Browser.msgBox(typeof e);
Browser.msgBox("U:" + Utilities.jsonStringify(e));
e.range.setComment("Edited at: " + new Date().toTimeString());
Browser.msgBox("ShowOBJ:"+dumpObject(e, 5));
}
function dumpObject(obj, maxDepth) {
var dump = function(obj, name, depth, tab){
if (depth > maxDepth) {
return name + ' - Max depth\n';
}
if (typeof obj === 'object') {
var child = null;
var output = tab + name + '\n';
tab += '\t';
for(var item in obj){
child = obj[item];
if (typeof child === 'object') {
output += dump(child, item, depth + 1, tab);
} else {
output += tab + item + ': ' + child + '\n';
}
}
}
return output;
};
return dump(obj, '', 0, '');
}

You're not getting quite what you expect from the event object. If you throw in:
for(var q in e) {
Logger.log(q + " = " + e[q])
}
and then check the View->Logs menu item in the script editor you get
source = Spreadsheet
user = <your user>
So, checking the docs, you can come up with this as an alternative to your e.range.setComment("Edited at: " + new Date().toTimeString());:
e.source.getActiveSheet().getActiveCell().setComment("Edited at: " + new Date().toTimeString());
note: you can debug an error like you were (secretly) getting by wrapping your statement in a try catch like so:
try {
e.range.setComment("Edited at: " + new Date().toTimeString());
} catch (ex) {
Logger.log(ex);
}
and then checking the logs as mentioned above (or dumping to Browser.msgBox(), if you prefer).

This might not be a great "answer" but it works.
I found that replacing typeof with Object.prototype.toString.call(obj) I got something usable.
Of note, the e object returns [object Object] but the properties (e.user) return [object JavaObject]
if (Object.prototype.toString.call(obj).indexOf("object") != -1) {
var child = null;
var output = tab + name + '\n';
tab += '\t';
for(var item in obj){
child = obj[item];
if (Object.prototype.toString.call(child).indexOf("object") != -1) {
output += dump(child, item, depth + 1, tab);

Related

Uncaught TypeError: Cannot read properties of null (reading 'filter')

I'm seeing these two errors:
Uncaught TypeError:
Cannot read properties of null (reading 'filter')
along with this - Uncaught ReferenceError: $ is not defined
I've tried running it a few times and it gave me the same answer each time. I can't seem to figure out where the issue is..
This is the code:
function scrollToBottom(){
bottom = document.body.scrollHeight;
current = window.innerHeight+ document.body.scrollTop;
if((bottom-current) >0){
window.scrollTo(0, bottom);
//wait a few
setTimeout ( 'scrollToBottom()', 1500 );
}
(function() {
var placeholderString,
placeholderWords,
contactName,
companyName,
customNote,
jobDescription,
jobUrl,
jobInfo,
str = '';
$('.job_listings')
.filter(function(_, elem) {
return ($(elem).attr('data-force-note') );
})
.each(function(_, elem) {
// Find the URL for each job listing.
$(elem)
.find('.top a[href]')
.each( function(idx, value) { str += $(value).attr('href') + "\n"; });
// Get the company and contact info
placeholderString = $(elem)
.find('.interested-note').attr('placeholder');
// Split placeholder string into words:
placeholderWords = placeholderString.split(' ');
// Grab name of recruiter/contact
contactName = placeholderWords[4];
// Grab company name
companyName = $(elem).find('.startup-link').text();
// Build personalized note
customNote = "Hi " + contactName + "! Would love to join " + companyName + " using my diverse set of skills. Let's chat!";
// .header-info .tagline (text)
jobDescription = $(elem).find('.tagline').text();
// .header-info .startup-link (href attr)
jobUrl = $(elem).find('.startup-link').attr('href');
// Compile and format job information
jobInfo = companyName + '\n' + jobDescription + '\n' + str + '\n\n';
// Get job data for your own records
console.log(jobInfo);
// Comment this out to verify your customNote
// console.log(customNote + '\n');
// Add your custom note.
// Comment these lines out to debug
$(elem)
.find('.interested-note').text( customNote );
//Comment these lines out to debug
$(elem)
.find('.interested-with-note-button')
.each( function(idx, button) { $(button).click(); });
});
// Print all of the company and job info to the console.
return jobInfo;
})();
};
scrollToBottom();
Is anyone able to help me here?
Thank you!

Creating a custom formula with array having multiple arguments in columns with different data

Up to this point I've managed to get it to work for a single argument (body) but without the second argument (photoUrl):
function SendTelegram(body,photoUrl) {
if (body.map) {
var response = body.map(function(b) {return SendTelegram(b);});
} else {
var response = UrlFetchApp.fetch(
"https://api.telegram.org/bot" + 'AAA' +
"/sendPhoto?caption=" + encodeURIComponent(body) +
"&photo=" + encodeURIComponent(photoUrl) +
"&chat_id=" + 'BBB' +
"&disable_web_page_preview=true&parse_mode=HTML"
);
}
}
I'm not able to understand how I should proceed to work with this two different arguments, for example, the function in the spreadsheet would be:
=ARRAYFORMULA(SendTelegram(A1:A,B1:B))
In my attempts when I try to add the second argument, it always uses the first row value in all calls, it doesn't follow the array one by one.
In your script, at SendTelegram(b) of var response = body.map(function(b) {return SendTelegram(b);});, the 2nd argument is not set. By this, at 2nd loop, photoUrl is not declared. I thought that this might be the reason of your issue.
And, in your script, I thought that it might be required to return the response value. So, how about the following modification?
Modified script:
function SendTelegram(body, photoUrl) {
if (body.map) {
return body.map((b, i) => SendTelegram(b, photoUrl[i]));
} else if (body && photoUrl) {
return UrlFetchApp.fetch(
"https://api.telegram.org/bot" + 'AAA' +
"/sendPhoto?caption=" + encodeURIComponent(body) +
"&photo=" + encodeURIComponent(photoUrl) +
"&chat_id=" + 'BBB' +
"&disable_web_page_preview=true&parse_mode=HTML", { muteHttpExceptions: true }
).getContentText();
}
return null;
}
Note:
This is a simple modification. Because I cannot understand your expected value from the URL. So, please modify the script for your situation.

Getting response with NodeJS request module

I just started using the twitch kraken api and I have a few questions.
Whenever I attempt to get a JSON object there is no response. I am attempting to run this function through Amazon AWS Lambda, and don't have access to a console.
In the code below my callback function will always print out "SUCCESS got streamers ERROR". I am pretty certain right now the "ERROR" comes from my initial setting of result.
How come result does not get changed into the proper JSON?
I have used postman and it returns the proper thing with the query and param, and headers:
function getJSON(callback){
var result = "ERROR";
request.get(url(games[0]),function(error,response,body){
console.log("requested for url: " + url(games[0]));
var d = JSON.parse(body);
result = d.streams[0];//.channel.display_name;
// for(var i = 0; i < limit; i++){
// streamers.push(d.streams[i].channel.display_name)
// }
streamers.push(result);
});
if (streamers.length < 0){
callback("ERROR");
}else{
callback("SUCCESS got streamers " + result);
}
}
function url(game){
return {
url: "https://api.twitch.tv/kraken/streams/",//twitchlimit,
qs : {
'game' : 'overwatch',
'limit' : 2
},
headers: {
'Client-ID': clientID,
'Accept': 'application/json',
'Accept-Charset': 'utf-8',
}
};
}
I think your streamers code
if (streamers.length < 0){
callback("ERROR");
}else{
callback("SUCCESS got streamers " + result);
}
should be included in the request callback because currently it's not waiting for the request to finish, it's just carrying on so therefore the value of result will not change. Also the array length cannot be less than 0 so it will always go to the else and say "SUCCESS got streamers ERROR"
Thank you guys for the suggestions. I did have a few oversights and attempted to fix them.
I have implemented you suggestions and it seems to have worked a bit. I ended up putting the json.parse into a try/catch block, and moved the if/else statements inside the getJSON method. However, now I don't get any output.
This is how I am invoking the getJSON method:
function handleGameResponse(intent,session,callback){
//gets the game
var game = intent.slots.game.value;
if (!games.includes(game)){
var speechOutput = "You asked for: " + intent.slots.game.value;
//var speechOutput = "You asked for: " + games[game] + " That game is currently not an option. These are your current options: " + arrayToString(games)
var repromptText = "Please ask one from the current options.";
var header = "Invalid Game";
}else {
getJSON(function(data){
if(data !== "ERROR"){
var speechOutput = data; //capitalizeFirst(game) + " top three streamers are: " + arrayToString(streamers) + '.';
var repromptText = "Do you want to hear more about games?";
var header = capitalizeFirst(game);
}else{
var speechOutput = "I'm sorry, something went wrong and I could not get the streamers.";
}
//speechOutput = data;
});
//speechOutput = games[0] + " games[0], game= " + game; //this executes so the getJSON isn't executing
}
var shouldEndSession = false;
callback(session.attributes,buildSpeechletResponse(header,speechOutput,repromptText,shouldEndSession));
}
Does the above execute the same way? As in the shouldEndSession and callback execute before the getJSON has time to give a response?
For ref, this is the getJSON method now:
function getJSON(callback){
var result = "ERROR";
request.get(url(games[0]),function(error,response,body){
try{
var d = JSON.parse(body);
} catch (err){
callback("Sorry, something seems to have malfunctioned while getting the streamers");
}
result = d.streams[0].channel.display_name;
// for(var i = 0; i < limit; i++){
// streamers.push(d.streams[i].channel.display_name)
// }
streamers.push(result);
if (streamers.length <= 0){
callback("ERROR");
}else{
callback("SUCCESS got streamers " + result);
}
});
}

Unexpected exception upon serializing continuation Google Apps Script

I recently started getting the error "Unexpected exception upon serializing continuation" on a spreadsheet Google Apps Script when trying to debug. The error seem to start after I created a connection to the Google CloudSQL api. This error still occurs even after commenting out the jdbc object constructor. It appears that others have had this issue and needed a Google Tech to resolve the issue.
I have searched all of the discussion boards for a solution to this issue with no luck. Any chance there is a Google tech out there who could take a look under the hood for me? I would post code if I could determine what line was actually triggering the error.
EDIT:
Ok, I think I have discovered where the error is occuring. Seems to be the
var response = UrlFetchApp.fetch(url + nextPage,oauth_options);
in the while loop. Here is the entire function code.
function retrieveEvents(endTimeMinimum, updatedAfter, orderBy){
//var url = 'https://www.googleapis.com/calendar/v3/calendars/' + source_cal + '/events?key=' + api_key + "&futureevents=true&orderBy=updated&sortOrder=descending&updatedMin=" + last_sync_date_formated;
//var url = 'https://www.googleapis.com/calendar/v3/calendars/' + source_cal + '/events?key=' + api_key + "&orderBy=updated&sortOrder=descending&updatedMin=" + last_sync_date_formated;
var url = 'https://www.googleapis.com/calendar/v3/calendars/' + source_cal + '/events?key=' + api_key + "&singleEvents=true";
if ((orderBy != null) && (orderBy != "")){
url += "&orderBy=" + orderBy;
}
else url += "&orderBy=updated";
if ((updatedAfter != null) && (updatedAfter != "")){
url += "&updatedMin=" + updatedAfter;
}
else url += "&updatedMin=" + last_sync_dateTime;
//if no endTimeMinimum is specified, the current time will be used.
if (endTimeMinimum == null || endTimeMinimum == ""){
endTimeMinimum = date_rfc339("Today");
}
url += "&timeMin=" + endTimeMinimum;
Logger.log("Request URL:" + url);
var largeString = "";
var events = new Array();
var nextPage = "";
var jsonObj
while(true){
var response = UrlFetchApp.fetch(url + nextPage,oauth_options);
largeString = response.getContentText();
if ((largeString != null) && (largeString != "")) {
jsonObj = JSON.parse(largeString);
}
if ('items' in jsonObj) events = events.concat(jsonObj.items);
if ('nextPageToken' in jsonObj){
nextPage = "&pageToken=" + jsonObj.nextPageToken;
continue;
}
break;
}
if (events.length == 0)return null;
return events;
}
OK, so I was able to make the problem go away by removing the try catch block inside a function that was called from inside a try catch block in the main function. I no longer am seeing the "Unexpected exception upon serializing continuation" when running the program from the debugger.
I wish I had a more solid answer on what causes this error and how to correct it.
In my experience, this is not an error caused by that line (or any other) specifically, but because an error is triggered within a loop. I haven't pinned down the exact replicable cause, but GAS seems to lose the loop pointer certain errors.
The best I can suggest is that any line that you suspect to causing an error within the while loop wrap with a try-catch that logs the error to the logger and proceeds. The loop pointer is then not lost and will debug as expected.

google script accessing a contact's custom label for an email address

(new to Google Script {about 2 weeks} so please be verbose)
I have tried everything I could find or think of to display the custom labels for a contact's list of email address. This has been very frustrating. When I search I get a lot of hits for gmail message labels, but nothing for custom labels for the email address within an individual contact.
Long term goal is to build an auto forwarder for my son’s Boy Scout Troop, taking baby steps to get there.
Some of the boys want to be notified by SMS, some by email. Since some have actual emails (used with attachments) and mobile phones (used for reminders), there is a need for custom contact email labels. I can make a list of all of the contact groups and I can make a list of all of the contact names within each group. I can even get all of the email address for each contact. But I can’t get the custom labels for a contact’s list of emails.
It is beyond me why the “getLabel” method does not do all of the “behind the curtain” work and return the label text regardless of the label type (custom or built in).
Any guidance would be appreciated.
function Get_Groups_Contacts(GroupList)
{
var groups = ContactsApp.getContactGroups(); //get the list of groups
for (var i in groups) //for each item in the group list ...
{
for (var n=0; n<=15; n++) //need to setup retries since the next part sometimes has server issues
{
try //trap errors
{
var cont = groups[i].getContacts() //get the list of contacts that belong to the group
var arrCont= [] //define the temp storage
for (var j in cont) //step through each contact
{
//I can store a list of contact names…
arrCont.push(cont[j].getFullName() ); //get the contact's full name
// but am trying to switch to specific email address….
var eml = cont[j].getEmails(); // the list of all email address for a contact
//now the messy part, trying to figure things out
//lists the built in labels but not the custom labels
for (k in eml) Logger.log(typeof eml[k].getLabel() + "??" + eml[k].getLabel() + "--" + eml[k].getAddress());
for (k in eml)
{
try
{
var x = eml[k].getLabel();
Logger.log(k + " !!" + typeof x + "??" + x + "--" + eml[k].getAddress() + "**" + "hello");
// var oneeml = eml[k];
var oneeml = cont[j].getEmails("Other");
Logger.log("xxxxxxxxxxxxxxxxxxxxxxxx");
Logger.log("oneeml " + oneeml);
//Logger.log(oneeml.getLabel())
Logger.log("zzzzzzzzzzzzzzzzzzzzzzzz");
for (zz in oneeml) Logger.log(oneeml[zz]);
for (zz in oneeml)
if (zz == "setAsPrimary") Logger.log(zz)
else if (zz == "setDisplayName") Logger.log(zz)
else if (zz == "setAddress") Logger.log(zz)
else if (zz == "setLabel") Logger.log(zz)
else if (zz == "deleteEmailField") Logger.log(zz)
else Logger.log(oneeml[zz]())
;
}
catch(ext)
{
Logger.log("inner catch");
Logger.log(ext.message);
}
}
}
//end of the messy part
GroupList[groups[i].getGroupName()] = arrCont //store the list in the property
break; //go on to the next group
} //end of try
catch(err) //catch the error here
{
// Logger.log (n + " error message" + err.message); //debug
Logger.log ("n=" + n); //debug
sleep((Math.pow(2,n) + (Math.random(0, 1000) / 100)) ); //increasing random sleep time waiting for the server
} //end of catch
finally //always do this part
{
cont = undefined; //cleanup
arrCont = undefined; //cleanup
} //end of error traping
} //end of retry loop
} //end for each group item
}; //end of function List_Groups_Contacts()