Created
April 26, 2013 12:50
-
-
Save dbro/5467157 to your computer and use it in GitHub Desktop.
This is a Google Apps Script (https://script.google.com) that replicates the "Snippets" messaging process as used by Google internally. See the notes at the bottom of this page for more info.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* ************************************** | |
Weekly Update Scripts | |
by Dan Brown, March 2013 | |
For automatic collection of weekly | |
update messages from employees. | |
* sends reminder messages | |
* posts to public sites pages | |
* alerts managers, coworkers and | |
other stakeholders | |
Known Issues: | |
* html tags should be stripped from subscription messages (ready to test) | |
* lines beginning with ">" are not being dropped as expected from both announcements | |
and subscription messages (might be fixed by previous issue's fix) | |
* fix link creation in sites list page | |
Suggestions for improvements: | |
* include most recent update in reminder (ready to test) | |
* link to instructions and example | |
* avoid sending reminder to people who | |
already sent an update this round | |
************************************** */ | |
var domainName = 'your-domain-here.com'; | |
var siteName = 'your-site-name-here'; | |
var site = SitesApp.getSite(domainName, siteName); | |
var debug = 2; // set to zero to disable logging | |
var dryRun = 1; // set to 1 to disable writing/sending. for testing | |
function getTextFromHtml(html) { | |
// getTextFromHtml("hello <div>foo</div>& world <br /><div>bar</div>!"); | |
// will return | |
// "hello foo& world bar!" | |
// from http://stackoverflow.com/questions/13288928/trouble-with-html-encoding-in-google-apps-script | |
// TODO: check to see if <br> tags and others like </li> get converted to newlines | |
// \n: br p li tr, others? (divs are difficult) | |
// space: td | |
return getTextFromNode(Xml.parse(html, true).getElement()); | |
} | |
function getTextFromNode(x) { | |
switch(x.toString()) { | |
case 'XmlText': return x.toXmlString() + '\n'; | |
case 'XmlElement': return x.getNodes().map(getTextFromNode).join(''); | |
default: return ''; | |
} | |
} | |
function cleanString(input) { | |
if (typeof(input) === "undefined") { return ""; } | |
if (input === null) { return ""; } | |
return input.trim().toLowerCase(); | |
} | |
function parseEmailAddress(input) { | |
// we expect it to return a simple [a-z0-9\.]*@[a-z0-9\.]* text string. | |
// returns an empty string in case of invalid input | |
if (typeof(input) === "undefined") { return ""; } | |
if (input === null) { return ""; } | |
var email = cleanString(input); | |
// remove stuff outside angle braces that gmail adds to addresses | |
var iOpenAngle = email.lastIndexOf("<"); | |
var iCloseAngle = -1; // default | |
if (iOpenAngle != -1) { | |
iCloseAngle = email.indexOf(">", iOpenAngle); | |
if (iCloseAngle != -1) { | |
var trimmedEmail = email.substr(iOpenAngle + 1, iCloseAngle - iOpenAngle -1); | |
if (debug > 2) { | |
Logger.log("Trimming email address: " + email + " --becomes--> " + trimmedEmail); | |
} | |
email = trimmedEmail; | |
} | |
} | |
iAt = email.indexOf("@"); | |
iDot = email.indexOf(".", iAt); | |
if ((email.length > 0) && (iAt != -1) && (iAt > 0) && (iDot != -1) && (iAt < iDot) && (iDot < (email.length - 1))) { | |
return email; | |
} else { | |
return ""; | |
}; | |
} | |
function removeOldThreadContent(input) { | |
// attempts to remove lines from a message body that come from older messages in a thread | |
// these start with ">" or have the pattern "On ... wrote:" | |
var output = []; | |
var inputArray = input.split("\n"); | |
while (inputArray.length) { | |
var line = inputArray.shift(); | |
var linelength = line.length; | |
if ((line.substr(0,1) == ">") || (line.substr(0,2) == " >") || (line.substr(0,4) == ">") || (line.substr(0,5) == " >")) { | |
// don't include this line | |
} else if ((line.substr(0,3) == "On ") && ((line.substr(linelength - 6,6) == "wrote:") || (line.substr(linelength - 12,6) == "wrote:"))) { | |
// don't include this line | |
} else { | |
output.push(line); | |
} | |
} | |
return output.join("\n"); | |
} | |
var INVALID_DATE = new Date(0); // Jan 1, 1970 | |
var TODAY = new Date(); | |
var THREEDAYSAGO = new Date(TODAY.valueOf() - 259200000); | |
var TENYEARSAGO = new Date(TODAY.valueOf() - 315569259747); | |
function parseDateString(input) { | |
// converts from ISO format (eg. "2013-01-20T23:45:56.000Z") | |
//if (isNaN(input) || (input == "")) { return INVALID_DATE; } | |
if (input == "") { return INVALID_DATE; } | |
try { | |
var date = new Date(input); | |
} catch(err) { | |
return INVALID_DATE; | |
} | |
if (date.valueOf() < TENYEARSAGO.valueOf()) { | |
return INVALID_DATE; | |
} | |
return date; | |
} | |
function formatDateString(input) { | |
// converts to ISO format (eg. "2013-01-20T23:45:56.000Z") | |
return input.toISOString(); | |
} | |
function maxDate(x, y) { | |
if ((x == INVALID_DATE) && (y == INVALID_DATE)) { return x; } | |
if (x == INVALID_DATE) { return y; } | |
if (y == INVALID_DATE) { return x; } | |
if (x > y) { return x; } | |
return y; | |
} | |
function getTextBetween(fullstring, start_marker, end_marker) { | |
var startIndex = fullstring.indexOf(start_marker) + start_marker.length; | |
if (startIndex == -1) { return ""; } | |
var endIndex = fullstring.indexOf(end_marker, startIndex); | |
if (endIndex == -1) { return ""; } | |
return fullstring.substring(startIndex,endIndex); | |
} | |
function createLink(ref, anchor) { | |
// URLs are stored as strings in Google Sites, | |
// eg. <a xmlns="http://www.w3.org/1999/xhtml" href="/a/your-domain-here.com/your-site-here/who/someone">here</a> | |
var trimThis = "https://sites.google.com"; | |
if (ref.slice(0,trimThis.length) === trimThis) { | |
ref = ref.slice(trimThis.length); | |
} | |
// return '<a xmlns="http://www.w3.org/1999/xhtml" href="' + ref + '">' + anchor + '</a>'; | |
return '<a href="' + ref + '">' + anchor + '</a>'; | |
} | |
function parseUrl(inputstring) { | |
// URLs are stored as strings in Google Sites, | |
// eg. <a xmlns="http://www.w3.org/1999/xhtml" href="/a/your-domain-here.com/your-site-here/who/someone">here</a> | |
var refStart = 'href="'; | |
var refEnd = '"'; | |
var anchorStart = '>'; | |
var anchorEnd = '<'; | |
return [getTextBetween(inputstring, refStart, refEnd), getTextBetween(inputstring, anchorStart, anchorEnd)]; | |
} | |
function getUrlRef(inputstring) { | |
return parseUrl(inputstring)[0]; | |
} | |
function getUrlAnchor(inputstring) { | |
return parseUrl(inputstring)[1]; | |
} | |
var DIRECTORY_PAGE_NAME = "who"; | |
var UPDATES_PAGE_NAME = "updates"; | |
var PAR_FIRSTNAME = "First name"; | |
var PAR_LASTNAME = "Last name"; | |
var PAR_EMAIL = "email"; | |
var PAR_DEPARTMENT = "Department"; | |
var PAR_EMAIL = "email"; | |
var PAR_OBJECTIVES = "Objectives"; | |
var PAR_UPDATES = "Updates"; | |
function getParticipants() { | |
if (debug > 0) { Logger.log("starting getParticipants() function"); } | |
var directoryPage = site.getChildByName(DIRECTORY_PAGE_NAME); | |
var participants = directoryPage.getListItems(); | |
var participantSeenFirstAtRow = {}; // to ignore duplicates | |
var participantsArray = []; | |
var participantCount = participants.length; | |
for (var i=0; i < participantCount; i++) { // don't need to skip header row, because it won't be a valid email address | |
var p = participants[i]; | |
var rawEmail = p.getValueByName(PAR_EMAIL); | |
var recipientEmail = parseEmailAddress(rawEmail); | |
// test to see if the row should be skipped | |
if (recipientEmail.length === 0) { | |
if (debug > 0) { | |
Logger.log("Skipping invalid email address " + rawEmail + " found at row " + i); | |
} | |
continue; | |
} | |
if (participantSeenFirstAtRow.hasOwnProperty(recipientEmail)) { | |
if (debug > 0) { | |
Logger.log("Skipping duplicate email address " + recipientEmail + " found at row " + i + ", first seen at row " + participantSeenFirstAtRow[recipientEmail]); | |
} | |
continue; | |
} | |
// clean original email if necessary | |
if (rawEmail != recipientEmail) { | |
p.setValueByName(PAR_EMAIL, recipientEmail); | |
if (debug > 0) { | |
Logger.log("Replaced original email value (" + rawEmail + ") at row " + i + " with cleaned version (" + recipientEmail + ")"); | |
} | |
} | |
if (p.getValueByName(PAR_DEPARTMENT) != "Product Management") { | |
continue; // skip this person who is not in PM dept. for testing phase | |
} | |
// new participant | |
participantSeenFirstAtRow[recipientEmail] = i; | |
var username = recipientEmail.split("@")[0]; | |
var pageURLObjectives = cleanString(p.getValueByName(PAR_OBJECTIVES)); // may be blank | |
if (pageURLObjectives.length === 0) { | |
try { | |
var pageObjectives = directoryPage.getChildByName(username); | |
} catch (exception) { | |
var pageObjectives = site.createWebPage(p.getValueByName(PAR_FIRSTNAME) + " " + p.getValueByName(PAR_LASTNAME), username, ""); | |
} | |
// URLs are strings, eg. "<a xmlns="http://www.w3.org/1999/xhtml" href="/a/getyourguide.com/poca/who/villars">here</a>" | |
//p.setValueByName(PAR_OBJECTIVES, pageObjectives.getUrl()); | |
// p.setValueByName(PAR_OBJECTIVES, createLink(pageObjectives.getUrl(), "here")); // TODO: re-enable once URLs are working (bug?) | |
// p.setValueByName(PAR_OBJECTIVES, '<a href="http://who">whourl</a> '); | |
} else { | |
var pageObjectives = directoryPage.getChildByName(username); | |
} | |
var pageURLToAppend = cleanString(p.getValueByName(PAR_UPDATES)); // may be blank | |
if (pageURLToAppend.length === 0) { | |
try { | |
var pageToAppend = pageObjectives.getChildByName(UPDATES_PAGE_NAME); | |
} catch (exception) { | |
var pageToAppend = pageObjectives.createAnnouncementsPage("Weekly updates from " + username, UPDATES_PAGE_NAME, ""); | |
} | |
//p.setValueByName(PAR_UPDATES, pageToAppend.getUrl()); | |
// p.setValueByName(PAR_UPDATES, createLink(pageToAppend.getUrl(), "here")); // TODO: re-enable once URLs are working (bug?) | |
} else { | |
var pageToAppend = pageObjectives.getChildByName(UPDATES_PAGE_NAME); | |
} | |
participantsArray.push(p); | |
} | |
return participantsArray; | |
} | |
function getSenderEmail() { | |
// we attempt to send from the official email alias for the service | |
// that we created and associated with Dan Brown's gmail account (brown@) | |
// if that fails, return an empty string | |
var senderEmail = "[email protected]"; | |
if (GmailApp.getAliases().indexOf(senderEmail) != -1) { | |
return senderEmail; | |
} | |
return ""; | |
} | |
function getPreviousUpdateContent(updatesURL) { | |
var previousUpdateContent = ""; // default is an empty string | |
try { | |
var pageUpdates = SitesApp.getPageByUrl(updatesURL); | |
var previousUpdates = pageUpdates.getAnnouncements(); | |
previousUpdateContent = getTextFromHtml(previousUpdates[previousUpdates.length - 1].getHtmlContent()); | |
previousUpdateContent = "\n> Your most recent update:\n> " + previousUpdateContent.replace(/\n/g,"\n> "); | |
} catch (exception) { | |
// no operation | |
if (debug > 0) { Logger.log("no previous update found at page " + updatesURL); } | |
} | |
return previousUpdateContent; | |
} | |
function emailReminderMessages() { | |
if (debug > 0) { Logger.log("starting emailReminderMessages() function"); } | |
// assemble the contents of the message to be sent | |
var subject = "Weekly Update reminder"; | |
var salutation = "> Hello "; | |
var body = "\n> Please respond with your weekly update before 15:00 CET today."; | |
body += "\n> "; | |
body += "\n> Include 3-5 bullet points describing your accomplishments for the past week"; | |
body += "\n> and 3-5 more describing your plan for next week."; | |
body += "\n> It should take less than 5 minutes to write."; | |
body += "\n> "; | |
body += "\n> Your response will be publicly visible as part of your profile page on our intranet."; | |
body += "\n> "; | |
var closing = "\n> Lines beginning with \">\" will be ignored.\n"; | |
// TODO: add reference to instructions and examples | |
var extraParams = { name: "Weekly Update" }; | |
// we prefer to send from the official email alias for the service | |
var senderEmail = getSenderEmail(); | |
if (senderEmail.length > 0) { | |
extraParams.from = senderEmail; | |
extraParams.replyTo = senderEmail; // TODO: get this to work properly! Or maybe it's because brown@ is also wupdate@, and others will be OK? | |
} | |
// send a message to each participant that wants to receive a reminder | |
var participants = getParticipants(); | |
while (participants.length) { | |
p = participants.shift(); | |
var email = p.getValueByName(PAR_EMAIL); | |
var firstname = p.getValueByName(PAR_FIRSTNAME); | |
var updatesURL = "https://sites.google.com" + getUrlRef(p.getValueByName(PAR_UPDATES)); | |
var previousUpdateContent = getPreviousUpdateContent(updatesURL); | |
// TODO: check to see if the person has already sent an update recently, and don't send a reminder if so | |
// if (p[LAST] > THREEDAYSAGO) { | |
// // TODO: do a Gmail search instead of this P[LAST] check! | |
// if (debug > 0) { Logger.log("INFO: no message sent to " + email + " (recent update received on " + p[LAST].toISOString() + ")"); } | |
// } else { | |
if (dryRun === 0) { | |
GmailApp.sendEmail(email, subject, salutation + firstname + body + updatesURL + closing + previousUpdateContent, extraParams); | |
} | |
if (debug > 0) { Logger.log("sent message to " + email); } | |
if (debug > 1) { Logger.log("content :\n" + salutation + firstname + body + updatesURL + closing + previousUpdateContent); } | |
// } | |
} | |
} | |
// we use escape key names to avoid collisions with existing properties of objects, when used as a dictionary | |
function escape(input) { | |
return "_" + input; | |
} | |
function unescape(input) { | |
return input.substr(1, input.length-1); | |
} | |
function isEscaped(input) { | |
return (input.charAt(0) === "_"); | |
} | |
function addDefaultDomain(rawstring, domain) { | |
// add the defaultDomain if the raw Email doesn't end in it | |
var atInd = rawstring.indexOf('@'); | |
if (atInd === -1) { | |
return rawstring + '@' + domain; | |
} else { | |
return rawstring; | |
} | |
} | |
var SUBSCRIPTIONS_PAGE_NAME = "--subscriptions"; | |
// schema of "Subscriptions" array | |
var SUB_ROW = 0; | |
var SUB_EMAIL = 1; | |
var SUB_TERM = 2; | |
var SUB_EMAILFIELD = "Subscriber email (@your-domain-name.com)"; | |
var SUB_TERMFIELD = "receive updates that contain this term"; | |
function getSubscriptions() { | |
// Create list of subscriptions to receive copies of relevant update messages | |
// each subscription is a list where the first element is the search term, | |
// and the second element is the address of a single subscriber | |
if (debug > 0) { Logger.log("starting getSubscriptions() function"); } | |
var directoryPage = site.getChildByName(DIRECTORY_PAGE_NAME); | |
var subscriptionsPage = directoryPage.getChildByName(SUBSCRIPTIONS_PAGE_NAME); | |
var subscriptions = subscriptionsPage.getListItems(); | |
var subscriptionsArray = []; | |
var alreadySeenSubscriptions = {}; | |
var i=-1; | |
while (subscriptions.length) { | |
i++; | |
subscription = subscriptions.shift(); | |
var rawEmail = addDefaultDomain(subscription.getValueByName(SUB_EMAILFIELD), "your-domain-name.com"); | |
var recipientEmail = parseEmailAddress(rawEmail); | |
// test to see if the row should be skipped | |
if (recipientEmail.length === 0) { | |
if (debug > 0) { | |
Logger.log("Skipping invalid email address " + rawEmail + " found at row " + i); | |
} | |
continue; | |
} | |
// clean original email if necessary | |
if (rawEmail != recipientEmail) { | |
subscription.setValueByName(SUB_EMAILFIELD, recipientEmail); | |
if (debug > 0) { | |
Logger.log("Replaced original email value (" + rawEmail + ") at row " + i + " with cleaned version (" + recipientEmail + ")"); | |
} | |
} | |
var searchTerm = subscription.getValueByName(SUB_TERMFIELD); | |
var subFingerprint = recipientEmail + "__@@__@@__" + searchTerm; | |
if (alreadySeenSubscriptions.hasOwnProperty(subFingerprint)) { | |
if (debug > 0) { | |
Logger.log("Skipping duplicate subscription on row " + i + " (" + recipientEmail + " : " + searchTerm + ")"); | |
} | |
} else { | |
subscriptionsArray.push(subscription); | |
alreadySeenSubscriptions[subFingerprint] = true; | |
} | |
} | |
return subscriptionsArray; | |
} | |
function groupSubscriptionsBySearchTerm(input) { | |
// input should be an array of list items, with no duplicate subscription information | |
// returns an array of arrays: | |
// each subscription is a list where the first element is the search term, | |
// and the second element is the address of a single subscriber | |
// and all following elements are email addresses of subscribers | |
var subscriptions = {}; | |
while (input.length) { | |
s = input.shift(); | |
var recipientEmail = escape(addDefaultDomain(s.getValueByName(SUB_EMAILFIELD), "getyourguide.com")); | |
var escapedSearchTerm = escape(s.getValueByName(SUB_TERMFIELD)); | |
if (!(subscriptions.hasOwnProperty(escapedSearchTerm))) { | |
subscriptions[escapedSearchTerm] = []; | |
} | |
subscriptions[escapedSearchTerm].push(recipientEmail); | |
} | |
// filter and flatten object data structure into an array of arrays | |
var unescapedSubscriptions = []; | |
for (escapedSearchTerm in subscriptions) { | |
if (isEscaped(escapedSearchTerm)) { | |
var pushThis = subscriptions[escapedSearchTerm]; | |
pushThis.unshift(unescape(escapedSearchTerm)); | |
unescapedSubscriptions.push(pushThis); | |
} | |
} | |
return unescapedSubscriptions; | |
} | |
function checkForResponseMessages() { | |
if (debug > 0) { Logger.log("starting checkForResponseMessages() function"); } | |
// remember start time and exclude messages received after that time | |
var functionStartTime = new Date(); | |
// Look for the time of the last run in the subscriptions page description. It is expected | |
// to be in a specific text string. | |
var functionLastRunTime = parseDateString(""); // default to invalid date | |
var directoryPage = site.getChildByName(DIRECTORY_PAGE_NAME); | |
var subscriptionsPage = directoryPage.getChildByName(SUBSCRIPTIONS_PAGE_NAME); | |
var subscriptionPageText = subscriptionsPage.getHtmlContent(); | |
var last_updated_marker_start = "[Last publish to subscribers was on "; | |
var last_updated_marker_end = "]"; | |
var last_updated_start_index = subscriptionPageText.indexOf(last_updated_marker_start); | |
var last_updated_end_index = -1; //default | |
if (last_updated_start_index != -1) { | |
last_updated_start_index += last_updated_marker_start.length; | |
last_updated_end_index = subscriptionPageText.indexOf(last_updated_marker_end, last_updated_start_index); | |
if (last_updated_end_index != -1) { | |
var functionLastRunTimeString = subscriptionPageText.substring(last_updated_start_index, last_updated_end_index); | |
var functionLastRunTime = parseDateString(functionLastRunTimeString); | |
} | |
} | |
var directoryPage = site.getChildByName(DIRECTORY_PAGE_NAME); | |
// Create set of subscriptions to receive copies of relevant update messages | |
var groupedSubscriptions = groupSubscriptionsBySearchTerm(getSubscriptions()); | |
// Get instructions for where to record information for each participant | |
var participants = getParticipants(); | |
// assemble the contents of the message to be sent | |
var subject = "Weekly Update subscription"; | |
var extraParams = { name: "Weekly Update" }; | |
// we prefer to send from the official email alias for the service | |
var senderEmail = getSenderEmail(); | |
if (senderEmail.length > 0) { | |
extraParams.from = senderEmail; | |
extraParams.replyTo = senderEmail; // TODO: get this to work properly! | |
} | |
var messagesFor = {}; // store the messages relevant to each subscriber | |
// data structure schema: | |
// messagesFor[escapedSubscriberEmail][escapedMessageID] = [array of searchTerms] | |
while (participants.length) { | |
p = participants.shift(); | |
// Look for new messages from this participant | |
var query = "from:" + p.getValueByName(PAR_EMAIL); | |
if (senderEmail.length > 0) { | |
query += " to:" + senderEmail; | |
} | |
//var lastHeardFrom = parseDateString(""); // Date object (aka epoch milliseconds) // TODO: implement last-heard-from cutoff, maybe using the list page's created/updated times? | |
var lastHeardFrom = functionLastRunTime; | |
if (lastHeardFrom != INVALID_DATE) { | |
var dateString = lastHeardFrom.toISOString().split("T")[0]; | |
query += " after:" + dateString; | |
} | |
var latestDate = INVALID_DATE; | |
if (debug > 0) { Logger.log("\n*** searching for messages with query: " + query); } | |
var threads = GmailApp.search(query); | |
if (debug > 1) { Logger.log("found " + threads.length + " threads to examine"); } | |
while (threads.length) { | |
t = threads.shift(); | |
messages = t.getMessages(); | |
if (debug > 1) { Logger.log(" found " + messages.length + " in this thread"); } | |
while (messages.length) { | |
m = messages.shift(); | |
var mdate = m.getDate(); | |
var mfrom = parseEmailAddress(m.getFrom()); | |
// check for skip conditions | |
if (m.isInTrash()) { | |
if (debug > 1) { Logger.log(" skipping trashed message"); } | |
continue; | |
} | |
if (mfrom != p.getValueByName(PAR_EMAIL)) { | |
if (debug > 1) { Logger.log(" skipping message from " + mfrom + " because it doesnt match " + p.getValueByName(PAR_EMAIL)); } | |
continue; | |
} | |
if ((lastHeardFrom != INVALID_DATE) && (mdate < lastHeardFrom)) { | |
if (debug > 1) { Logger.log(" skipping message sent on " + mdate.toISOString() + " which is earlier than " + lastHeardFrom.toISOString()); } | |
continue; | |
} | |
if (mdate > functionStartTime) { | |
if (debug > 1) { Logger.log(" skipping message sent on " + mdate.toISOString() + " which is after this script started at " + functionStartTime.toISOString()); } | |
continue; | |
} | |
var newContent = removeOldThreadContent(getTextFromHtml(m.getBody())); // TODO: check to see if newlines are handled appropriately when input formatted with html tags | |
if (newContent.length === 0) { | |
if (debug > 1) { Logger.log(" skipping message sent on " + mdate.toISOString() + " which has no new content"); } | |
continue; | |
} | |
// this message passed all the tests, so we accept it | |
latestDate = maxDate(latestDate, mdate); | |
if (debug > 0) { Logger.log(" found new message from " + mfrom + " sent on " + mdate.toISOString()); } | |
if (debug > 2) { Logger.log(" latestDate is now " + latestDate.toISOString()); } | |
// append this content to the person's google site page // TODO: make sure this page exists, and create it if necessary | |
var username = mfrom.split("@")[0]; | |
var appendToPage = directoryPage.getChildByName(username).getChildByName(UPDATES_PAGE_NAME); | |
if (debug > 0) { | |
Logger.log("Creating new announcement on " + appendToPage.getUrl()); | |
} | |
if (dryRun === 0) { | |
appendToPage.createAnnouncement(mdate.toISOString(), newContent); | |
} | |
// check to see if any subscribers should be notified of this message | |
for (var i=0; i<groupedSubscriptions.length; i++) { | |
var s = groupedSubscriptions[i]; | |
var searchTerm = s[0]; | |
var searchRegEx = /\bsearchTerm\b/i; // to require word boundaries at start and end of search term | |
// if ((mfrom == searchTerm) || (newContent.indexOf(searchTerm) != -1)) { | |
if ((mfrom == searchTerm) || (searchRegEx.test(newContent))) { | |
for (var j=1; j<s.length; j++) { // start at second term in array | |
var escapedSubscriber = s[j]; // it's already escaped | |
if (!(messagesFor.hasOwnProperty(escapedSubscriber))) { | |
messagesFor[escapedSubscriber] = {}; | |
} | |
var messagesForSubscriber = messagesFor[escapedSubscriber]; | |
var escapedMessageId = escape(m.getId()); | |
if (!(messagesForSubscriber.hasOwnProperty(escapedMessageId))) { | |
messagesForSubscriber[escapedMessageId] = []; | |
} | |
messagesForSubscriber[escapedMessageId].push(searchTerm); | |
} | |
} | |
} | |
} | |
} | |
// maybe todo: store the time stamp of the most recent message received from this participant | |
} | |
// send messages to subscribers | |
// batch all notifications into as few messages as possible (limit 20kB) | |
for (var escapedSubscriber in messagesFor) { | |
if (isEscaped(escapedSubscriber)) { | |
var subscriber = unescape(escapedSubscriber); | |
if (debug > 1) { Logger.log("preparing notification messages for " + subscriber); } | |
var content = ""; | |
for (var escapedMessageId in messagesFor[escapedSubscriber]) { | |
if (isEscaped(escapedMessageId)) { | |
var messageId = unescape(escapedMessageId); | |
var message = GmailApp.getMessageById(messageId); | |
var searchTerms = messagesFor[escapedSubscriber][escapedMessageId]; | |
content += "\n----- found search term(s): " + searchTerms.join(" , "); | |
content += "\n----- in message from " + message.getFrom() + " sent " + message.getDate().toISOString(); | |
content += "\n" + removeOldThreadContent(getTextFromHtml(message.getBody())); // TODO: check newlines from html handled properly | |
content += "\n----------------------------\n"; | |
if (debug > 1) { Logger.log("found message (ID = " + messageId + ") regarding search term(s) " + searchTerms.join(" , ")); } | |
if (content.length > 15000) { // send this now if the message is approaching the 20kb limit | |
if (debug > 0) { Logger.log("sending message to " + subscriber + " triggered by large message accumulator size"); } | |
if (dryRun === 0) { | |
GmailApp.sendEmail(subscriber, subject, content, extraParams); | |
} | |
content = ""; | |
} | |
} | |
} | |
if (content.length !== 0) { | |
if (debug > 0) { Logger.log("sending message to " + subscriber); } | |
if (dryRun === 0) { | |
GmailApp.sendEmail(subscriber, subject, content, extraParams); | |
} | |
} | |
} | |
} | |
if (functionStartTime != INVALID_DATE) { | |
// Store the time of this run in the subscriptions page description for future reference. | |
if ((last_updated_start_index === -1) || (last_updated_end_index === -1)) { | |
var newSubscriptionPageText = subscriptionPageText + "\n" + last_updated_marker_start + | |
formatDateString(functionStartTime) + last_updated_marker_end; | |
} else { | |
var newSubscriptionPageText = subscriptionPageText.substring(0,last_updated_start_index) + | |
formatDateString(functionStartTime) + subscriptionPageText.substring(last_updated_end_index); | |
} | |
if (debug > 0) { Logger.log("updating last run time on subscriptions page to " + formatDateString(functionStartTime)); } | |
if (dryRun === 0) { | |
subscriptionsPage.setHtmlContent(newSubscriptionPageText); | |
} | |
} | |
} | |
function onOpen() { | |
// create some convenient manual trigger menu options | |
var subMenus = [ | |
{name:"Send Reminder Email Messages Now", functionName: "emailReminderMessages"}, | |
{name:"Check For Response Messages Now", functionName: "checkForResponseMessages"} | |
]; | |
SpreadsheetApp.getActiveSpreadsheet().addMenu("Weekly Update Automated Functions", subMenus); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This script does 3 things:
Notes: The script interacts with Google Sites and Gmail. It expects a list page with a company directory to be stored at /who with certain fields present. It has some testing settings (dryrun=1 disables writing and email sending operations). It expects there to be an alias "wupdate@" used to send a received messages.