I need a google apps script which can do the following:
if I send an email to my alias email, say to myemail+expenses#gmail.com, it adds a line in a specific google spreadsheet with data from that email. E.g. timestamp, subject, the first line of a body - whatever.
I found interesting article describing similar process of sending data to google spreadsheet.
May be there is no any direct way of doing that, however, it should exist some workaround.
P.S. It should be done only by using a google echosystem. No php, servers etc.
There are two step for this.
#1
At first create a trigger to run the mail checker.
function switchTrigger() {
var isExist = false;
var triggers = ScriptApp.getProjectTriggers();
for (var i = 0; i < triggers.length; i++) {
if ( /*<CONDITION>*/ ) {
isExist = true;
ScriptApp.deleteTrigger(triggers[i]);
}
}
if (!isExist) {
ScriptApp.newTrigger( /*<CONDITION>*/ ).create();
Logger.log('create');
}
}
#2
Then you have to check emails. Something like that
function checkMail() {
var sh = SpreadsheetApp.openById(properties.targetSheetId).getSheets()[0];
var query = /* properties.queryString */ ;
var threads = GmailApp.search(query);
if (threads.length < 1) return;
for (var i = 0; i < threads.length; i++) {
var messages = threads[i].getMessages();
for (var j = 0; j < messages.length; j++) {
if (messages[j].isStarred()) {
sh.appendRow([
new Date(),
messages[j].getFrom(),
messages[j].getPlainBody()
]);
messages[j].unstar();
}
}
}
}
Be careful. You have to set up Gmail filters so that all incoming to myemail+expenses#gmail.com would be marked with an star at the beginning.
Working Example
Related
My emails usually has more than one Labels assigned. I like to search emails with a specific label then list them into the spreadsheet and show all other labels also assigned to the email.
Here's what i have so far, can't figure out how to get the other labels...
function myFunction() {
var ss = SpreadsheetApp.getActiveSheet();
var threads = GmailApp.search("label:Test");
for (var i=0; i<threads.length; i++)
{
var messages = threads[i].getMessages();
for (var j=0; j<messages.length; j++)
{
var sub = messages[j].getSubject();
var from = messages[j].getFrom();
var dat = messages[j].getDate();
ss.appendRow([dat, sub, from])
}
}
}
As far as Apps Script is concerned, Gmail labels are applied to threads and not to individual messages. (There are other contexts where this isn't necessarily true, as a Web Apps post details).
So, you should use the getLabels method of the Thread object. It then makes sense to structure the output so that each row corresponds to a thread, rather than a message. This is what I did below. The script takes subject/from/date from the first message in each thread. The 4th column is the comma-separated list of labels, except the one you search for.
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var search_label = 'Test';
var threads = GmailApp.search('label:' + search_label);
var output = [];
for (var i=0; i < threads.length; i++) {
var firstMessage = threads[i].getMessages()[0];
var sub = firstMessage.getSubject();
var from = firstMessage.getFrom();
var dat = firstMessage.getDate();
var labels = threads[i].getLabels();
var otherLabels = [];
for (var j = 0; j < labels.length; j++) {
var labelName = labels[j].getName();
if (labelName != search_label) {
otherLabels.push(labelName);
}
}
output.push([dat, sub, from, otherLabels.join(', ')]);
}
sheet.getRange(1, 1, output.length, output[0].length).setValues(output);
}
I prefer not to add one row at a time, instead gathering the double array output and inserting it all at once. Of course you can use appendRow as in your script. Then you wouldn't necessarily need a comma-separated list,
sheet.appendRow([dat, sub, from].concat(otherLabels));
would work.
Hi im trying to export the Emailadresses from the senders in a label in Gmail Called "Suarez". But it's just running and never completing, it should be about 372 emails. Is it to much to print to the logger?
Here is what im trying:
function getEmailsadresses(){
var threads = GmailApp.search("in:suarez");
for (var i = 0; i < threads.length; i++) {
var messages = threads[i].getMessages();
for (var m = 0; m < messages.length; m++) {
var msg = messages[m].getFrom();
}
}
Logger.log(msg()); // log from address of the message
}
I use a script embedded in an spreadsheet the reads every messages in every threads in a Label that could probably be a good starting point for you.
My specific use case was to log SMS messages that my android phone sends me as emails each time I send or receive a text message.
So it will work for you by just changing the label name and maybe remove the data filtering I added on the message. Since I have a lot of data I set up a time trigger task manager to works in small bunches to avoid exceeding time limit.
Usage is simple : run the "startSMSLog" and go have a couple of coffees until the cell A1 in the spreadsheet stops being RED... that's it.
Code below : the start function
function startSMSLog(){
var triggerID = ScriptApp.newTrigger('countSMSLogEmails').timeBased().everyMinutes(5).create().getUniqueId();
var thisTrigger = PropertiesService.getScriptProperties().setProperty('thisTriggerSMS',triggerID);
PropertiesService.getScriptProperties().setProperty('current thread SMS',0);
var sh = SpreadsheetApp.getActive().getSheetByName('SMS'); // change to the name of your sheet
sh.getRange('A1').setBackground('red');
countSMSLogEmails(true);
}
and the "working" code :
function countSMSLogEmails(clearSheet){
var sh = SpreadsheetApp.getActive().getSheetByName('SMS');
var start = Number(PropertiesService.getScriptProperties().getProperty('current thread SMS'));
var CallLogThreads = GmailApp.getUserLabelByName('SMS').getThreads(start,10);
if(CallLogThreads.length==0){
sh.sort(2,false);
sh.getRange('A1').setBackground('#FFC');
var triggers = ScriptApp.getProjectTriggers();
var thisTrigger = PropertiesService.getScriptProperties().getProperty('thisTriggerSMS');
for(var n in triggers){
if(triggers[n].getUniqueId()==thisTrigger){ScriptApp.deleteTrigger(triggers[n])};
}
sh.getRange('A1').setValue('Subject (Log on '+Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "dd-MMM-yy HH:mm")+')');
return;
} else {
PropertiesService.getScriptProperties().setProperty('current thread SMS',start+CallLogThreads.length);
}
var data = [];
for(var n=0;n<CallLogThreads.length;n++){
var thread = CallLogThreads[n];
Logger.log('thread message count = '+thread.getMessageCount());
var msg = thread.getMessages();
var msgData = [];
for(var m in msg){
var msgDate = msg[m].getDate();
var msgSubject = msg[m].getSubject();
var msgBody = msg[m].getBody().replace(/[\n\r]/g,' ').replace(/'/g,"'").replace(/ /g,"!").replace(/&/g,"&").replace(/"/g,'"')
.replace(/>/g,'>').replace(/</g,'<').replace(/<br \/>/g,'\n').replace(/<br>/g,'\n');
msgData.push([msgSubject,msgDate,msgBody]);
}
data.push(msgData);
}
var dataTotal = [];
if (clearSheet==true) {
dataTotal.push(['subject', 'date', 'message']);
}
for(var n in data){
dataTotal = dataTotal.concat(data[n]);
};
Logger.log(JSON.stringify(dataTotal));
if (clearSheet==true) {
sh.clearContents();
sh.getRange(1,1,dataTotal.length,dataTotal[0].length).setValues(dataTotal);
}else{
sh.getRange(sh.getLastRow()+1,1,dataTotal.length,dataTotal[0].length).setValues(dataTotal);
}
}
I'm using Firefox + searchbastard addon to do a multi-search on shopping search engines.The pages are part of a frame. This works just fine for all sites I tried so far except for shopmania.com.
If I use noscript to forbid scripts from the shopmania domain name then everything stays in place but the part of the website elements become nonresponsive. I know there is an option in Firefox to force links that open in a new window to open in a new tab. Is there something similar to prevent websites from popping out of frame? Maybe a Firefox addon that blocks these requests?
Or at least can someone please tell me what is causing only this website to act like this?
EDIT: What tool can i use to pause firefox OR javascript and stepthrough code like in c++ ? I tried a javascript debugger and firebug. They don't help but i'm probably not using them right..
EDIT2: I tried this greasemonkey script : https://userscripts.org/scripts/show/92424. It does not work so i guess it isn't because of 'target' attribute
This is wrong. I'm guessing you're using a plugin to capture and override the output some site gives you. I'm pretty sure this violates their ToS and it's not a very nice thing to do in general.
JavaScript is not designed to allow this kind of meddling. It's patchy at best.
If you want to use the data from a website, to aggregate or display in some manner, use their public API. If they don't have a public API they probably don't want you to use their service in such a manner.
The solution : I took the script from Stop execution of Javascript function (client side) or tweak it and modified it to search for the tag that has in it top.location = location and then appended a new script with the if (top != self) {top.location = location;} line commented . Being a js total newbie i don't know if it's the most elegant choice but it soves the prob. Special thanks to Tim Fountain.
I will leave this open just in case someone else will suggest a better solution, for my and others's education. Again thanks for the help.
Below is the code:
// ==UserScript==
// #name _Replace evil Javascript
// #run-at document-start
// ==/UserScript==
/****** New "init" function that we will use
instead of the old, bad "init" function.
*/
function init () {
/* //changing stuff around here
var newParagraph = document.createElement ('p');
newParagraph.textContent = "I was added by the new, good init() function!";
document.body.appendChild (newParagraph); */
<!--//--><![CDATA[//><!--
document.getElementsByTagName("html")[0].className+=" js "+(navigator.userAgent.toLowerCase().indexOf("webkit")>=0?"webkit":navigator.userAgent.toLowerCase().indexOf("opera")>=0?"opera":"");
for(i in css3_tags="abbr|header|footer".split("|")){document.createElement(css3_tags[i]);}
var PATH = "http://www.shopmania.com";
var PATH_STATIC = "http://im4.shopmania.org";
var PATH_SELF = "http://www.shopmania.com/";
var RETURN = "http%3A%2F%2Fwww.shopmania.com%2F";
var DOMAIN_BASE = "shopmania.com";
var SUBDOMAINS_FORCE_FILES_JS = "aff.remote,biz.remote,my.remote,cp.remote,cp.register_quick,cp.account_details,partner.remote,site.recommend,site.remote,site.feedback,site.report_problem,site.report,site.cropper";
var URL_REWRITE_MAPPING_JS = "cmd,section,do,option|feed,mode,option|forgot,section|info,page|login,section|logout,section|new_password,section,code|settings,section|shopping,param_main,param_sec|site,store_str_key|register,section|unsubscribe,section|agentie,store_str_key,id|brand,manuf_str_key|brands,letter|build,type,param_main,param_sec|compare,online|confirm,section|edit,section|deal,deal|dictionary,online|home,section|link_accounts,section|profile,user|reactivate,section|searches,letter|signup,section|rs_agent,store_str_key|rs_list,param_main,param_sec|rs_view,ad|agents,state|complex_list,param_main|complex_view,complex|list,cat|ad,a|map,option|my_ads,section|my_alerts,section";
var SVR_SITE_ID = "us";
var CONTEXT = "c5b27de70340c97a94092a43bd34b2b8";
var link_close = "Close";
var txt_loading = "Loading...";
var form_is_submitted = 0;
var search_is_focused = 0;
// Overlay object
var OL;
var DB;
var iframe_cnt = "";
// Facebook post to user's Wall action
var FACEBOOK_WALL_FEED_SIGNUP = "";
var SITENAME = "ShopMania";
//if (top != self) {top.location = location;} // SIT!
var comps = new Array(); comps['all'] = 0;var comps_cat_titles = new Array(); var views = new Array(); views['auto'] = 0; views['prod'] = 0; views['realestate'] = 0; views['classifieds'] = 0; views['all'] = 0; var search = new Array(); search['all'] = 0; search['prod'] = 0;
var favs = new Array(); favs['all'] = 0; favs['prod'] = 0; favs['store'] = 0; favs['manuf'] = 0; favs['other'] = 0; favs['realestate'] = 0; favs['auto'] = 0;
function addCss(c){var b=document.getElementsByTagName("head")[0];var a=document.createElement("style");a.setAttribute("type","text/css");if(a.styleSheet){a.styleSheet.cssText=c}else{a.appendChild(document.createTextNode(c))}b.appendChild(a)};
addCss(".lzl {visibility: hidden;}");
var RecaptchaOptions = { theme : 'clean' };
//--><!]]>
}
/*--- Check for bad scripts to intercept and specify any actions to take.
*/
checkForBadJavascripts ( [
[false, /top.location = location/, function () {addJS_Node (init);} ]
] );
function checkForBadJavascripts (controlArray) {
/*--- Note that this is a self-initializing function. The controlArray
parameter is only active for the FIRST call. After that, it is an
event listener.
The control array row is defines like so:
[bSearchSrcAttr, identifyingRegex, callbackFunction]
Where:
bSearchSrcAttr True to search the SRC attribute of a script tag
false to search the TEXT content of a script tag.
identifyingRegex A valid regular expression that should be unique
to that particular script tag.
callbackFunction An optional function to execute when the script is
found. Use null if not needed.
*/
if ( ! controlArray.length) return null;
checkForBadJavascripts = function (zEvent) {
for (var J = controlArray.length - 1; J >= 0; --J) {
var bSearchSrcAttr = controlArray[J][0];
var identifyingRegex = controlArray[J][1];
if (bSearchSrcAttr) {
if (identifyingRegex.test (zEvent.target.src) ) {
stopBadJavascript (J);
return false;
}
}
else {
if (identifyingRegex.test (zEvent.target.textContent) ) {
stopBadJavascript (J);
return false;
}
}
}
function stopBadJavascript (controlIndex) {
zEvent.stopPropagation ();
zEvent.preventDefault ();
var callbackFunction = controlArray[J][2];
if (typeof callbackFunction == "function")
callbackFunction ();
//--- Remove the node just to clear clutter from Firebug inspection.
zEvent.target.parentNode.removeChild (zEvent.target);
//--- Script is intercepted, remove it from the list.
controlArray.splice (J, 1);
if ( ! controlArray.length) {
//--- All done, remove the listener.
window.removeEventListener (
'beforescriptexecute', checkForBadJavascripts, true
);
}
}
}
/*--- Use the "beforescriptexecute" event to monitor scipts as they are loaded.
See https://developer.mozilla.org/en/DOM/element.onbeforescriptexecute
Note that it does not work on acripts that are dynamically created.
*/
window.addEventListener ('beforescriptexecute', checkForBadJavascripts, true);
return checkForBadJavascripts;
}
function addJS_Node (text, s_URL, funcToRun) {
var D = document;
var scriptNode = D.createElement ('script');
scriptNode.type = "text/javascript";
if (text) scriptNode.textContent = text;
if (s_URL) scriptNode.src = s_URL;
if (funcToRun) scriptNode.textContent = '(' + funcToRun.toString() + ')()';
var targ = D.getElementsByTagName ('head')[0] || D.body || D.documentElement;
//--- Don't error check here. if DOM not available, should throw error.
targ.appendChild (scriptNode);
}
there are some escaping issues with the cdata part in the code.So SO does not allow me to post the code.
EDIT: fixed
I need to generate a report on the list of messages and the sender of a letter, I can extract the theme of letters, but not understanding how to get the sender's address for each letter, the report turned out to be:
topic: hello From: frank#gmail.com
topic: your basket from: jerry#facebook.com
function myFunction() {
var emailAddress = Session.getActiveUser().getEmail();
var threads = GmailApp.getInboxThreads();
var output = ContentService.createTextOutput();
for (var i = 0; i < threads.length; i++) {
output.append(threads[i].getFirstMessageSubject()+" from:"+'\n');
}
GmailApp.sendEmail(emailAddress,"Mail Report", output.getContent()
);
}
UPDATE
Thank you for your answers, the solution was simple
function myFunction() {
var emailAddress = "example#email.com" ;
var threads = GmailApp.getInboxThreads();
var messages = threads[0].getMessages();
var senderEmail = messages[0].getFrom();
var output = ContentService.createTextOutput();
for (var i = 0; i < threads.length; i++) {
messages = threads[i].getMessages()
senderEmail = messages[0].getFrom();
output.append(i + ". " + threads[i].getFirstMessageSubject()+" from:"+ senderEmail + '\n');
}
GmailApp.sendEmail(emailAddress,"Mail Report", output.getContent()
);
}
Example result:
Email Verification - Stack Overflow from:Stack Overflow
Project Update #10: Double Fine Adventure by Double Fine and 2 Player Productions from:Kickstarter
If I understand what you are looking to do, then I believe you could get the messages from the thread, and then the sender from the message
var threads = GmailApp.getInboxThreads();
var messages = threads[0].getMessages();
var senderEmail = messages[0].getFrom();
var label = GmailApp.getUserLabelByName('inbox');
var threads = label.getThreads();
for(i in threads)
{
threads[i].getMessages()[0].getFrom()
}
You need to add
"https://www.googleapis.com/auth/userinfo.email"
in oauthScopes.(appscript.json file)
you can get more details in this link
enter link description here
For reasons of security, you cannot get the email using Session.getActiveUser().getEmail() if you are on a consumer Google account. It works only when the script is owned by a Google Apps user and the visitor belongs to the same domain.
However, you can try what is mentioned here
I've been trying to whip up a quick google script to count rsvps for the invite response spreadsheet for a wedding. The script worked perfectly for a week as new entries were added to the spreadsheet, then suddenly stopped working with the following error message in each cell:
Error: Service Times Out: Apps Script
The script itself is simple. It queries the relevant column (there are multiple events) and then checks to see whether there is some response spefied by the user - "YES", "NO", or a blank, typically.
What does this error mean, and does anyone have any suggestions for fixes?
function sumRSVP(response, rsvpType) {
var rsvpCol = 7;
if (rsvpType == "rehearsal") rsvpCol = 8;
if (rsvpType == "brunch") rsvpCol = 9;
var mySum = 0;
var sh = SpreadsheetApp.getActiveSheet();
for( i=2; i<177; i++){
var rsvp = sh.getRange(i, rsvpCol).getValue();
var nguests = sh.getRange(i, 6).getValue();
if(nguests != "" && rsvp == response){
mySum = mySum + parseFloat(nguests);
}
}
return mySum;
}
Hopefully the wedding went well. This was asked some time ago but has been viewed over 300 times at this post and I believe is important:
Data should not be extracted from a spreadsheet in a loop. The data needed should be extracted in a batch to an array and the array evaluated in the loop.
See docs reference at:
https://developers.google.com/apps-script/guide_common_tasks#OptimizeScripts
You can write scripts to take maximum advantage of the built-in caching, by minimizing the number of reads and writes. Alternating read and write commands is slow. To speed up a script, read all data into an array with one command, perform any operations on the data in the array, and write the data out with one command.
function sumRSVP(response, rsvpType) {
var rsvpCol = 7;
if (rsvpType == "rehearsal") rsvpCol = 8;
if (rsvpType == "brunch") rsvpCol = 9;
var mySum = 0;
var sh = SpreadsheetApp.getActiveSheet();
// start at row 2 - uses columns 6-9
var data = sh.getRange(2, 6, 177 - 1 , 4).getValues();
for(var i=0; i<data.length; i++){
var rsvp = data[i][rsvpCol - 6];
var nguests = data[i][0];
if(nguests != "" && rsvp == response){
mySum = mySum + parseFloat(nguests);
}
}
return mySum;
}