Skip to content

Instantly share code, notes, and snippets.

@anstosa
Last active December 30, 2022 08:25
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anstosa/08ba9102d9d8e33fe704beb47354acad to your computer and use it in GitHub Desktop.
Save anstosa/08ba9102d9d8e33fe704beb47354acad to your computer and use it in GitHub Desktop.
Daily Spam Digest for Google Apps Script
// Daily Spam Digest for Gmail.
// Creates a single digest email with deep links to any spam messages you received that day
// so you can easily check for false positives and retrieve them
// Usage:
// 1. Replace EMAIL with your email
// 2. Create trigger whenever you want to get the email
// 3. If you set your trigger for the morning, you likely want to edit the date settings so you don't miss any
function run() {
const EMAIL = 'username@gmail.com';
const date = new Date();
const day = date.getDate();
const month = date.getMonth() + 1;
const year = date.getFullYear();
const dateString = [year, month, day].join('/');
const results = GmailApp.search(['in:spam in:unread after:', dateString].join(' '));
var threadsByRecipient = {};
if (!results.length) { return; }
results.forEach(function(thread) {
const lastMessage = thread.getMessages()[thread.getMessageCount() - 1];
const recipient = getTo(lastMessage);
if (!threadsByRecipient[recipient]) {
threadsByRecipient[recipient] = [];
}
threadsByRecipient[recipient].push(thread);
});
var body = '<table>';
for (var recipient in threadsByRecipient) {
var threads = threadsByRecipient[recipient];
body += '<tr></tr>';
body += '<tr><th colspan=3 style="text-align:left;">';
body += ['<h2>', unlink(escapeEmail(recipient)), '</h2></a>'].join('');
body += '</th></tr>';
threads.forEach(function(thread) {
body += renderThread(thread);
})
}
body += '</table>';
GmailApp.sendEmail(EMAIL, "Daily Spam Digest", body.replace(/<[^>]*>/g, ''), {
htmlBody: body,
});
}
function unlink(string) {
return ['<a style="color:black;text-decoration:none;">', string, '</a>'].join('');
}
function getTo(message) {
try {
return message.getRawContent().match(/^To:.*<(.*)>$/m)[1];
} catch(error) {
return message.getTo();
}
}
function getFrom(message) {
try {
return message.getRawContent().match(/^From:(.*)$/m)[1].trim();
} catch(error) {
return message.getFrom();
}
}
function splitFrom(from) {
try {
const midpoint = from.indexOf(' <');
const name = from.substr(0, midpoint);
const email = from.substr(midpoint + 2, from.length - midpoint - 2);
return {name: name, email: email};
} catch(error) {
return {name: name, email: ''};
}
}
function escapeEmail(email) {
return email.replace('<', '&lt;').replace('>', '&gt;');
}
function renderThread(thread) {
const subject = thread.getFirstMessageSubject();
const lastMessage = thread.getMessages()[thread.getMessageCount() - 1];
const sender = splitFrom(getFrom(lastMessage));
const link = 'https://mail.google.com/mail/u/0/#all/' + lastMessage.getId();
return tr([
td(unlink(truncate(sender.name, 15))),
td(unlink(truncate(sender.email, 25, true)), true),
td('<a href="' + link + '">' + subject + '</a>'),
]);
}
function truncate(string, length, rtl) {
if (string.length < length) {
return string;
}
else {
const truncated = rtl ?
'...' + string.substr(string.length - length, length) :
string.substr(0, length) + '...'
;
return '<span title="' + string + '">' + truncated + '</span>';
}
}
function tr(elements) {
return ['<tr>', elements.join(''), '</tr>'].join('');
}
function td(string, rtl) {
return ['<td style="text-align:' + (rtl ? 'right' : 'left') + '">', string, '</td>'].join('');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment