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",
|
||||
"dependencies": {
|
||||
"enabledAdvancedServices": [{
|
||||
"userSymbol": "Gmail",
|
||||
"serviceId": "gmail",
|
||||
"version": "v1"
|
||||
}]
|
||||
},
|
||||
"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