How to overcome "Cannot find method addLabel(string)" in Gmail App Script? - google-apps-script

The below App Script fetches the first message of the first Gmail Inbox thread and checks the "From:" header against a regular expression.
Based on the outcome, we want to use addLabel() to set a Gmail label.
It performs the fetch and test fine, but fails when attempting to set the label - Cannot find method addLabel(string). (line 15, file "Code")
function myFunction() {
// Get first thread in Inbox.
var thread = GmailApp.getInboxThreads(0,1)[0];
// Get the first message in the thread.
var message = thread.getMessages()[0];
// Get a message header
var headerstring = message.getHeader("From");
// Check header for "rob" test string, apply label
if ( headerstring.match(/rob/g) ) {
thread.addLabel("MyLabel");
Logger.log("Matched rob");
} else {
thread.addLabel("AnotherLabel"); // failing, not class scope?
Logger.log("No match");
}
}
It feels like the existence of addLabel within the if clause has robbed it of the application of GmailApp, since I have had addLabel functioning outside of one - am I right? This is my first script.
How can I overcome this?

Explanation:
The issue is that addLabel(label) does not accept a string but an object of type GmailLabel.
If the labels are already created by you, you need to use getUserLabelByName(name) where you can pass the label name as string, get a GmailLabel object back and finally pass it to addLabel(label).
Solution:
function myFunction() {
// Get first thread in Inbox.
var thread = GmailApp.getInboxThreads(0,1)[0];
// Get the first message in the thread.
var message = thread.getMessages()[0];
// Get a message header
var headerstring = message.getHeader("From");
// Check header for "rob" test string, apply label
if ( headerstring.match(/rob/g) ) {
var label = GmailApp.getUserLabelByName("MyLabel");
thread.addLabel(label);
Logger.log("Matched rob");
} else {
var label = GmailApp.getUserLabelByName("AnotherLabel");
thread.addLabel(label);
Logger.log("No match");
}
}

Related

Adding a tag to an email file creates an error

I created a script that receives last minute emails if they do not contain a "a" tag and sends the content of the message to a function I called "ssfunction" and then adds the "a" tag to it
And this is the script I made:
const threads = GmailApp.search('-{label:a}');
for (const thread of threads) {
const messages = thread.getMessages();
const minuteAgo = new Date(Date.now() - 60000);
if (thread.getLastMessageDate() > minuteAgo) {
for (const message of messages) {
if (message.getDate() > minuteAgo) {
const result = ssfunction(message);
didUpload = result || didUpload;
}
}
thread.addLabel("a");
} else {
const result = ssfunction(messages[messages.length - 1]);
didUpload = result || didUpload;
thread.addeLabel("a");
}
But I get such an error:
TypeError: thread.addeLabel is not a function
Thank you so much for all the willingness to help
In your script, I thought that the method name of addeLabel is required to be modified to addLabel. I think that this is the reason of your error message. And, in the case of addLabel, the argument is GmailLabel object. When these points are reflected in your script, it becomes as follows.
From:
thread.addeLabel('a');
To:
var label = GmailApp.getUserLabelByName("a");
thread.addLabel(label);
Refernce:
addLabel(label)

If statement that does nothing if the first condition is not met

I'm busy writing a script that includes an If statement and I'm trying to make the else part of the statement do nothing if the first condition isn't met. The script checks a google drive folder for a file name and if it is present the first condition should run, else if it isn't present it should do nothing.
I've tried using else{ return false;} , else{ } and none are working. I've also tried swapping the conditions around but that didn't work either
I keep getting an error saying "Exception: Cannot retrieve the next object: iterator has reached the end. (line 7, file "calls")" because the file is deleted once the data is retrieved from it.
The first condition works perfectly but if the csv file isn't present in the folder the above message is displayed. The idea is that I set a trigger that runs every minute to check the folder.
Below is what I have so far. Any help would be greatly appreciated.
function calls(){
var hotFolder = DriveApp.getFolderById('idHere');
var targetSheet = SpreadsheetApp.openById('idHere').getSheetByName('sheetNameHere');
var callsSheet = SpreadsheetApp.openById('idHere').getSheetByName('sheetNameHere');
var callsCsv = hotFolder.getFilesByName('fileName.csv.Here').next();
if(callsCsv) {
var csvData = Utilities.parseCsv(callsCsv.getBlob().getDataAsString());
targetSheet.getRange(1,1,csvData.length,csvData[0].length).setValues(csvData);
callsCsv.setTrashed(true);
var targetSheetData = targetSheet.getRange(2,1,targetSheet.getLastRow()-1,targetSheet.getLastColumn()).getValues();
var handleTime = targetSheetData.map(function(row){
if([row[9]] != 'ABANDON') {
return [row[6] + row[7] +15];
} else {
return [['']];
}
});
targetSheet.getRange(2,12,handleTime.length).setValues(handleTime);
var newData = targetSheet.getRange(2,1,targetSheet.getLastRow(),targetSheet.getLastColumn()).getValues();
callsSheet.getRange(callsSheet.getLastRow,1,newData.length,newData[0].length).setValues(newData);
targetSheet.clear();
} else {
}
}
The "Exception: Cannot retrieve the next object: iterator has reached the end. (line 7, file "calls")" error message you are receiving is due to the fact that the it's not possible to get the next item in the collection of files or folders - probably because of the deletion from the previous run.
What you can do in this situation is to use the hasNext() method. According to its documentation :
hasNext() > Determines whether calling next() will return an item.
Return
Boolean — true if next() will return an item; false if not
Therefore, you can change your code to this:
var callsCsv = hotFolder.getFilesByName('fileName.csv.Here');
if (callsCsv.hasNext()) {
var callsCsvFile = callsCsv.next();
// your code here - callsCsv becomes callsCsvFile
}
Reference
Apps Script Class FileIterator.

Triggering GAS function via URL

Very new to this, but giving it a shot. I am trying to set up an Arduino motion sensor to trigger a script. At this point, my goal is to trigger a script via URL. I found this code below that I am working through, but I continue to get this error when running/debugging.
TypeError: Cannot read property "parameter" from undefined. (line 4, file "Code")
I have been looking into e.parameter object, but have not been able to make any headway
function doGet(e) {
Logger.log(e)
var passedString,whatToReturn;
passedString = e.parameter.searchStringName;
if (passedString === 'tylog') {
whatToReturn = tylog(); //Run function One
};
return ContentService.createTextOutput(whatToReturn);
};
var mns = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Monster")
var tyl = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("tyLog")
var tyd = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("tyData")
var twl = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("twLog")
var twd = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("twData")
var tym = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("tyMaster")
var twm = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("twMaster")
var test = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("test")
var tydate = tyd.getRange('A2');
var tydur = tyd.getRange(2, 2);
// Start functions
function start() {
tyl.getRange('A1').setValue(new Date());
twl.getRange('A1').setValue(new Date());
}
//Log Typhoon ride
function tylog() {
tyl.getRange(tyl.getLastRow() + 1, 1).setValue(new Date());
}
//Log Twister ride
function twlog() {
twl.getRange(twl.getLastRow() + 1, 1).setValue(new Date());
}
//Send Data to both logs and clear
function tyclear() {
tyd.getRange('A2:H2').copyTo(tym.getRange(tym.getLastRow() + 1, 1), {contentsOnly: true});
twd.getRange('A2:H2').copyTo(twm.getRange(twm.getLastRow() + 1, 1), {contentsOnly: true});
tyl.getRange('A1:A100').clearContent();
twl.getRange('A1:A100').clearContent();
}
URL Request:
https://script.google.com/macros/s/AKfycbxC5zYevR1IhfFcUMjmIqUaQ1dKNHTm4mhmWBq_Rc9HgemJQ6Q/exec?searchStringName=tylog
I put this into a new project by itself and it still returned undefined​.
function doGet(e) {
var passedString,whatToReturn;
passedString = e.parameter.searchStringName;
if (passedString === 'functionOne') {
whatToReturn = functionOne(); //Run function One
};
return ContentService.createTextOutput(whatToReturn);
};
function functionOne() {
var something;
return ContentService.createTextOutput("Hello, world!"); }
I believe that your URL should be https://script.google.com/macros/s/AKfycbxC5zYevR1IhfFcUMjmIqUaQ1dKNHTm4mhmWBq_Rc9HgemJQ6Q/exec?searchStringName=functionOne
After pondering this question for a while it makes no sense to require a return from functionOne. I was getting the client server communication mixed up with the Get request process. For most Get requests the request suggests some type of response since in general we're looking for some type of content to be displayed. In this situation that may not be required since the requestor is a machine.
The use of e.parameter.paramname; just enables us to send key/value pairs from within our querystring that we can recover to redirect our server actions.
2020 UPD:
Upon revisiting the question, I noticed that the OP runs the doGet trigger in the context of script editor, hence the e becoming undefined (as it is only constructed when a request hits the URL with an HTTP request with GET method).
Thus, the answer to the debugging part is:
When running a trigger manually from the script editor, event object will be unavailable
The answer to the running part is as a result of an extended discussion:
When assigning a result of the function, one has to put the return statement inside the function, and the tylog function did not return anything.
Also note that any change to a Web App code, unless accessing it via /dev endpoint (i.e. via /exec endpoint), won't be available until after redeployment.
References
Web Apps guide

Checking If GmailApp.sendEmail() Is Running

I'm trying to run a script to send automated emails to a list of people (I needed to edit out a bit of information).
function sendEmail(){
for (var key in emailBank){
var email = getEmail(key);
var bodyText = emailBank[key]['body'];
var commitmentType = emailBank[key]['type'];
GmailApp.sendEmail(key,"Subject Line", bodyText {htmlBody:bodyText});
}
}
I'm using an array (emailBank) to store information from a spreadsheet before sending it out. The function should loop through and send each person an email.
On occasion, I'm getting the following error: "We're sorry, a server error occurred. Please wait a bit and try again." for the line including GmailApp.
Can you think of any to verify if an email is sent or not? My concern is that half of the emails will be sent and half will not, so I wouldn't know who actually received the email and at which point the loop stopped.
Any ideas are appreciated here!
Ok, so I think I found a solution to this after trying out a few methods:
// Variables for error checking later
var emailRecipients = "";
var trueRecipients = "";
//The sendEmail function to send the email as expected
//It will also increment the emailRecipients variable
//only after the email has been sent
function sendEmail(){
for (var key in emailBank){
var email = getEmail(key);
var bodyText = emailBank[key]['body'];
var commitmentType = emailBank[key]['type'];
GmailApp.sendEmail(key,"Subject Line", bodyText {htmlBody:bodyText});
emailRecipients += key + "<br>";
}
}
//This function will only run if there was an error
//It will check increment all of the email addresses
//that should have been sent a message and then send me an
function errorEmail(){
for (var key in emailBank){
trueRecipients += key + "<br>";
}
var errorBodyText = "Emails sent to:<br><br>" + emailRecipients + "<br><br>Number of emails that should have sent:<br><br>" + trueRecipients;
GmailApp.sendEmail("example#gmail.com","Email Errors", errorBodyText,{htmlBody:errorBodyText});
}
function reminderEmail(){
try{
sendEmail();
}
catch(e){
errorEmail();
}
}
As a temporary solution, you could check the last sent email after calling sendEmail() method.
/* the search() method returns an array of GmailThread objects.
Pop() returns the GmailThread */
var threads = GmailApp.search("label: sent", 0, 1).pop(); // returns the most recent thread in 'Sent Mail'
var msg = threads.getMessages().pop(); //returns the most recent message
Logger.log(msg.getId()); //returns unique id for the message
Logger.log(msg.getTo()); //returns comma-separated list of recipients
One option would be to store the message id and check if it changes on each run of the loop. You may also directly compare email properties, e.g. the recipient's email, against the properties of the message you sent.

getMessageById() slows down

I am working on a script that works with e-mails and it needs to fetch the timestamp, sender, receiver and subject for an e-mail. The Google script project has several functions in separate script files so I won't be listing everything here, but essentially the main function performs a query and passes it on to a function that fetches data:
queriedMessages = Gmail.Users.Messages.list(authUsr.mail, {'q':query, 'pageToken':pageToken});
dataOutput_double(sSheet, queriedMessages.messages, queriedMessages.messages.length);
So this will send an object to the function dataOutput_double and the size of the array (if I try to get the size of the array inside the function that outputs data I get an error so that is why this is passed here). The function that outputs the data looks like this:
function dataOutput_double(sSheet, messageInfo, aLenght) {
var sheet = sSheet.getSheets()[0],
message,
dataArray = new Array(),
row = 2;
var i, dateCheck = new Date;
dateCheck.setDate(dateCheck.getDate()-1);
for (i=aLenght-1; i>=0; i--) {
message = GmailApp.getMessageById(messageInfo[i].id);
if (message.getDate().getDate() == dateCheck.getDate()) {
sheet.insertRowBefore(2);
sheet.getRange(row, 1).setValue(message.getDate());
sheet.getRange(row, 2).setValue(message.getFrom());
sheet.getRange(row, 3).setValue(message.getTo());
sheet.getRange(row, 4).setValue(message.getSubject());
}
}
return;
};
Some of this code will get removed as there are leftovers from other types of handling this.
The problem as I noticed is that some messages take a long time to get with the getMessageById() method (~ 4 seconds to be exact) and when the script is intended to work with ~1500 mails every day this makes it drag on for quite a while forcing google to stop the script as it takes too long.
Any ideas of how to go around this issue or is this just something that I have to live with?
Here is something I whipped up:
function processEmails() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var messages = Gmail.Users.Messages.list('me', {maxResults:200, q:"newer_than:1d AND label:INBOX NOT label:PROCESSED"}).messages,
headers,
headersFields = ["Date","From","To","Subject"],
outputValue=[],thisRowValue = [],
message
if(messages.length > 0){
for(var i in messages){
message = Gmail.Users.Messages.get('me', messages[i].id);
Gmail.Users.Messages.modify( {addLabelIds:["Label_4"]},'me',messages[i].id);
headers = message.payload.headers
for(var ii in headers){
if(headersFields.indexOf(headers[ii].name) != -1){
thisRowValue.push(headers[ii].value);
}
}
outputValue.push(thisRowValue)
thisRowValue = [];
}
var range = ss.getRange(ss.getLastRow()+1, ss.getLastColumn()+1, outputValue.length, outputValue[0].length);
range.setValues(outputValue);
}
}
NOTE: This is intended to run as a trigger. This will batch the trigger call in 200 messages. You will need to add the label PROCESSED to gmail. Also on the line:
Gmail.Users.Messages.modify( {addLabelIds:["Label_4"]},'me',messages[i].id);
it shows Label_4. In my gmail account "PROCESSED" is my 4th custom label.