mirror of
https://github.com/Luzifer/gmail-manage.git
synced 2024-12-22 20:11:21 +00:00
Advance development
- Use ES6 and Babel to transpile to AppsScript - Add filter management - Move config to config
This commit is contained in:
parent
05c5feac6a
commit
3e3cf696cd
9 changed files with 3024 additions and 25 deletions
4
.claspignore
Normal file
4
.claspignore
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
node_modules/**
|
||||||
|
package.json
|
||||||
|
package-lock.json
|
||||||
|
src/**
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
config.js
|
||||||
|
app.js
|
||||||
|
node_modules
|
25
Code.js
25
Code.js
|
@ -1,25 +0,0 @@
|
||||||
// Apps Scripts may only run for ~5m so we limit the execution
|
|
||||||
var MAX_DELETE_PER_LOOP = 1000
|
|
||||||
|
|
||||||
function cleanup() {
|
|
||||||
trashByQuery("label:crontab older_than:6m")
|
|
||||||
trashByQuery("label:newsletter older_than:6m")
|
|
||||||
trashByQuery("label:social-media older_than:2y")
|
|
||||||
trashByQuery("older_than:10y")
|
|
||||||
}
|
|
||||||
|
|
||||||
function trashByQuery(query) {
|
|
||||||
var threads = []
|
|
||||||
var removedThreads = 0
|
|
||||||
|
|
||||||
do {
|
|
||||||
threads = GmailApp.search(query)
|
|
||||||
for (var i = 0; i < threads.length; i++) {
|
|
||||||
var thread = threads[i]
|
|
||||||
thread.moveToTrash()
|
|
||||||
removedThreads++
|
|
||||||
}
|
|
||||||
} while (threads.length > 0 && removedThreads < MAX_DELETE_PER_LOOP)
|
|
||||||
|
|
||||||
Logger.log('Removed %s threads for query "%s"', removedThreads, query)
|
|
||||||
}
|
|
|
@ -1,6 +1,11 @@
|
||||||
{
|
{
|
||||||
"timeZone": "Europe/Paris",
|
"timeZone": "Europe/Paris",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"enabledAdvancedServices": [{
|
||||||
|
"userSymbol": "Gmail",
|
||||||
|
"serviceId": "gmail",
|
||||||
|
"version": "v1"
|
||||||
|
}]
|
||||||
},
|
},
|
||||||
"exceptionLogging": "STACKDRIVER"
|
"exceptionLogging": "STACKDRIVER"
|
||||||
}
|
}
|
2834
package-lock.json
generated
Normal file
2834
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
16
package.json
Normal file
16
package.json
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"babel": {
|
||||||
|
"presets": [
|
||||||
|
"google-apps-script"
|
||||||
|
],
|
||||||
|
"comments": false
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "babel src/*.js --out-file app.js"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/cli": "^7.1.2",
|
||||||
|
"@babel/core": "^7.1.2",
|
||||||
|
"babel-preset-google-apps-script": "0.0.3"
|
||||||
|
}
|
||||||
|
}
|
16
src/const.js
Normal file
16
src/const.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// Apps Scripts may only run for ~5m so we limit the execution
|
||||||
|
var MAX_DELETE_PER_LOOP = 1000
|
||||||
|
|
||||||
|
// Label constants (these are label NAMES to be used with getOrCreateLabel)
|
||||||
|
var labelImportant = "IMPORTANT"
|
||||||
|
var labelInbox = "INBOX"
|
||||||
|
var labelSpam = "SPAM"
|
||||||
|
var labelStarred = "STARRED"
|
||||||
|
var labelTrash = "TRASH"
|
||||||
|
var labelUnread = "UNREAD"
|
||||||
|
|
||||||
|
var labelCatForums = "CATEGORY_FORUMS"
|
||||||
|
var labelCatPersonal = "CATEGORY_PERSONAL"
|
||||||
|
var labelCatPromotions = "CATEGORY_PROMOTIONS"
|
||||||
|
var labelCatSocial = "CATEGORY_SOCIAL"
|
||||||
|
var labelCatUpdates = "CATEGORY_UPDATES"
|
48
src/index.js
Normal file
48
src/index.js
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
// executeMailCleanup takes the queries defined in config and deletes
|
||||||
|
// mails matched by those filters
|
||||||
|
function executeMailCleanup() {
|
||||||
|
for (let i = 0; i < config.cleanup_queries.length; i++) {
|
||||||
|
let query = config.cleanup_queries[i]
|
||||||
|
trashByQuery(query)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// applyFilterDefinition takes the filter definitions from the config
|
||||||
|
// matches them to the filters already defined in the Gmail account
|
||||||
|
// and afterwards applies reqired changes
|
||||||
|
function applyFilterDefinition() {
|
||||||
|
let presentFilters = getHashedFilters()
|
||||||
|
let definedFilters = {}
|
||||||
|
|
||||||
|
for (let i = 0; i < config.filters.length; i++) {
|
||||||
|
let filter = config.filters[i]
|
||||||
|
|
||||||
|
if (filter.action.add_labels) {
|
||||||
|
filter.action.addLabelIds = filter.action.add_labels.map((name) => {
|
||||||
|
return getOrCreateLabel(name).id
|
||||||
|
})
|
||||||
|
delete filter.action.add_labels
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.action.remove_labels) {
|
||||||
|
filter.action.removeLabelIds = filter.action.remove_labels.map((name) => {
|
||||||
|
return getOrCreateLabel(name).id
|
||||||
|
})
|
||||||
|
delete filter.action.remove_labels
|
||||||
|
}
|
||||||
|
|
||||||
|
definedFilters[hashFilter(filter)] = filter
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let hash in presentFilters) {
|
||||||
|
if (hash in definedFilters) continue
|
||||||
|
Gmail.Users.Settings.Filters.remove("me", presentFilters[hash].id)
|
||||||
|
Logger.log(["Removed filter", presentFilters[hash]])
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let hash in definedFilters) {
|
||||||
|
if (hash in presentFilters) continue
|
||||||
|
Gmail.Users.Settings.Filters.create(definedFilters[hash], "me")
|
||||||
|
Logger.log(["Created filter", definedFilters[hash]])
|
||||||
|
}
|
||||||
|
}
|
98
src/lib.js
Normal file
98
src/lib.js
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
// getHashedFilters returns a dictionary of all present filters with
|
||||||
|
// sha256 hashes as keys
|
||||||
|
function getHashedFilters() {
|
||||||
|
let resp = Gmail.Users.Settings.Filters.list("me")
|
||||||
|
let hashedFilters = {}
|
||||||
|
|
||||||
|
for (let i = 0; i < resp.filter.length; i++) {
|
||||||
|
let filter = resp.filter[i]
|
||||||
|
hashedFilters[hashFilter(filter)] = filter
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashedFilters
|
||||||
|
}
|
||||||
|
|
||||||
|
// getOrCreateLabel fetches a label by name or creates a new one if it does not exist
|
||||||
|
function getOrCreateLabel(name) {
|
||||||
|
let labels = parseCacheResponse(CacheService.getScriptCache().get("labels"))
|
||||||
|
if (labels === null) {
|
||||||
|
labels = Gmail.Users.Labels.list("me").labels
|
||||||
|
CacheService.getScriptCache().put("labels", JSON.stringify(labels), 60)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < labels.length; i++) {
|
||||||
|
let label = labels[i]
|
||||||
|
if (label.name == name) {
|
||||||
|
return label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalidate cache, needs to be reloaded next time
|
||||||
|
CacheService.getScriptCache().remove("labels")
|
||||||
|
|
||||||
|
return Gmail.Users.Labels.create({
|
||||||
|
labelListVisibility: "labelShow",
|
||||||
|
messageListVisibility: "show",
|
||||||
|
name: name,
|
||||||
|
}, "me")
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashFilter returns a hash to the filter dictionary given for comparison
|
||||||
|
function hashFilter(filter) {
|
||||||
|
let obj = {}
|
||||||
|
|
||||||
|
if (filter.criteria.query) obj["crit.query"] = filter.criteria.query
|
||||||
|
if (filter.action.addLabelIds) obj["act.add"] = filter.action.addLabelIds.join("|")
|
||||||
|
if (filter.action.removeLabelIds) obj["act.del"] = filter.action.removeLabelIds.join("|")
|
||||||
|
|
||||||
|
return sha256sum(JSON.stringify(obj))
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseCacheResponse unmarshals the JSON string from the cache if any
|
||||||
|
function parseCacheResponse(resp) {
|
||||||
|
if (resp === null) return null
|
||||||
|
return JSON.parse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolveLabelIDToName determines the name of a label to a given ID
|
||||||
|
function resolveLabelIDToName(id) {
|
||||||
|
let labels = parseCacheResponse(CacheService.getScriptCache().get("labels"))
|
||||||
|
if (labels === null) {
|
||||||
|
labels = Gmail.Users.Labels.list("me").labels
|
||||||
|
CacheService.getScriptCache().put("labels", JSON.stringify(labels), 60)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < labels.length; i++) {
|
||||||
|
let label = labels[i]
|
||||||
|
if (label.id == id) {
|
||||||
|
return label.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// sha256sum calculates a hex digest from the input string
|
||||||
|
function sha256sum(input) {
|
||||||
|
return Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, input)
|
||||||
|
.map((chr) => {
|
||||||
|
return (chr + 256).toString(16).slice(-2)
|
||||||
|
}).join('')
|
||||||
|
}
|
||||||
|
|
||||||
|
// trashByQuery moves messages matching a query to trash
|
||||||
|
function trashByQuery(query) {
|
||||||
|
let threads = []
|
||||||
|
let removedThreads = 0
|
||||||
|
|
||||||
|
do {
|
||||||
|
threads = GmailApp.search(query)
|
||||||
|
for (let i = 0; i < threads.length; i++) {
|
||||||
|
let thread = threads[i]
|
||||||
|
thread.moveToTrash()
|
||||||
|
removedThreads++
|
||||||
|
}
|
||||||
|
} while (threads.length > 0 && removedThreads < MAX_DELETE_PER_LOOP)
|
||||||
|
|
||||||
|
Logger.log('Removed %s threads for query "%s"', removedThreads, query)
|
||||||
|
}
|
Loading…
Reference in a new issue