How I can know the limit for groups read? - google-apps-script

I'm working into the migration of a large list to Google Groups from Google Apps Script.
After some creation of members, i'm having this Exception:
Exception: Service invoked too many times for one day: premium groups
read.
There is one way to know the available quote of reads for integrate this verification into my code.
This is my function:
function readSheet(){
var sheetId='MY-ID';
var sheet = SpreadsheetApp.openById(sheetId);
var toIgnore = 1;
var mData = sheet.getDataRange().offset(toIgnore, 0, sheet.getLastRow() - toIgnore).getValues();
for (var i = 0; i < mData.length; i++) {
/*Here verify quota*/
addUsertoGroup(mData[i][0]);
}
}
How I can achieve this?

Your script reaches a quota or limitation.
You can see the daily quotas for Groups read in the table below:
You can use a try..catch statement to ignore the error:
function readSheet(){
var sheetId='MY-ID';
var sheet = SpreadsheetApp.openById(sheetId);
var toIgnore = 1;
var mData = sheet.getDataRange().offset(toIgnore, 0, sheet.getLastRow() - toIgnore).getValues();
for (var i = 0; i < mData.length; i++) {
try{
addUsertoGroup(mData[i][0]);
}
catch(e){
console.log("The script reached the daily quota: ",e);
}
}
}

Related

Google Groups: Getting the "Premium Groups Read" error while running Appscript to fetch all the email IDs

I am using the following code to fetch the email data from Google groups. It is working well for most of the groups, but for the ones that has more than 90-100 email IDs, it's returning an error : "Exception: Service invoked too many times in a short time: premium groups read. Try Utilities.sleep(1000) between calls."
I am not able to find any solution to the above, could someone help me figure this out? I have GSuite enterprise edition.
var wsSettings = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("INPUT");
var name = wsSettings.getRange("B1").getValue();
try {
var group = GroupsApp.getGroupByEmail(name);
}
catch(e) {
var group = false;
}
if (group) {
var members = group.getUsers();
var membersLength = members.length;
for (var i=0; i<membersLength; i++) {
var memberEmailAddress = members[i].getEmail();
var memberRole = group.getRole(memberEmailAddress).toString().toLowerCase();
memberDetails.push([memberEmailAddress, memberRole]);
}
var groupMembersSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Final List');
var lastRow = groupMembersSheet.getLastRow();
groupMembersSheet.getRange(2, 1, lastRow, 2).clearContent();
groupMembersSheet.getRange(2, 1, memberDetails.length, 2).setValues(memberDetails);
}
else {
}

Unprotecting protected sheet so that others can run script and then protect the sheet again

I have compiled a list of scripts that I run on a sheet. I'm not a programmer I'm still learning so I have used some code from other people.
The following are the only unprotected ranges B2:C2,N5:N43 but for the other scripts to run the whole sheet needs to be unprotected and protected again.
Using Google Apps Script, you can modify your scripts so that before running they unprotect your sheets and ranges, and after running they re-protect them. You could use code such as the following:
function unProtectAndProtect() {
var sheetProtections = SpreadsheetApp.getActive().getProtections(SpreadsheetApp.ProtectionType.SHEET);
var rangeProtections = SpreadsheetApp.getActive().getProtections(SpreadsheetApp.ProtectionType.RANGE);
var protectionData = {
sheetProtections: [],
rangeProtections: []
};
for (var i=0; i<sheetProtections.length; i++) {
var protection = {};
protection['editors'] = sheetProtections[i].getEditors();
protection['description'] = sheetProtections[i].getDescription();
protection['range'] = sheetProtections[i].getRange();
protection['unprotected ranges'] = sheetProtections[i].getUnprotectedRanges();
protection['candomainedit'] = sheetProtections[i].canDomainEdit();
protection['iswarningonly'] = sheetProtections[i].isWarningOnly();
sheetProtections[i].remove();
protectionData.sheetProtections.push(protection);
}
for (var i=0; i<rangeProtections.length; i++) {
var protection = {};
protection['editors'] = rangeProtections[i].getEditors();
protection['description'] = rangeProtections[i].getDescription();
protection['range'] = rangeProtections[i].getRange();
protection['unprotected ranges'] = rangeProtections[i].getUnprotectedRanges();
protection['candomainedit'] = rangeProtections[i].canDomainEdit();
protection['iswarningonly'] = rangeProtections[i].isWarningOnly();
rangeProtections[i].remove();
protectionData.rangeProtections.push(protection);
}
try {
/**
*
* HERE YOU CAN RUN YOUR SCRIPT
*
**/
catch(e) {
Logger.log("Caught exception: " + e.toString());
}
for (var i=0; i<protectionData.sheetProtections.length; i++) {
var sheet = protectionData.sheetProtections[i]['range'].getSheet();
var protection = sheet.protect()
.setDescription(protectionData.sheetProtections[i]['description'])
.setRange(protectionData.sheetProtections[i]['range'])
.setUnprotectedRanges(protectionData.sheetProtections[i]['unprotected ranges'])
.setDomainEdit(protectionData.sheetProtections[i]['candomainedit'])
.setWarningOnly(protectionData.sheetProtections[i]['iswarningonly']);
var protectionEditors = protectionData.sheetProtections[i]['editors'];
// add Editors
for (var j=0; j<protectionEditors.length; j++) {
protection.addEditor(protectionEditors[j]);
}
}
for (var i=0; i<protectionData.rangeProtections.length; i++) {
var range = protectionData.rangeProtections[i]['range'];
var protection = range.protect()
.setDescription(protectionData.rangeProtections[i]['description'])
.setDomainEdit(protectionData.rangeProtections[i]['candomainedit'])
.setWarningOnly(protectionData.rangeProtections[i]['iswarningonly']);
var protectionEditors = protectionData.rangeProtections[i]['editors'];
// add Editors
for (var j=0; j<protectionEditors.length; j++) {
protection.addEditor(protectionEditors[j]);
}
}
}
The idea would be to actually run your script's code where the HERE YOU CAN RUN YOUR SCRIPT comment is located, which is the point at which the protections are removed from the Sheet and saved in memory. Afterwards, they are retrieved from memory and put back into the Sheet.
You must be careful, however, of the actual script exceeding the runtime limit (see quotas). If this situation occurs, the script will halt without re-setting your protections.
In case you are interested about protections in Google Apps Scripts, I suggest you check out the following link:
Class Protection

Getting the body of individual emails from gmail to google sheets

I'm really new at using Google Apps Script, so if what I'm trying doesn't make sense, or just isn't possible please let me know.
Everyday I get several emails that look like the following:
Your Name: FirstName LastName
Phone Number: 555 867 5309
Email Address: FakeEmail#email.com
What do you need help with? Request someone makes.
I'm attempting to automatically send the body of these emails to a new line in a Google Sheet when they come in.
As of right now I have every email get the label "myLabel" when it comes in. I then run the following script, which is a slightly modified version of something I found here:
function myFunction() {
var ss = SpreadsheetApp.getActiveSheet();
var label = GmailApp.getUserLabelByName("MyLabel");
var threads = label.getThreads();
for (var i=0; i<threads.length; i++)
{
var messages = threads[i].getMessages();
for (var j=0; j<messages.length; j++)
{
var msg = messages[j].getBody();
ss.appendRow([msg])
}
threads[i].removeLabel(label);
}
}
I'm attempting to run this code with a timer trigger every 15 minutes. The issue I've run into is that every time the code runs it pulls from every email in the thread. I would like it to just pull from the emails that are new since the last time it ran. Any advice would be greatly appreciated.
Why not mark the messages as read when you finish processing them? Here is a sample from one of my scripts.
var pendingEmailLabel = "MyLabel";
var threads = GmailApp.getUserLabelByName(pendingEmailLabel).getThreads();
for (var t = 0; t < threads.length; ++t) {
var thread = threads[t];
var messages = thread.getMessages();
for (var m = 0; m < messages.length; ++m) {
var message = messages[m];
if (message.isUnread()) {
// INSERT YOUR CODE HERE THAT TAKES ACTION ON THE MESSAGE
message.markRead();
}
}
}
}

Time-Driven trigger for mail forwarding

I'm attempting to test a script that I'm working on. The script is fine, it executes successfully when I trigger it manually. When I add a time driven script of every minute interval the scrips starts throwing an exception after couple of hrs .
Exception: Service invoked too many times for one day: gmail
I checked the daily quota of email and found that i still have mail quota left
var quota = MailApp.getRemainingDailyQuota();
Logger.log(quota);
Also I am able to receive the try catch email but the mails are not forwarded .
Is this because of the execution time quota associated with the trigger? Below is the code
function MailForward() {
try{
var glabel = createLabel_("Mail-Forwarded");
var rtm_email = 'abc#abc.com';
var from_email = Session.getActiveUser().getEmail();
var threads = GmailApp.search('in:inbox is:unread newer_than:1d');
var mForward = 0;
for (var i=0;i<threads.length;i++) {
var messages=threads[i].getMessages();
for (var m = 0; m < messages.length; m++){
if (messages[m].isUnread()){
mForward = 0;
var mlabels = threads[i].getLabels();
for (var j = 0; j < mlabels.length; j++) {
Logger.log(mlabels[j].getName());
if (mlabels[j].getName() === "Mail-Forwarded") {
mForward = 1;
}
}
if (mForward===0) {
// Logger.log(messages.length)
// Logger.log(messages[m].getFrom());
var from = messages[m].getFrom();
//Logger.log(messages[m].getDate());
var date = messages[m].getDate();
// Logger.log(messages[m].getSubject());
var subject = messages[m].getSubject();
// Logger.log(messages[m].getTo());
var to = messages[m].getTo();
var body = messages[m].getBody();
var attachment = messages[m].getAttachments();
var emailoptions = ("---------- Forwarded message ----------" +'<br>'+'From: '+from+ "<'" + from.replace(/^.+<([^>]+)>$/, "$1") +"'>"+'<br>'+ 'Date: '+date+'<br>'+ 'Subject: ' +subject+'<br>'+
'To: ' +to+ "<'" + to.replace(/^.+<([^>]+)>$/, "$1") +"'>"+'<br>'+'<br>'+'<br>');
messages[m].forward(rtm_email,{htmlBody: emailoptions + body , Attachment: attachment});
glabel.addToThread(threads[i]);
Logger.log(glabel.getName());
messages[m].markRead();
mForward = 1;
}
}
}
}
} catch(e) {
MailApp.sendEmail("abc#abc.com", "Exception found in Sript", e );
Logger.log(e);
}
}
You checked quota using MailApp.getRemainingDailyQuota(); not GmailApp. These are two different services.
The quota method returns only "the number of remaining emails a user can send for the rest of the day." But the limit you are hitting is the number of times you invoked the service, for whatever purpose.
You are using GmailApp a lot to access existing messages, not so much to send new ones. In particular, you are checking every message in every thread from today, and do it every minute. That's a lot of API calls: getMessages, isUnread, etc.
One way to reduce the number of API calls is to have more targeted search. The after: search parameter accepts Unix timestamp, which makes it possible to do the following:
function doSomethingWithNewEmail() {
var interval = 5; // if the script runs every 5 minutes; change to 1 if it runs every minute
var date = new Date();
var timeFrom = Math.floor(date.valueOf()/1000) - 60 * interval;
var threads = GmailApp.search('is:inbox after:' + timeFrom);
for (var i = 0; i < threads.length; i++) {
// do something
}
}
I successfully used the above approach with 5 minute interval. It may work with 1 minute too, since most of the time search will be the only API call made by the script.

Google Apps Script ScriptApp.getUserTriggers returns empty array when called on another project that contains user triggers

I am trying to use the Google Apps Script ScriptApp getUserTriggers(spreadsheet) method to create a report that displays all of my user-installed triggers across all of my projects.
This code loops through all of the files in my Google Drive and makes an array of file IDs for all of the files that are Google Sheets files, and then attempts to get the triggers for each file:
var spreadsheetIds = [];
// get all spreadsheets in my google drive
var files = DriveApp.getFiles();
while (files.hasNext()) {
var file = files.next();
var type = file.getMimeType();
if (type == 'application/vnd.google-apps.spreadsheet') spreadsheetIds.push(file.getId());
}
var data = [];
try {
for (var i = 0; i < spreadsheetIds.length; i++) {
var ssOpen = SpreadsheetApp.openById(spreadsheetIds[i]);
Utilities.sleep(100);
var triggers = ScriptApp.getUserTriggers(ssOpen);
var spreadsheetName = ssOpen.getName();
Logger.log(spreadsheetName + ' triggers: ' + triggers.length);
for (var j = 0; j < triggers.length; j++) {
Logger.log(spreadsheetName + ' trigger ID: ' + triggers[j].getUniqueId());
data.push([spreadsheetName, triggers[j].getEventType(), triggers[j].getHandlerFunction(), triggers[j].getTriggerSource(), triggers[j].getTriggerSourceId(), triggers[j].getUniqueId()]);
}
}
} catch(e) {
Logger.log(e);
}
Many of my projects have user-installed triggers, but this just returns 0 for all of them (empty arrays are returned from ScriptApp.getUserTriggers()).
I am 100% sure that I am logged in to the same account when running this code as when I've set the triggers.
But just to be sure, when I add a test trigger to this project and run this code it correctly returns the number of triggers (which is 1):
function getCurrentProjectTriggersCount() {
Logger.log('Current project has ' + ScriptApp.getProjectTriggers().length + ' triggers.');
}
This has been previously reported as Issue 4562 - getProjectTriggers() always returns the triggers for the script it's contained in.
The usual advice is to visit and star the issue to increase its priority, and receive updates.