A Google App Script to detele emails from the spam folder. Why? – Because that's what bored programmers do. This is a fork of https://github.com/spamzero/spamzero, with minor updates to suite my needs. In case you use this please check and update the rules. Goodluck.
Last active
October 15, 2023 18:35
-
-
Save raisedadead/27c335bd879725c72e54a08d6b99a0b5 to your computer and use it in GitHub Desktop.
Delete Spam Emails from Gmail — using Google App Script.
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
// version 1.0.0 | |
// Forked from https://github.com/spamzero/spamzero with some improvements. | |
/** | |
* The threads you want to process. A thread (GmailThread) consists | |
* of multiple messages (GmailMessage). | |
* See: https://developers.google.com/apps-script/reference/gmail/ | |
*/ | |
var threads = GmailApp.getSpamThreads(); | |
/** | |
* The action(s) to take on the threads matching your rules. | |
* | |
* Possible values are: READ, MOVE_TO_TRASH, LABEL(name). | |
* You can also use multiple actions ["READ", "LABEL(Some label)"]. | |
*/ | |
var actions = ['MOVE_TO_TRASH']; | |
var rules = [ | |
// This first one should be the first rule in the ruleset | |
function (m, raw) { | |
'No subject in the headers' | |
if (!raw.headers.subject) { | |
return true; | |
} | |
}, | |
function (m, raw) { | |
'Goomoji + name in subject' | |
return StringHelper.containsGoomoji(raw.headers.subject); | |
}, | |
function (m, raw) { | |
'From *.ru + image attachment' | |
var ruSender = m.getFrom().match(/\.ru>?$/); | |
var withImageAttachment = MessageHelper.hasImageAttachment(m); | |
return ruSender && withImageAttachment; | |
}, | |
function (m, raw) { | |
'Profanity filter' | |
var matchers = [ | |
/slut/i, | |
/f.ck/i, | |
/s.ck/i, | |
/c.ck/i, | |
/lady/i, | |
/h..kup/i, | |
/cum/i, | |
/babe/i, | |
/p.rn/i, | |
/s[e3\*\.]xy?/i, | |
/house\s?wife/i | |
]; | |
for (var i = 0; i < matchers.length; i++) { | |
if ( | |
raw.headers.subject.match(matchers[i]) || | |
raw.body.match(matchers[i]) || | |
raw.headers.from.match(matchers[i]) | |
) { | |
return true; | |
} | |
} | |
return false; | |
}, | |
function (m, raw) { | |
'Has link to dropbox in body' | |
return raw.body.match(/dl\.dropboxusercontent\.com/i); | |
}, | |
function (m, raw) { | |
'Shortened url in body' | |
var matchers = [/bit\.ly/i, /tinyurl\.com/i, /goo\.gl/]; | |
for (var i = 0; i < matchers.length; i++) { | |
if (raw.body.match(matchers[i])) { | |
return true; | |
} | |
} | |
return false; | |
}, | |
function (m, raw) { | |
'From: *.club, *.date, *.top, etc.' | |
return m.getFrom().match(/(\.club|\.date|\.top|\.xyz|\.trade|\.party)>?$/i); | |
}, | |
function (m, raw) { | |
'URL to *.club, *.date, *.top, etc.' | |
return raw.body.match( | |
/(https?|www).+(\.club|\.date|\.top|\.xyz|\.trade|\.party)/i | |
); | |
}, | |
function (m, raw) { | |
"URL to a .php site & 'confidential' / 'broken' words in body" | |
return ( | |
raw.body.match(/(https?|www).+\.php/i) && | |
raw.body.match(/(confidential|broken)/i) | |
); | |
} | |
]; | |
// Debug mode (will log events, use the log viewer [View / Show logs] to view them) | |
var isDebug = true; | |
// Use these helper methods in your rules | |
var MessageHelper = { | |
hasImageAttachment: function (message) { | |
var attachments = message.getAttachments(); | |
for (var i = 0; i < attachments.length; i++) { | |
if (attachments[i].getContentType().match(/image/)) { | |
return true; | |
} | |
} | |
return false; | |
} | |
}; | |
var StringHelper = { | |
containsGoomoji: function (string) { | |
return string.match(/=\?[Uu][Tt][Ff]-8\?[BQ]\?[^\?]+\?=/i); | |
} | |
}; | |
// Probably you won't need to edit the code below | |
var getLogger = function () { | |
if (isDebug) { | |
return Logger; | |
} else { | |
return { | |
clear: function () {}, | |
log: function () {}, | |
getLog: function () {} | |
}; | |
} | |
}; | |
/** | |
* Checks whether the given thread's messages matches any of your rules. | |
* Only one message needs to be matched by your rules for this function to return true. | |
*/ | |
var anyMessageMatchesAnyRuleInThread = function (thread) { | |
var messages = thread.getMessages(); | |
for (var i = 0; i < messages.length; i++) { | |
var raw = parseRawContent(messages[i].getRawContent()); | |
for (j = 0; j < rules.length; j++) { | |
if (rules[j](messages[i], raw)) { | |
var ruleDescription = parseRuleDescription(rules[j]); | |
getLogger().log( | |
'"' + | |
messages[i].getSubject() + | |
'" matched rule "' + | |
(ruleDescription ? ruleDescription : j) + | |
'"' | |
); | |
return true; | |
} | |
} | |
} | |
return false; | |
}; | |
/** | |
* Parses the raw message into an object. For the returned object's format see the rule array's description. | |
*/ | |
var parseRawContent = function (rawContent) { | |
var lines = rawContent.split('\n'); | |
var result = {}; | |
var headers = {}; | |
var body = ''; | |
var currentHeaderKey = null; | |
var headerParsed = false; | |
for (var i = 0; i < lines.length; i++) { | |
if (lines[i].trim() === '') { | |
if (headers.date === undefined) { | |
continue; | |
} | |
headerParsed = true; | |
continue; | |
} | |
if (!headerParsed) { | |
var headerParts = lines[i].match(/^([-a-z]+):(.*)/i); | |
if (headerParts) { | |
currentHeaderKey = headerParts[1].toLowerCase(); | |
headers[currentHeaderKey] = headerParts[2].trim(); | |
} else { | |
// Header continues on new line | |
headers[currentHeaderKey] += ' ' + lines[i].trim(); | |
} | |
} else { | |
body += lines[i]; | |
} | |
} | |
if (headers['content-transfer-encoding'] === 'base64') { | |
try { | |
body = Utilities.newBlob(Utilities.base64Decode(body)).getDataAsString(); | |
} catch (err) { | |
getLogger().log('Could not base64 decode body.'); | |
} | |
} | |
result.headers = headers; | |
result.body = body; | |
return result; | |
}; | |
/** | |
* Extracts the description from a rule. | |
*/ | |
var parseRuleDescription = function (func) { | |
var lines = func.toString().split('\n'); | |
for (var i = 0; i < lines.length; i++) { | |
var matches = lines[i].trim().match(/^"([^"]+)";?$/); | |
if (matches) { | |
return matches[1]; | |
} | |
} | |
return ''; | |
}; | |
var ReadAction = function () { | |
this.run = function (thread) { | |
thread.markRead(); | |
}; | |
}; | |
var MoveToTrashAction = function () { | |
this.run = function (thread) { | |
thread.moveToTrash(); | |
}; | |
}; | |
var LabelAction = function (labelName) { | |
this.run = function (thread) { | |
var label = GmailApp.getUserLabelByName(labelName); | |
if (!label) { | |
label = GmailApp.createLabel(labelName); | |
} | |
thread.addLabel(label); | |
}; | |
}; | |
var ActionFactory = { | |
create: function (actionName) { | |
if (actionName === 'READ') { | |
return new ReadAction(); | |
} | |
if (actionName === 'MOVE_TO_TRASH') { | |
return new MoveToTrashAction(); | |
} | |
var labelParts = actionName.match(/LABEL\((.+)\)/i); | |
if (labelParts) { | |
return new LabelAction(labelParts[1]); | |
} | |
throw 'Unknown action: ' + actionName; | |
} | |
}; | |
var runActionsOnThread = function (thread) { | |
for (var i = 0; i < actions.length; i++) { | |
var action = ActionFactory.create(actions[i]); | |
getLogger().log('Running action "' + actions[i] + '"'); | |
action.run(thread); | |
} | |
}; | |
/** | |
* The main function. This should be the function chosen in the execute dropdown | |
* in the script editor toolbar above. | |
*/ | |
function main() { | |
getLogger().clear(); | |
for (var i = 0; i < threads.length; i++) { | |
if (i > 0) { | |
getLogger().log('---'); | |
} | |
getLogger().log('Processing thread number ' + i); | |
// Match the rules, or check if the thread even has a subject (seemingly a new hack from the spammers) | |
if (anyMessageMatchesAnyRuleInThread(threads[i]) || !threads[i].subject) { | |
runActionsOnThread(threads[i]); | |
} else { | |
getLogger().log( | |
'No action taken because none of your rules were matching this thread.' | |
); | |
getLogger().log('Subject:' + threads[i].subject); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment