protection.removeEditors is throwing error - google-apps-script

Simple code is failing with error (after displaying 3 in logger) in the first iteration itself.
Exception: Service Spreadsheets timed out while accessing document with id .....
This was reported in issuetracker 3 years back and it is marked "Fixed"
The error is still there and the function failing 25 times a day (hourly timer)
https://issuetracker.google.com/issues/148894990
function lockrow(){
// timer to run every hour
var ss = SpreadsheetApp.getActiveSpreadsheet();
var gpsht = ss.getSheetByName("GatePass");
var gplr=gpsht.getLastRow();
var locks = gpsht.getRange("W:X").getValues();
for (i=gplr-1 ; i > 1 ; i--){//
if (locks[i][1]==1) { //printed
if (locks[i][0]=="") { //not locked
Logger.log("Locking row - " + (i+1));
var range = gpsht.getRange('A'+(i+1)+':Q'+(i+1)).activate();
Logger.log(2)
var protection = range.protect().setDescription('Locked');
Logger.log(3)
protection.removeEditors(protection.getEditors());
Logger.log(4)
Logger.log("Success")
gpsht.getRange("W"+(i+1)).setValue(1);
Logger.log(5)
SpreadsheetApp.flush();
Utilities.sleep(3000) ;
}
}//if
}//for
}//function
````

Related

Error when assigning auto-created label to threads (TypeError: thread[t].addLabel is not a function) - google app script

I'm trying to assign auto-created label to gmail threads. The label alone was successfully created but it couldn't be assigned to the respective threads due to a TypeError.
Here's the script
//call cell value by column (Sheet: "SupplierNumber")
//label created based on the "Label" column in Sheet: SupplierNumber
//Label applied to related threads
function callLabelcolumn()
{
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheetByName('SupplierNumber');
var rg=sh.getRange('A2:A');
var vA=rg.getValues();
var sh3 = ss.getSheetByName("SearchParameter")
var rg3=sh3.getRange('A2');
var searchPara =rg3.getValues();
var threads = GmailApp.search(searchPara);
for (var i = 0; i < threads.length; i++){
var msg = threads[i].getMessages();
for (var j = 0; j < msg.length; j++){
// Get same thread by its ID.
//var threadID = GmailApp.getThreadById(msg[j].getId());
var msgid = msg[j].getId();
}
}
vA.forEach(function(row) {
if(row != "" && row != "#N/A"){
Logger.log('Label: %s', row);
var label = GmailApp.createLabel(row);
Logger.log("label: " + label);
}
var thread = [0];
var thread = GmailApp.getMessageById(msgid).getThread().getId();
var name = GmailApp.getUserLabelByName(label.getName())
for(var t in thread){
console.log("So far so good. Let's add label")
thread[t].addLabel(name);
}
});
}
The error is " TypeError: thread[t].addLabel is not a function " when run script in the editor.
TypeError: thread[t].addLabel is not a function
After debugging, I'm suspecting var name returned "" or null which is why the error is happening.
Debug result: thread[t].addLabel
I tried debugging on var name, some of results were name: undefined, t: undefined which I think is the cause of the TypeError.
Debug result: var name = GmailApp.getUserLabelByName(label.getName())
So is there a way to solve this?
The main problem lies in the line
var thread = GmailApp.getMessageById(msgid).getThread().getId();
The method addLabel() needs to be applied on the thread resource rather than a threadId.
A working code snippet would be:
var thread = GmailApp.getMessageById(msgid).getThread();
var name = GmailApp.getUserLabelByName(label.getName())
console.log("So far so good. Let's add label")
thread[t].addLabel(name);
Other wissue in your code are:
You are trying to loop through thread, but in your case thread is not array, but a single value
You retrieve the msgid within a lopo and overwrite it within each iteration, however you use msgid for retrieving the thread only after exiting the loop - this means only for the very last value of msgid.

my code doesn't work anymore but i haven't changed it

i created several months ago a script to publish assignments for my students in google classroom.the code is the following
function createTrigger() {
// Trigger every day at 9
ScriptApp.newTrigger('pubblicavideo')
.timeBased()
.atHour(9)
.everyDays(1) // Frequency is required if you are using atHour() or nearMinute()
.create();
}
function onOpen() {
//aggiunge il bottone autotrigger con run e stop all'apertura del documento spreadsheet
var ui = SpreadsheetApp.getUi();
ui.createMenu("Auto Trigger")
.addItem("Run","runAuto")
.addItem("Stop","deleteTrigger")
.addToUi();
}
function runAuto() {
// resets the loop counter if it's not 0
refreshUserProps();
// clear out the sheet
clearData();
// create trigger to run program automatically
createTrigger();
}
function refreshUserProps() {
var userProperties = PropertiesService.getUserProperties();
userProperties.setProperty('loopCounter', 0);
userProperties.setProperty('contarighe', 1);
}
function deleteTrigger() {
// Loop over all triggers and delete them
var allTriggers = ScriptApp.getProjectTriggers();
for (var i = 0; i < allTriggers.length; i++) {
ScriptApp.deleteTrigger(allTriggers[i]);
}
}
function pubblica(k)
{
var corso = Classroom.Courses.get(XXXXXXXXXXXXXX);
var foglio = SpreadsheetApp.getActive();
var linkini = foglio.getRange("C709:C3014");
var titolini = foglio.getRange("B709:B3014");
var autorini = foglio.getRange("A709:A3014");
var cell = linkini.getCell(k, 1);
var cella = titolini.getCell(k, 1);
var cello = autorini.getCell(k, 1);
var link = cell.getValue();
var titolo = cella.getValue();
var autore = cello.getValue();
var courseWork = {
'title': titolo,
'description': autore,
'materials': [
{'link': { "url": link}}
],
'workType': 'ASSIGNMENT',
'state': 'PUBLISHED',
}
Classroom.Courses.CourseWork.create(courseWork,XXXXXXXXXXXXXX);
}
function clearData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Data');
// clear out the matches and output sheets
var lastRow = sheet.getLastRow();
if (lastRow > 1) {
sheet.getRange(2,1,lastRow-1,1).clearContent();
}
}
function pubblicavideo()
{
var pezzialgiorno = 3; //numero di pezzi da pubblicare ogni giorno
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Data');
var userProperties = PropertiesService.getUserProperties();
var loopCounter = Number(userProperties.getProperty('loopCounter'));
var contarighe = Number(userProperties.getProperty('contarighe'));
// put some limit on the number of loops
// could be based on a calculation or user input
// using a static number in this example
var limit = 2301;
// if loop counter < limit number, run the repeatable action
if (loopCounter < limit) {
// see what the counter value is at the start of the loop
Logger.log(loopCounter);
// do stuff
for (var i=0; i<pezzialgiorno; i++)
{
pubblica(contarighe);
contarighe++;
}
// increment the properties service counter for the loop
loopCounter +=1;
userProperties.setProperty('loopCounter', loopCounter);
userProperties.setProperty('contarighe', contarighe);
// see what the counter value is at the end of the loop
Logger.log(loopCounter);
Logger.log(contarighe);
}
// if the loop counter is no longer smaller than the limit number
// run this finishing code instead of the repeatable action block
else {
// Log message to confirm loop is finished
sheet.getRange(sheet.getLastRow()+1,1).setValue("Finished");
Logger.log("Finished");
// delete trigger because we've reached the end of the loop
// this will end the program
deleteTrigger();
}
}
where i put the XXXXXXXXXXX there is the course ID.
the script is attached to a spreadsheet with 3 columns that have title, author and youtube link and the assignment is one of these videos put there as a link attached to the material. the script should publish every day at 9:00 a.m. 3 of the youtube videos in 3 different materials running down the list in the spreadsheet
when i try to execute the function called pubblicavideo it says
GoogleJsonResponseException: Chiamata API a classroom.courses.courseWork.create non riuscita con errore: Invalid value at 'course_work.description' (TYPE_STRING), 883 (riga 69, file "Codice")
i think the translation in english goes something like
GoogleJsonResponseException: API call to classroom.courses.courseWork.create didn't work with error: Invalid value at 'course_work.description' (TYPE_STRING), 883 (row 69, file "Code")
this very script worked perfectly until the 13th of august. i don't know if there were any changes in google classroom scripts.
anyone of you knows how to make it work again?
Answer:
The issue seems to be that at some point, the type of the value obtained from autorini.getCell(k, 1).getValue() is not a string which is causing the error.
Steps to Solve:
In pubblicavideo(), the function pubblica() is called inside a loop with the variable contarighe passed. The value of this gets put into k for each run of pubblica.
These are the lines that are problematic:
var autorini = foglio.getRange("A709:A3014");
var cello = autorini.getCell(k, 1);
var autore = cello.getValue();
You will need to check the value of autore before you make the request to Classroom.Courses.CourseWork.create().
As per the documentation on the CourseWork Resource, the value of description should be of type String.
When creating the coursework object:
var courseWork = {
'title': titolo,
'description': autore,
'materials': [
{'link': { "url": link}}
],
'workType': 'ASSIGNMENT',
'state': 'PUBLISHED',
}
The value of autore must reflect this. Make sure that at whatever point in the loop your code is halting, your sheet value is both of type String, and being retrieved correctly.
References:
REST Resource: courses.courseWork | Classroom API | Google Developers

Need to Copy data from email and paste into google spreadsheet

There was two function wrote in that answer code, one is main and another one is extract details so its take two function, whenever ran the script its throwing error like getdate() not define so i just make as one function as main, now the data tracked automatically into sheet but its reading only first thread which is unread label. if the label having 23 thread , the first thread only repeating as 23 times. even I Have changed threads.messages(0) tracking only first thread which is already read thread, not read and track unread thread. Could you help me
function main(){
var query = {'q':'label:"Sanmina EDI Failed Concurrent Jobs Alert" is:UNREAD'};
var threads = Gmail.Users.Messages.list(userId='me', q=query);
for(i in threads.messages){
var threadIds = threads.messages[0].threadId;
var message = GmailApp.getMessageById(threadIds);
extractDetails(message);
GmailApp.markMessageRead(message);
}
function extractDetails(message)
{
var dateTime = message.getDate();
var bodyContents = message.getBody();
var action= bodyContents.search("Invoice")
var action1=bodyContents.search("Error")
var action2=bodyContents.search("Terminated")
if(action > 0)
{
var out="Need to create SR"
}
else if(action1>0 || action2>0)
{
var out="Need to create SR"
}
else
{
var out="Printing output file"
}
var activeSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
activeSheet.autoResizeRows(1, 2);
activeSheet.appendRow([dateTime, bodyContents, out]);
}
}
[enter image description here][1]
Sanmina EDI Failed Concurrent Jobs Alert - subject same name for label
Production Workflow Mailer
May 26, 2017, 7:30 PM
Request Id Program Name Status Requested By
---------- ------------------------------------------------------------ -------- 813511350 Sanmina EDI 850/860 Inbound PO/Change Tabular Report Warning NERODRIG
Try using the Gmail API to search the threads:
function main(){
var query = {'q':'label:"Sanmina EDI Failed Concurrent Jobs Alert" is:UNREAD'};
var threads = Gmail.Users.Messages.list(userId='me', q=query);
for(i in threads.messages){
var threadIds = threads.messages[i].threadId;
var message = GmailApp.getMessageById(threadIds);
extractDetails(message);
GmailApp.markMessageRead(message);
}
}
function extractDetails(message){
var dateTime = message.getDate();
var bodyContents = message.getPlainBody();
var action= bodyContents.search("Invoice")
var action1=bodyContents.search("Error")
var action2=bodyContents.search("Terminated")
if(action > 0)
{
var out="Need to create SR"
}
else if(action1>0 || action2 > 2)
{
var out="Need to create SR"
}
else
{
var out="Printing output file"
}
var activeSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
activeSheet.appendRow([dateTime, bodyContents, out]);
}
In case you only want the last message, use threads.messages[0] instead of i.
More information:
List method

How to check Gmail Thread for Replies from Email

I am creating a basic CRM that needs to mark when a thread has been replied to.
I have created a script that can scan my inbox for threads from a list of emails in a sheet, check the last message in each thread and collect the .getFrom in order to see if I was the last to reply.
However, I can't figure out how to check if there has been a response from the person who's been contacted throughout the whole thread.
Here's the script that checks for the last message. (It's an extract of a larger script in case any references are missing):
Example Sheet
function UpdateStatus() {
// Connect to our active sheet and collect all of our email addresses in column G
var sheet = SpreadsheetApp.getActiveSheet();
var totalRows = sheet.getLastRow();
var range = sheet.getRange(2, COLUMN_WITH_EMAIL_ADDRESSES, totalRows, 1);
var emails = range.getValues();
// Attempt to iterate through 100 times (although we'll timeout before this)
for (var cntr = 0; cntr<100; cntr++ ) {
// If we've reached the end of our last, wrap to the front
if (lastRowProcessed >= totalRows) lastRowProcessed = 1;
// Increment the row we're processing
var currentRow = lastRowProcessed+1;
// Get the email address from the current row
var email = emails[currentRow-2][0];
// If the email address field is empty, skip to the next row
if (!email) {
lastRowProcessed = currentRow;
cache.put("lastRow", currentRow, 60*60*24);
continue;
}
// Look for all threads from me to this person
var threads = GmailApp.search('from:me to:'+email);
// If there are no threads, I haven't emailed them before
if (threads.length == 0) {
// Update the spreadsheet row to show we've never emailed
var range = sheet.getRange(currentRow,13, 1, 4 ).setValues([["NEVER", "", "", ""]] );
// And carry on
lastRowProcessed = currentRow;
cache.put("lastRow", currentRow, 60*60*24); // cache for 25 minutes
continue;
}
// Beyond a reasonable doubt
var latestDate = new Date(1970, 1, 1);
var starredMsg = "";
var iReplied = ""
// Iterate through each of the message threads returned from our search
for (var thread in threads) {
// Grab the last message date for this thread
var threadDate = threads[thread].getLastMessageDate();
// If this is the latest thread we've seen so far, make note!
if (threadDate > latestDate) {
latestDate = threadDate;
// Check to see if we starred the message (we may be back to overwrite this)
if (threads[thread].hasStarredMessages()) {
starredMsg = "★";
} else {
starredMsg = "";
}
// Open the thread to get messages
var messages = threads[thread].getMessages();
// See who was the last to speak
var lastMsg = messages[messages.length-1];
var lastMsgFrom = lastMsg.getFrom();
// Use regex so we can make our search case insensitive
var re = new RegExp(email,"i");
// If we can find their email address in the email address from the last message, they spoke last
// (we may be back to overwrite this)
if (lastMsgFrom.search(re) >= 0) {
iReplied = "NO";
} else {
iReplied = "YES";
}
}

Logging (lots of) Gmail Data into a Google Sheet

The object of my Google App Script is to log all basic data from email within a certain range of time (not yet implemented), from within all labels, into a Google Sheet.
This currently works to a limited degree. It will collect a small number of emails, but if I increase this (I have a lot of emails to log), this code will either ‘Exceeded maximum execution time’ if I pause for 500 or 1000 milliseconds (as below), otherwise I hit the other ratemax quota Service invoked too many times in a short time: gmail rateMax. Try Utilities.sleep(1000) between calls.
In the code below, I believe I attempted to repeat the main FOR loop 20 times with a WHILE loop, this was a way of seeing if I could perform this single grab n load var labelThreads = GmailApp.getUserLabelByName(label).getThreads(start, 1), 20 times. This would be one way to begin tracking "batches" -- It didn't quite work and I believe there is a better way to approach this, need some help.
function whenV24() {
function setColumnNames(range, columnNames){
var cell = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getRange(range);
cell.setValues(columnNames);
}
setColumnNames("A1:G1", [["Date","Label","To","From","Subject","Body","File Names"]]);
betterGetV24();
}
function betterGetV24() {
var myspreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var mysheet = myspreadsheet.getSheets()[0];
var threads = GmailApp.getInboxThreads();
var messages = GmailApp.getMessagesForThreads(threads);
// ** LABEL NAME COLLECTION **
var labels = GmailApp.getUserLabels();
// ** CREATE EMPTY DATA ARRAYS **
var emailFrom = [];
var emailTo = [];
var emailBody = [];
var emailDate = [];
var emailLabel = [];
var emailSubject = [];
// ** LOAD "INBOX EMAIL DATA" INTO EMPTY ARRAYS **
for(var i = 0; i < threads.length; i++) {
emailBody.push([" "]);
emailFrom.push([messages[i][0].getFrom()]);
emailTo.push([messages[i][0].getTo()]);
emailSubject.push([messages[i][0].getSubject()]);
emailDate.push([Utilities.formatDate(messages[i][0].getDate(), "GMT", "yyyy-MM-dd'T'HH:mm:ss'Z'")]);
emailLabel.push(["Inbox"]);
};
// ** LOAD "LABELED EMAIL DATA" INTO EMPTY ARRAYS **
for (var l = 0; l < labels.length; l++) { // ** Runs a for-loop over "labels array".
var label = labels[l].getName(); // Gets "this" label name.
var start = 0; // sets start number as 0
var tracker = 0;
// this section of code has to loop based on a separate set of logic
while (start < 20){
tracker++;
Logger.log("tracker :" + tracker);
var labelThreads = GmailApp.getUserLabelByName(label).getThreads(start, 1); // Gets threads in "this" label. (*Set Limits Here*)
var labelMessages = GmailApp.getMessagesForThreads(labelThreads); // Gets array with each email from "this" thread.
Utilities.sleep(500); // pause in the loop for 500 milliseconds
for (var t = 0; t <labelThreads.length; t++){ // ** Runs a for-loop over threads in a label.
Logger.log("part 1 - inside for-loop over message number: " + labelMessages[t][0].getId());
Utilities.sleep(500);// **pause in the loop for 500 milliseconds
if (labelMessages[t] == undefined){} // If it's empty, skip.
else { // If it's not empty.
Logger.log("part 2 - inside if statement in for-loop > push emailData into arrays");
emailBody.push([" "]);
emailFrom.push([labelMessages[t][0].getFrom()]);
emailTo.push([labelMessages[t][0].getTo()]);
emailDate.push([Utilities.formatDate(labelMessages[t][0].getDate(), "GMT", "yyyy-MM-dd'T'HH:mm:ss'Z'")]);
emailSubject.push([labelMessages[t][0].getSubject()]);
emailLabel.push([labels[l].getName()]);
mysheet.getRange(2,2,emailLabel.length,1).setValues(emailLabel);
}
}
Logger.log("part 3 - outside if statement -> start += 2")
var start = start + 3;
}
Logger.log("part 4 - outside while loop");
}
// ** THEN, LOG THE FILLED DATA ARRAYS TO ROWS **
//getSheetValues(startRow, startColumn, numRows, numColumns)
mysheet.getRange(2,4,emailFrom.length,1).setValues(emailFrom);
mysheet.getRange(2,3,emailTo.length,1).setValues(emailTo);
mysheet.getRange(2,1,emailDate.length,1).setValues(emailDate);
mysheet.getRange(2,5,emailSubject.length,1).setValues(emailSubject);
mysheet.getRange(2,6,emailBody.length,1).setValues(emailBody);
}
I think at this point I should be using triggers, but it seems like I also have to track the previous batch of emails-logged and continue to the next batch. I'm also not aware of how to tie this in with triggers.
Thanks for reading. Any help is appreciated.
EDIT
I was setting data to the spreadsheet incorrectly here. For each loop I was trying to set data in the spreadsheet. I'm not sure how I ended up doing that. Simply moving this out of the loop and setting it later fixed my issue. updated code here: http://pastie.org/9793256#96,100,109,117,123-125,131,135-139
for (var t = 0; t <labelThreads.length; t++){ // ** Runs a for-loop over threads in a label.
Logger.log("part 1 - inside for-loop over message number: " + labelMessages[t][0].getId());
Utilities.sleep(500);// **pause in the loop for 500 milliseconds
if (labelMessages[t] == undefined){} // If it's empty, skip.
else { // If it's not empty.
Logger.log("part 2 - inside if statement in for-loop > push emailData into arrays");
emailBody.push([" "]);
emailFrom.push([labelMessages[t][0].getFrom()]);
emailTo.push([labelMessages[t][0].getTo()]);
emailDate.push([Utilities.formatDate(labelMessages[t][0].getDate(), "GMT", "yyyy-MM-dd'T'HH:mm:ss'Z'")]);
emailSubject.push([labelMessages[t][0].getSubject()]);
emailLabel.push([labels[l].getName()]);
// ** INCORRECTLY TRYING TO SET DATA PER LOOP **
mysheet.getRange(2,2,emailLabel.length,1).setValues(emailLabel);
}
}
You can use PropertiesService to locally store the index of the last processed email. You can then add the "start" parameter to the GmailApp.search() method to begin searching from the last position and set this whole thing to trigger every 5 or 10 minutes.
function myTrigger() {
var start = PropertiesService.getScriptProperties().getProperty("startIndex");
var threads = GmailApp.search("in:inbox", start, 200);
for (var t in threads) {
// Log the thread using your existing code
start++;
}
PropertiesService.getScriptProperties().setProperty("startIndex", start);
}