Google workspace add-ons, cats example error: TypeError: Cannot read property 'userTimezone' of undefined - google-workspace

I am investigating in how to create Google Workspace add-ons and I am following the cats quickstart
But running it I encounter this error "TypeError: Cannot read property 'userTimezone' of undefined" on Common.gs row 24
var hour = Number(Utilities.formatDate(new Date(), e.userTimezone.id, 'H'));
The function that contains this row is
function onHomepage(e) {
console.log(e);
var hour = Number(Utilities.formatDate(new Date(), e.userTimezone.id, 'H'));
var message;
if (hour >= 6 && hour < 12) {
message = 'Good morning';
} else if (hour >= 12 && hour < 18) {
message = 'Good afternoon';
} else {
message = 'Good night';
}
message += ' ' + e.hostApp;
return createCatCard(message, true);
}
Can anyone help me to understand where is the problem ?
Thanks in advance, Vincenzo

I came across the same problem too. Tried to deploy after encountering this message, I was able to install the app :)

It's straight forward but like many things, not obvious!
You need to run the script once in order to set up the requisite permissions. It doesn't actually run the script but presents you with the option to agree to authorize the script. Once you have authorized the script, you cannot run it from the console because there will be no event(e) available at this time, hence the e.userTimezone.id error you encountered.
Now you will be able to install the add-on. Refresh Gmail and you will be good to go. Look for the cat paw icon over to the right of your screen.

Related

Testing Cloud Function - Cannot read property 'data' of undefined

I've got a Raspberry Pi and setup a weather (and soil moisture) rig.
I found this guide: https://codelabs.developers.google.com/codelabs/iot-data-pipeline which I followed and got stuck around Step 6-7.
From what I understand - when I send data to PubSub - nothing happens. On the Raspberry End I sort of get the idea that data is being sent but it doesn't get passed into BigQuery. I did some print statements at various points to try and see where it got stuck.
As I was trying to find the error I slowly backtracked to Step 5 (Create a Cloud Function).
Step 5 along with associated code I copied can be seen here: https://codelabs.developers.google.com/codelabs/iot-data-pipeline/#4
In GCP - I click into Cloud Function -> function-weatherPubSubToBQ -> Testing (tab)
Under the heading - Trigger event - I filled out the JSON below:
{
"sensorID":"Raspberry",
"timecollected":"2020-09-11 06:45:19",
"zipcode":"00000",
"latitude":"0.0",
"longitude":"0.0",
"temperature":"-273",
"humidity":"-1",
"dewpoint":"-273",
"pressure":"0"
}
When I click on - Test the function - the output is as below
**Error: function execution failed. Details:
Cannot read property 'data' of undefined**
Screen capture of JSON and error message
I am guessing one of these two things are causing the problem.
event.data or PubSubMessage.data
I tried to make some changes to the code but I am just shooting in the dark.
I was wondering if:
I did something wrong which means there might be some other
issues somewhere else.
This guide is slightly old and there have
been some updates which make the older code in the guide not
function as desired. (not step/image in the guide matches with what
I saw online, as of Sep 2020)
If someone knows what is wrong in
the code and is able to let me know how to solve it that would be
much appreciated.
Thanks in advance.
TLDR: The tutorial is outdated, don't use it, unless you want to face multiple problems and want to learn in the hard way
I went through the tutorial and I was able to replicate the issue.. and many more. As you already mentioned it, the tutorial is outdated and many things have changed, you can infer that by looking at the images and noticing that the UI is even different, so I wouldn't recommend this tutorial to anyone who is new to GCP.
The first issue:
**Error: function execution failed. Details: Cannot read property 'data' of undefined**
Can be easily resolved by looking at the structure of what it's expected from a pub/sub message:
{
"data": string,
"attributes": {
string: string,
...
},
"messageId": string,
"publishTime": string,
"orderingKey": string
}
So easy right? However once you mimic the structure of the message with your own variables as I did:
{
"data": "",
"attributes": {
"sensorID":"Raspberry",
"timecollected":"2020-09-11 06:45:19",
"zipcode":"00000",
"latitude":"0.0",
"longitude":"0.0",
"temperature":"-273",
"humidity":"-1",
"dewpoint":"-273",
"pressure":"0"
},
"messageId": "id_1",
"publishTime": "2014-10-02T15:01:23Z",
"orderingKey": ""
}
You will get an error regarding the JSON:
SyntaxError: Unexpected token ' in JSON at position 1
This error is due to the use of ' on the construction of the JSON inside the variable incomingData so you have to change the first variable declaration, I did it by using template literals:
const incomingData = PubSubMessage.data ? Buffer.from(PubSubMessage.data, 'base64').toString() : `{"sensorID": "na","timecollected":"01/01/1970 00:00:00","zipcode":"00000","latitude":"0.0","longitude":"0.0","temperature":"-273","humidity":"-1","dewpoint":"-273","pressure":"0"}`;
But this is not the end of the issues, after doing some tests while trying to insert into BigQuery, I got an error regarding the insertion, but didn't get a clue of what was really happening, so I isolated the consult in an external script and found that the error handling was wrong, the first thing I recommend you to change is the BigQuery version in the package.json from:
"#google-cloud/bigquery": "^0.9.6"
Into
"#google-cloud/bigquery": "5.2.0"
Which is the last version at the time of writing this answer. The next part is to redefine the way you're using BigQuery constructor into:
const bigquery = new BigQuery({
projectId: projectId
});
Then after many tests I found that the catch wasn't doing it's job as expected, so have to rewrite that part into:
bigquery
.dataset(datasetId)
.table(tableId)
.insert(rows)
.then((foundErrors) => {
rows.forEach((row) => console.log('Inserted: ', row));
if (foundErrors && foundErrors.insertErrors != undefined) {
foundErrors.forEach((err) => {
console.log('Error: ', err);
})
}
})
.catch((err) => {
bigquery
.dataset(datasetId)
.table(tableId)
.insert(rows)
.then((foundErrors) => {
rows.forEach((row) => console.log('Inserted: ', row));
if (foundErrors && foundErrors.insertErrors != undefined) {
foundErrors.forEach((err) => {
console.log('Error: ', err);
})
}
})
.catch((err) => {
if(err.name=='PartialFailureError'){
if (err && err.response.insertErrors != undefined) {
err.response.insertErrors.forEach((errors) => {
console.log(errors);
})
}
}else{
console.log("GENERIC ERROR:",err)
}
});
});
After this you will finally notice that the error is due to (again) the incomingData variable:
Could not parse \'01/01/1970 00:00:00\' as a timestamp. Required format is YYYY-MM-DD HH:MM[:SS[.SSSSSS]]'
You have to change the date from 01/01/1970 00:00:00 into 1970-01-01 00:00:00.
Still here with me? Well there's another error coming from the usage of callback at the end of the CF:
This is due to the fact that Cloud Functions now require three parameters and callback is the last one, so change the function declaration into:
exports.subscribe = function (event, context, callback)
After all of this you have been able to insert data into BigQuery, however we're using the local variable and not the data coming from pub/sub, at this point I gave up, since I will need to practically rewrite the full function in order to make it work by using Attributes instead of the data.
So as mentioned it before don't follow this tutorial if you're beginning in the GCP world.

My Discord bot recently won't respond when mentioned

This is my first time using this site so I apologize if the formatting's sub par.
The problem:
My Discord bot (javascript) recently has stopped responding when #mentioned. There were no changes to the code to cause this and it was working perfectly fine not too long ago. A friend who's bot is programmed similarly also has this issue so I know that it's not only me.
The bot's basically a chat-and-reply bot; you #mention it's name and include a trigger and it has a random chance to respond with one of four responses. However, something's happened where it doesn't seem to register that it's been #mentioned and therefore doesn't reply.
So, for example, if I were to type "#bot hi!" in discord, the bot would reply with one of the following replies: "It's too early for this.", "Mornin'.", "I need coffee.". "[yawn, mumbled greeting]".
I've tried replacing client.user.toString() directly with it's client identifier as well as the identifiers that would be used in discord (for example; "#name#0000", "<#########>") but those are also ignored. I've added an arrow next to this area in the code.
I'm not sure if there was an update that's made some of the code go out of date, but I've tried searching for similar issues with no success.
I'm relatively sure that the issue isn't with the processChat(receivedMessage) function, as I can replace the noted problem section with an alternate trigger such as:
if (receivedMessage.content.startsWith("#all ")) {
processChat(receivedMessage)
}
and the bot will send a reply. It simply doesn't seem to want to respond when mentioned; the rest of the code works as it should. While this is something I can convert to, I've had this bot on my server for almost a year now and there are multiple other server members who'd need to adapt to the change. I'd rather get things running the way they used to than have everyone fall out of habit to compensate.
Is there a way to fix this?
Here's a small example code that has the same issue:
const Discord = require('discord.js');
const client = new Discord.Client();
client.on('ready', () => {
console.log("Connected as " + client.user.tag);
})
//The color for all of the bot's messages
messageColor = 4611141;
client.on('message', receivedMessage => {
// Prevent bot from responding to its own messages
if (receivedMessage.author == client.user) {
return;
}
//process regular triggers (# mentions)
//This is where the issue seems to be <--------------------------
if ((receivedMessage.content.includes(client.user.toString()))) {
processChat(receivedMessage);
}
});
//For usage with chat prompts and triggers
function processChat(receivedMessage) {
if ((receivedMessage.content.toLowerCase().includes("hi"))){
var random = randomInt(5, 1) ;
if (random == 1) {
receivedMessage.channel.send({embed: {
color: messageColor,
description: "It's too early for this."
}});
} else if (random == 2) {
receivedMessage.channel.send({embed: {
color: messageColor,
description: "Mornin'."
}});
} else if (random == 3) {
receivedMessage.channel.send({embed: {
color: messageColor,
description: "I need coffee."
}});
} else if (random == 4) {
receivedMessage.channel.send({embed: {
color: messageColor,
description: "*[yawn, mumbled greeting]*"
}});
} else {
return;
}
}
}
//Random number generation
function randomInt(max, min) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
client.on('error', console.error);
client.login(token)
Switching from
if ((receivedMessage.content.includes(client.user.toString()))) {
processChat(receivedMessage);
}
to
if (recievedMessage.isMemberMentioned(client.user)) {
processChat(recievedMessage)
}
Solved the problem. Thanks, Bauke, for those links.

Iterative Post request in Postman

I need help for doing Post request repeatedly in Postman with different bodies. Example given below, where company name must be changed. It would be better to read company names from a document or may be from an array in script. Please advice how can I do it?
{
"d": "{{company}}"
}
I found somethin like that, but I am getting error: (There was an error in evaluating the Pre-request Script: TypeError: Cannot read property 'get' of undefined)
Pre-request Script:
if(!companies){
companies = ["111",
"222",
"333"];
}
var currentCompany = companies.shift();
pm.enviroment.set("company",currentCompany);
pm.enviroment.set("companies", companies);
Tests:
var companies = pm.enviroment.get("companies");
if(companies && companies.length > 0){
postman.setNextRequest("my url");
} else {
postman.setNextRequest(null);
}
There are multiple but small mistakes:
the set environment function is misspelled: please use pm.environment.set(right) instead of pm.enviroment.set(false)
you have to load the companies var from the environments before. Add var companies = JSON.parse(pm.environment.get("companies")); to the first line of your pre rquest script.
Please make sure that you are saving string values to the environment vars. Use JSON.stringify(myObject) e.g. pm.environment.set("company", JSON.stringify(currentCompany)); and analogue JSON.parse(myStringVar) for loading variables in var companies = JSON.parse(pm.enviroment.get("companies"));
(Maye also a problem) Please make sure, there is a enviroment chosen in Postman, postman-runner and newman. If there is no environment set, you can get errors:

How to fix this authorization Google Apps Script suggest box library issue?

I'm trying to add an autocomplete feature (a Ui in a popup window in a spreadsheet) in my Google Spreadsheet using this Google Apps Script suggest box library from Romain Vialard and James Ferreira's book (modified):
function onEdit() {
var s = SpreadsheetApp.getActiveSheet();
if( s.getName() == "my_sheet" ) { //checks that we're on the correct sheet
var r = s.getActiveCell();
if( r.getColumn() == 1) {
var names = ["Adam", "Peter", "Benjamin", "Ceaser", "Prometheus", "Gandi", "Gotama", "Mickey Mouse"];
var app = UiApp.createApplication();
var suggestBox = SuggestBoxCreator.createSuggestBox(app, 'contactPicker', 200, names);
app.add(suggestBox);
SpreadsheetApp.getActive().show(app);
var dataCell0 = r.offset(0, 1);
var dataCell0 = r.offset(0, 1);
if( dataCell0.getValue() == '' )
otherTestTunction();
}
}
}
But when I start editing column 1 of "my_sheet" and the Ui box appears, this autorization error happens (In my language it says: "you must have permission to perform this action"):
The documentation says that onEdit() trigger "They cannot access any services that require authentication as that user. For example, the Google Translate service is anonymous and can be accessed by the simple triggers. Google Calendar, Gmail, and Sites are not anonymous and the simple triggers cannot access those services."
Since I'm not using ContactsApp, I suppose that the suggest box library require authorization.
How could I set up an installable on Edit trigger that will ask for authorization? (Could you give me some code sample?)
Here is my test spreadsheet: https://docs.google.com/spreadsheet/ccc?key=0AtHEC6UUJ_rsdFBWMkhfWUQ0MEs2ck5OY1BsYjRSLXc&usp=drive_web#gid=0
Out of curiosity about what you suggested on authorizations needed by the library I made a test on a new sheet, the exact code below asks for spreadsheet access, nothing more.
function onEdit() {
var s = SpreadsheetApp.getActiveSheet();
var names = ["Adam", "Peter", "Benjamin", "Ceaser", "Prometheus", "Gandi", "Gotama", "Mickey Mouse"];
var app = UiApp.createApplication();
var suggestBox = SuggestBoxCreator.createSuggestBox(app, 'contactPicker', 200, names);
app.add(suggestBox);
SpreadsheetApp.getActive().show(app);
var dataCell0 = r.offset(0, 1);
}
To handle that you just have to ask your user to run one function in your script (no matter what function) and they will get the following popup :
After this initial step your function will work as expected, ie the Ui will show up.
Beside that, I'm not sure I understand what you want to achieve is feasible, the onEdit trigger fires when the edit is done, meaning after you hit ENTER, then the value in the suggestBox is not taken into account... moreover you have to handler in the UI to do anything so I'm still wondering what do you really expect to do with this code ? (what would be ideal is an onClick event in spreadsheetApp but unfortunately it doesn't exist so far...)
But maybe I missed something obvious.
About installable onEdit that you mention also in your post, you should note that it doesn't need to be authorized by the end user since it would run as YOU, the person that creates the trigger and not the user accessing the SS , therefor accessing your own data and not the user's ones... That might be a non negligible restriction (as Zig mentioned in a comment earlier...)

setting up script to include google docs form data in email notification

I've setup a form using googledocs. I just want to have the actual data entered into the form emailed to me, as opposed to the generic response advising that the form has been completed.
I have no skill or experience with code etc, but was sure i could get this sorted. I've spent hours+hours and haven't had any luck.
My form is really basic.it has 5 fields. 4 of which are just text responses, and one multiple choice.
I found this tute online (http://www.labnol.org/internet/google-docs-email-form/20884/) which i think sums up what i'm trying to do, but have not been able to get it to work.
from this site i entered the following code:
function sendFormByEmail(e)
{
var email = "reports.mckeir#gmail.com";
var subject = "Google Docs Form Submitted";
var s = SpreadsheetApp.getActiveSheet();
var headers = s.getRange(1,1,1,s.getLastColumn()).getValues()[0];
var message = "";
for(var i in headers)
message += headers[i] + ' = '+ e.namedValues[headers[i]].toString() + "\n\n";
MailApp.sendEmail(email, subject, message);
}
To this, i get the following response: ->
Your script, Contact Us Form Mailer, has recently failed to finish successfully. A summary of the failure(s) is shown below. To configure the triggers for this script, or change your setting for receiving future failure notifications, click here.
The script is used by the document 100% Club.
Details:
Start Function Error Message Trigger End
12/3/12 11:06 PM sendFormByEmail TypeError: Cannot call method "toString" of undefined. (line 12) formSubmit 12/3/12 11:06 PM
Is anyone able to help shed some light on this for me? I'm guessing i'm not including some data neeeded, but i honestly have no clue.
Workaround http://www.labnol.org/internet/google-docs-email-form/20884/
You have to setup app script to forward the data as email.
I'll point to the comment above that solved it for me: https://stackoverflow.com/a/14576983/134335
I took that post a step further:
I removed the normal notification. The app script makes that generic text redundant and useless now
I modified the script to actually parse the results and build the response accordingly.
function sendFormByEmail(e)
{
var toEmail = "changeme";
var name = "";
var email = "";
// Optional but change the following variable
// to have a custom subject for Google Docs emails
var subject = "Google Docs Form Submitted";
var message = "";
// The variable e holds all the form values in an array.
// Loop through the array and append values to the body.
var s = SpreadsheetApp.getActiveSheet();
var headers = s.getRange(1,1,1,s.getLastColumn()).getValues()[0];
// Credit to Henrique Abreu for fixing the sort order
for(var i in headers) {
if (headers[i] = "Name") {
name = e.namedValues[headers[i]].toString();
}
if (headers[i] = "Email") {
email = e.namedValues[headers[i]].toString();
}
if (headers[i] = "Subject") {
subject = e.namedValues[headers[i]].toString();
}
if (headers[i] = "Message") {
message = e.namedValues[headers[i]].toString();
}
}
// See https://developers.google.com/apps-script/reference/mail/mail-app#sendEmail(String,String,String,Object)
var mailOptions = {
name: name,
replyTo: email,
};
// This is the MailApp service of Google Apps Script
// that sends the email. You can also use GmailApp here.
MailApp.sendEmail(toEmail, subject, message, mailOptions);
// Watch the following video for details
// http://youtu.be/z6klwUxRwQI
// By Amit Agarwal - www.labnol.org
}
The script utilized in the example is extremely generic but very resilient to change because the message is built as a key/value pair of the form fields submitted.
If you use my script you'll have to tweak the for loop if statements to match your fields verbatim. You'll also want to edit the toEmail variable.
Thanks again for the question and answers. I was about to ditch Google Forms as the generic response was never enough for what I was trying to do.
Lastly, in response to the actual problem above "toString of undefined" specifically means one of the form fields was submitted as blank. If I had to guess, I would say the author only used this for forms where all the fields were required or a quick undefined check would've been put in place.
Something like the following would work:
for(var i in headers) {
var formValue = e.namedValues[headers[i]];
var formValueText = "";
if (typeof(formValue) != "undefined") {
formValueText = formValue.toString();
}
message += headers[i] + ' = '+ formvalueText + "\n\n";
}
I haven't tested this precisely but it's a pretty standard way of making sure the object is defined before trying methods like toString() that clearly won't work.
This would also explain Jon Fila's answer. The script blindly assumes all of the header rows in the response are sent by the form. If any of the fields aren't required or the spreadsheet has fields that are no longer in the form, you'll get a lot of undefined objects.
The script could've been coded better but I won't fault the author as it was clearly meant to be a proof of concept only. The fact that they mention the replyTo correction but don't give any examples on implementing it made it perfectly clear.
If this is a Google Form, do you have any extra columns in your spreadsheet that are not on the form? If you delete those extra columns then it started working for me.
You don't need to use a script. Simply go to Tools >> Notification Rules on your Google Spreadsheet. There you can change the settings to receive an email with your desired information every time the document is changed.