Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
// 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:notifications@slab.com 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
You can’t perform that action at this time.