Created
July 6, 2020 17:51
-
-
Save joeybaker/98f4076bb2cfc9b1575841fb4718f577 to your computer and use it in GitHub Desktop.
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
// useful at script.google.com | |
// set these to discord webhooks. | |
const WEBHOOKS = { | |
SLAB: '', | |
ASANA: '', | |
FIGMA: '', | |
GREENHOUSE: '' | |
} | |
// these searches work great for me. The first does the search logic in | |
// this script the other three rely on doing the search logic in gmail. | |
// I've found doing th search logic in gmail is better. It's faster, and | |
// helps with debugging/gives a better log of what's happening. | |
const EMAIL_SEARCHES = { | |
SLAB: 'from:[email protected] is:unread', | |
ASANA: 'label:notifications-asana is:unread', | |
FIGMA: 'label:notifications-figma is:unread', | |
GREENHOUSE: 'label:notifications-greenhouse is:unread', | |
} | |
// it's okay to leave keys out here, we'll fall back to simplistic parsing | |
const PARSERS = { | |
SLAB: slabParse, | |
FIGMA: figmaParse, | |
GREENHOUSE: greenhouseParse | |
} | |
// discord limits us to 2000 char | |
const MAX_BODY_LENGTH = 2000 | |
// use tinyurl.com to compress urls which can often be quite long with email | |
// tracking redirect shinangiens. | |
const URL_REGEXP = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-_]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?)/g | |
const TINYURL_SUCESS_REGEXP = /<b>(.*)<\/b><div id="success">/ | |
function shortenUrls(body) { | |
const rawUrls = body.match(URL_REGEXP) | |
if (rawUrls == null) return body | |
return rawUrls.reduce(function (out, url) { | |
try { | |
const resp = UrlFetchApp.fetch('https://tinyurl.com/create.php?url=' + encodeURIComponent(url)) | |
// we get back a full webpage, look through from the "success" marker and grab the tinyurl | |
const urlResults = resp.getContentText().match(TINYURL_SUCESS_REGEXP) | |
// ensure we actually found a match in the HTML | |
if (urlResults.length > 1) { | |
return out.replace(url, urlResults[1]) | |
} | |
} | |
catch (e) { | |
return out | |
} | |
return out | |
}, body) | |
} | |
// | |
// parsers | |
// | |
// These functions take in an email subject and body and remove | |
// extra cruft like headers and footers. Don't worry about shortening | |
// urls, we'll do that automatically. | |
function basicParse({subject, body}) { | |
return '**' + subject + '**\n' + body.trim() | |
} | |
function slabParse({subject, body}) { | |
return body | |
.replace('[image: Slab Logo]', '') | |
.replace(subject, '**' + subject + '**') | |
} | |
function figmaParse({subject, body}) { | |
const removeRegExp = new RegExp('Figma.*\n(.*)\n------------------------------\n', 'm') | |
const removeFooterRegExp = new RegExp('Turn off comment notifications for this file.*\n(.*\n){1,10}', 'm') | |
return '**' + subject + '**\n' + body | |
.replace(removeRegExp, '') | |
.replace(removeFooterRegExp, '') | |
} | |
function greenhouseParse({subject, body}) { | |
const removeFooterRegExp = new RegExp('Thanks,\n(.*\n){1,10}', 'm') | |
const noFooter = body | |
.replace('© 2020 Greenhouse / 18 West 18th Street, 9th Floor, New York, NY 10011, USA', '') | |
.replace(removeFooterRegExp, '') | |
return basicParse({subject, body: noFooter}) | |
} | |
// Search gmail for the `serach` string, parse the results with a function and | |
// send them to a webhook. | |
function findAndPing({search, parse = basicParse, webhook}) { | |
// Search for any notification emails from slab in your inbox | |
let emails | |
try { | |
emails = GmailApp.search(search); | |
} catch (e) { | |
// if we can't contact gmail, there's nothing to do, just die | |
return | |
} | |
// For each email we find | |
emails.forEach(thread => { | |
// Get the subject of the email, and create form data for our webhook | |
const messages = thread.getMessages(); | |
const subject = thread.getFirstMessageSubject(); | |
const lastMessage = messages[messages.length - 1]; | |
let body = shortenUrls( | |
parse({body: lastMessage.getPlainBody(), subject}) | |
.trim() | |
) | |
if (body.length >= MAX_BODY_LENGTH) { | |
body = body.substring(0, MAX_BODY_LENGTH - 1) + '…' | |
} | |
// Send a POST request with the data to our Webhook URL | |
const options = { | |
method : 'post', | |
payload : {content: body} | |
} | |
try { | |
const res = UrlFetchApp.fetch(webhook, options); | |
const resCode = res.getResponseCode(); | |
if (resCode >= 200 && resCode < 400) { | |
// Mark as read so this doesn't come up again | |
thread.markRead(); | |
} | |
} | |
catch (e) { | |
// if we fail to send to the webhook, do nothing. The thread will | |
// remain unread, and will re-try next time around | |
} | |
}) | |
} | |
// This is the main function! Tell google scripts to run this. | |
function run() { | |
Object.keys(WEBHOOKS).forEach(key => { | |
findAndPing({search: EMAIL_SEARCHES[key], parse: PARSERS[key], webhook: WEBHOOKS[key]}) | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment