GMailApp rateMax - service invoked too fast - google-apps-script

I published a Google Apps Script as a WebApp, but sometimes the users using the script run into:
Service invoked too many times in a short time: gmail rateMax. Try Utilities.sleep(1000) between calls. (line XXX)
The exception tells me to slow it down with a Utitlities.sleep(1000), however before doing that I'd like to know what exactly the maximum rate is. The only documentation I can find about this is the quota page, but this says:
GMail Read: 10000 / day
and my script is far away from 10000 reads.
Does anybody know what rateMax exactly refers to?
Update: The code causing this is the following (gets called via XHR):
add = function(form) {
// [...]
messageId = (_ref = form.msgId) != null ? _ref : form.messageId;
if (!messageId || !(message = GmailApp.getMessageById(messageId))) {
throw ErrorCodes.INVALID_MESSAGE_ID;
}
// [...]
thread = GmailApp.getThreadById(message.getThread().getId());
if (String(form.archive) === "true") {
thread.moveToArchive();
}
// [...]
addLabel(LABEL_BASE, thread);
addLabel(LABEL_OUTBOX, thread);
};
getLabel = function(name, create) {
var _ref;
return (_ref = GmailApp.getUserLabelByName(name)) != null ? _ref : (create ? GmailApp.createLabel(name) : void 0);
};
addLabel = function(name, thread) {
var _ref;
if ((_ref = this.getLabel(name, true)) != null) {
_ref.addToThread(thread);
}
};
// [...] denotes removed code from the sample that does not do calls to the GMail API.

Were you checking attachments, or doing something else? There was a bug that should be fixed now that triggered this when loading attachments.

Related

"Bad Request" error for chromeosdevices.list

I am trying to figure out how to get device information about Chromebooks that my organization uses (using Google Apps Scripts.) Being new to this, I basically tried to modify a tutorial code that would get users from the organization. When I execute the code for users, it works fine, but gives me a "Bad Request (line 7, file "Code") error when I try to run it as below:
function getDevices() {
var optionalArgs = {
customerId: 'XXXXXXXXXXX',
maxResults: 10,
orderBy: 'serialNumber'
};
var response = AdminDirectory.Chromeosdevices.list(optionalArgs);
var devices = response.devices;
if (devices && devices.length > 0) {
Logger.log('Devices:');
for (i = 0; i < devices.length; i++) {
var device = devices[i];
Logger.log('%s (%s)', device.serialNumber, device.lastSync);
}
} else {
Logger.log('No devices found.');
}
}
I know it's referencing this line for the error:
var response = AdminDirectory.Chromeosdevices.list(optionalArgs);
I checked with Google's Directory API documentation to make sure I was passing parameters correctly, but I don't see that being an issue. I have enabled the API under Advanced Google Services, and enabled it on the Cloud Platform API Dashboard (the dashboard shows the requests and errors of my attempts.)
Any ideas?
I tinkered around with the script and tried entering the customerID directly to the parameters instead of passing it with the other arguments, and I no longer receive the error:
function getDevices() {
var optionalArgs = {
maxResults: 200,
orderBy: 'serialNumber'
};
var response = (AdminDirectory.Chromeosdevices.list("my_customer", optionalArgs));
I realize the that my_customer parameter is optional with the Users.list request, it is required with Chromeosdevices.list. I am not sure why it does not like having that argument passed, it seems to have been the root of my problem. While it didn't list any devices for me in the log, it did post "No devices found", which means the everything else executed.

Supply API key to avoid Hit Limit error from Maps Service in Apps Script

I have a Google Sheet where we are fetching the driving distance between two Lat/Lng via the Maps Service. The function below works, but the matrix is 4,500 cells, so I'm getting the "Hit Limit" error.
How can I supply my paid account's API key here?
Custom Function
function drivingMeters(origin, destination) {
if (origin=='' || destination==''){return ''}
var directions = Maps.newDirectionFinder()
.setOrigin(origin)
.setDestination(destination)
.getDirections();
return directions.routes[0].legs[0].distance.value ;
}
Example use:
A1: =drivingMeters($E10,G$9)
Where E10 = 42.771328,-91.902281
and G9 = 42.490390,-91.1626620
Per documentation, you should initialize the Maps service with your authentication details prior to calling other methods:
Your client ID and signing key can be obtained from the Google Enterprise Support Portal. Set these values to null to go back to using the default quota allowances.
I recommend storing these values in PropertiesService and using CacheService, to provide fast access. Using this approach, rather than writing them in the body of your script project, means they will not be inadvertently copied by other editors, pushed to a shared code repository, or visible to other developers if your script is published as a library.
Furthermore, I recommend rewriting your custom function to accept array inputs and return the appropriate array output - this will help speed up its execution. Google provides an example of this on the custom function page: https://developers.google.com/apps-script/guides/sheets/functions#optimization
Example with use of props/cache:
function authenticateMaps_() {
// Try to get values from cache:
const cache = CacheService.getScriptCache();
var props = cache.getAll(['mapsClientId', 'mapsSigningKey']);
// If it wasn't there, read it from PropertiesService.
if (!props || !props.mapsClientId || !props.mapsSigningKey) {
const allProps = PropertiesService.getScriptProperties().getProperties();
props = {
'mapsClientId': allProps.mapsClientId,
'mapsSigningKey': allProps.mapsSigningKey
};
// Cache these values for faster access (max 6hrs)
cache.putAll(props, 21600);
}
// Apply these keys to the Maps Service. If they don't exist, this is the
// same as being a default user (i.e. no paid quota).
Maps.setAuthentication(props.mapsClientId, props.mapsSigningKey);
}
function deauthMaps_() {
Maps.setAuthentication(null, null);
}
// Your called custom function. First tries without authentication,
// and then if an error occurs, assumes it was a quota limit error
// and retries. Other errors do exist (like no directions, etc)...
function DRIVINGMETERS(origin, dest) {
if (!origin || !destination)
return;
try {
return drivingMeters_(origin, dest);
} catch (e) {
console.error({
message: "Error when computing directions: " + e.message,
error: e
});
// One of the possible errors is a quota limit, so authenticate and retry:
// (Business code should handle other errors instead of simply assuming this :) )
authenticateMaps_();
var result = drivingMeters_(origin, dest);
deauthMaps_();
return result;
}
}
// Your implementation function.
function drivingMeters_(origin, dest) {
var directions = Maps.newDirectionFinder()
...
}

Chrome speech API network error

I'm trying to create a PHP web application using Chrome web speech API. I'm always getting network errors. But, I have a good network connection.
window.SpeechRecognition = window.SpeechRecognition
|| window.webkitSpeechRecognition || null;
if (window.SpeechRecognition === null) {
} else {
var recognizer = new window.SpeechRecognition();
recognizer.continuous = false;
recognizer.lang = 'en-US';
recognizer.onstart = function() {
recognizing = true;
};
recognizer.onresult = function(event) {
};
}
recognizer.onerror = function(event) {
alert(event.error);
ignore_onend = true;
if (event.error == 'not-allowed') {
alert("Allow to access your microphone");
}
recognizer.stop();
recognizer.start();
};
Thanks
I have similar problem since the last Google update and I am sure that there is a bug in the last Update. I have similar thread at RecognizerIntent gives error after latest Google Search update
My android app gives network error for recognizerintent. Till then, there was no network problem.
In summary the error is W/JavaNetConnection﹕ Failed to get connection status. java.io.FileNotFoundException: https://www.google.com/m/voice-search/down?pair=6239918a-dc45-4eea-ac6f-b9bf8de57ced

Chrome Extension crashes with "bad extension message webRequestInternal.eventHandled"

I have a chrome extension that out of nowhere crashes, so I saw that you could debug your chrome by activating the logging, so that's what I did, and I noticed that before the crash happens, it's thrown an error of: " bad extension message webRequestInternal.eventHandled : terminating renderer.", so maybe this error occur in one of webRequests listeners. But I don't know what to do anymore to make it right.
This is the log error that happens before the function closes:
[1888:3844:17965500:ERROR:extension_function.cc(143)] bad extension message webRequestInternal.eventHandled : terminating renderer.
[1888:3844:17965625:VERBOSE1:web_request_time_tracker.cc(181)] WR percent 2643: http://mypage.com/test: = 0.985185
[1888:3844:17965625:VERBOSE1:web_request_time_tracker.cc(181)] WR percent 2644: http://mypage.com/test: 123/123 = 1
[1888:3464:17965734:VERBOSE1:speech_input_extension_manager.cc(228)] Extension unloaded. Requesting to enforce stop...
I have 2 webRequest listeners:
The OnBeforeRequest Page blocking:
chrome.webRequest.onBeforeRequest.addListener(blockURLs,
{urls: ["http://*\/*", "https://*\/*"]}, //I have to use all because I use specific page filters
["blocking"]
);
function blockURLs(details){
var url = details.url.split('/');
if(STRING_OF_SERVERS.indexOf(url[2]) < 0 || details.url.indexOf('.css') > -1 )
return {cancel: true};
}
And the onBeforeSendHeaders (This is probably the one erroneous):
chrome.webRequest.onBeforeSendHeaders.addListener(
function(details) {
var cookie_found = false;
for (var i = 0; i < details.requestHeaders.length; ++i) {
if (details.requestHeaders[i].name === 'Cookie') {
//details.requestHeaders.splice(i,1); //,localStorage['COOKIES']
//alert("ADDED: " + localStorage['COOKIES']);
if(window['SERVIDOR_TEMP_DATA_' + SERVER_INDEX]['COOKIES'] != ''){
details.requestHeaders[i] = new Object();
details.requestHeaders[i].name = 'Cookie';
details.requestHeaders[i].value = window['SERVIDOR_TEMP_DATA_' + SERVER_INDEX]['COOKIES'];
}else{
window['SERVIDOR_TEMP_DATA_' + SERVER_INDEX]['COOKIES'] = details.requestHeaders[i].value;
cookie_found = true;
break;
}
}
if(cookie_found == false && window['SERVIDOR_TEMP_DATA_' + SERVER_INDEX]['COOKIES'] != ''){
var i = details.requestHeaders.length;
details.requestHeaders[i] = new Object();
details.requestHeaders[i].name = 'Cookie';
details.requestHeaders[i].value = window['SERVIDOR_TEMP_DATA_' + SERVER_INDEX]['COOKIES'];
}
//console.log(details.url);
//console.log(details.requestHeaders);
return {requestHeaders: details.requestHeaders};
},
{urls: URLS_TYPE, types : ["main_frame", "sub_frame", "xmlhttprequest", "object", "stylesheet", "script", "image", "other"]},
["blocking", "requestHeaders"]);
//StartClicking();
});
The variables not specified:
*var URLS_TYPE is an array of Sites allowed
*var STRING_OF_SERVERS is a String containing all the possible combination of sites that are allowed
And in my application I make a lot of web requests, and I don't know what to do anymore :/
What could possibly be throwing this crash?
Thanks in advance.
I was able to find the answer.
the problem is that if the Request Headers come missing any information required , it just crashes instead of reporting an error.
My details.requestHeader was returning a cookie that only has a name and no value attribute, so If I added the value attribute the crash would go away.
Cookie object is supposed to come like this:
{ name: "key", value: "val"}
and I was generating only {name: "key"} since I was adding an undefined value, After I used JSON.Stringify(details.requestHeaders) I could see that it was missing and now problem solved.
I just had to make sure the cookie value wasn't undefined, if it was just add a empty string or whatever you want.

Geolocation feedback while accepting the request

the geolocation implementation is quite good and got few steps to observe but only on thing is missing, i guess.
Im not able to see if the user accepted the request or not ( before i get the position object ), i dunno if the user just ignores my request ( during my timeout ) or if the request just get lost ( and the failure callback doesnt get called for no reason ).
It would be useful to set a timestamp when the user accepts the request, i couldnt find anything which gives me that kind of response.
Based on my new understanding of what you are after, you want something like this.
(Tested: in Opera - works, Firefox 3.6 & Chrome 8 - not so much (I need more time to debug))
Scenario:
Page attempts to get location... but user ignores the prompt completely thus there is no (accept or deny) and since the request for the location is never sent, there is no timeout either!
Based on this you may want to add your own logic to handle this scenario. For the sake of this example, I'm going to prototype my own "wrapper" method. (for the picky - I'm not condoning using globals etc. I was just trying to get something to work)
navigator.geolocation.requestCurrentPosition = function(successCB, errorCB, timeoutCB, timeoutThreshold, options){
var successHandler = successCB;
var errorHandler = errorCB;
window.geolocationTimeoutHandler = function(){
timeoutCB();
}
if(typeof(geolocationRequestTimeoutHandler) != 'undefined'){
clearTimeout(window['geolocationRequestTimeoutHandler']);//clear any previous timers
}
var timeout = timeoutThreshold || 30000;//30 seconds
window['geolocationRequestTimeoutHandler'] = setTimeout('geolocationTimeoutHandler()', timeout);//set timeout handler
navigator.geolocation.getCurrentPosition(
function(position){
clearTimeout(window['geolocationRequestTimeoutHandler']);
successHandler(position);
},
function(error){
clearTimeout(window['geolocationRequestTimeoutHandler']);
errorHandler(error);
},
options
);
};
function timeoutCallback(){
alert('Hi there! we are trying to locate you but you have not answered the security question yet.\n\nPlease choose "Share My Location" to enable us to find you.');
}
function successCallback(position){
var msg = '';
msg += 'Success! you are at: ';
msg += '\nLatitude: ' + position.coords.latitude;
msg += '\nLongitude: ' + position.coords.longitude;
msg += '\nAltitude: ' + position.coords.altitude;
msg += '\nAccuracy: ' + position.coords.accuracy;
msg += '\nHeading: ' + position.coords.heading;
msg += '\nSpeed: ' + position.coords.speed;
alert(msg);
}
function errorCallback(error){
if(error.PERMISSION_DENIED){
alert("User denied access!");
} else if(error.POSITION_UNAVAILABLE){
alert("You must be hiding in Area 51!");
} else if(error.TIMEOUT){
alert("hmmm we timed out trying to find where you are hiding!");
}
}
navigator.geolocation.requestCurrentPosition(successCallback, errorCallback, timeoutCallback, 7000, {maximumAge:10000, timeout:0});
The concept is to set up a timer first (defaults to 30 seconds if not set). If the user doesn't do anything before the timer expires, a timeoutCallback is called.
Notes:
Some UI's (e.g. iPhone/iPad/iPod Safari) may make the Allow/Deny prompt modal - thus the user can't really continue until they pick something (I'd suggest to leave these users alone and let the default UI handle things
If the user Allows the request (late), the timeout may still fire before the response comes back - I don't think there is anything you can do about this
Code above is an example only... it needs cleaning up.
It is part of the Geolocation API:
// navigator.geolocation.getCurrentPosition(successCallback, errorCallback, options);
navigator.geolocation.getCurrentPosition(
function(position){
//do something with position;
}, function(){
//handle condition where position is not available
//more specifically you can check the error code...
//error.code == 1
if(error.PERMISSION_DENIED){
alert("you denied me! ");
}
});
If you specify the errorCallback... then you can track if the user has declined to provide access.
Possible error codes include:
error.PERMISSION_DENIED (numeric value 1)
error.POSITION_UNAVAILABLE (numeric value 2)
error.TIMEOUT (numeric value 3)
Tested it successful in FF 3.5, Opera 10.6, Chrome8, IE6-8..
var succeed = function(obj) {
navigator.geolocation.received = true;
!navigator.geolocation.timedout?alert('GOT YAH'):alert('GOT YAH but user was to slow');
};
var failed = function(obj) {
navigator.geolocation.received = true;
!navigator.geolocation.timedout?alert('just failed'):alert('failed and user was to slow as well, tzz ._.');
};
var timedout = function() {
navigator.geolocation.timedout = true; // could be used for other callbacks to trace if its timed out or not
!navigator.geolocation.received?alert('Request timed out'):null;
}
// Extend geolocation object
if ( navigator.geolocation ) {
navigator.geolocation.retrievePermission = function retrievePermission(succeed,failed,options,timeout) {
this.received = false; // reference for timeout callback
this.timedout = false; // reference for other callbacks
this.getCurrentPosition.apply(this,arguments); // actual request
// Trigger timeout with its function; default timeout offset 5000ms
if ( timeout ) {
setTimeout(timeout.callback,timeout.offset || 5000);
}
}
// New location request with timeout callback
navigator.geolocation.retrievePermission(succeed,failed,{},{
offset: 10000, // miliseconds
callback: timedout
});
// Awesome thingy is not implemented
} else {
alert('geolocation is not supported');
}
With that workaround we know if the request timedout, even when the succeess / failure callback get called afterwards.