Examples - OdysseusLevy/emailscript GitHub Wiki

I've included a number of useful scripts as examples. I'm actually using all of these scripts -- my email would be unusable without them.

I'm most comfortable using Groovy so that is what the examples are written in. I would love it if someone did translations of these scripts into other languages.

List

This is a good script to start with because it is so simple. It just logs what is in your inbox. I already talk about them in the overview, so here is the code:

MyEmails.foreach(){
  logger.info("weeks ago: ${email.weeksAgo} days ago: ${email.daysAgo}" +
              "from: ${email.from} subject: ${email.subject} " +
              "is read?: ${email.isRead} size: ${Helper.toBytes(email.size)}")
}

Counting

I wrote Emailscript because I had an email problem. I had thousands of emails in my Inbox and it was getting harder and harder to actually see what was important.

The very first question to answer was, "Who is clogging up my inbox?"

For this one you need to copy over both examples/counts.groovy and examples/table.ms

table.ms is a mustache template to format the results.

If you run

java -jar emailscript.jar counts.groovy

You will get an email report that shows you the top 200 email senders. This script simply counts up how many emails you have in your inbox, so it is pretty safe. Try it. Once you've run it, check your inbox to see the result.

Here is the code:

class Count {
  static linecount = 0

  Count(email, formatHelper) {
    from = email.from.toString()
    helper = formatHelper  // Global script variables like Helper are not accessible inside a class
  }

  def helper
  def from
  def count = 0
  def read = 0
  def size = 0
  def backgroundColor() {
    (linecount++ % 2 == 0)? "#cef;":"#fff"
  }

  def sizeInBytes() { helper.toBytes(size)}
  def percentRead() { helper.toPercent(read,count)}

  def String toString() {" count: ${count} read: ${read}"}
}

def emails = MyEmail.getEmails(folder)
def counts = [:]

def increment(counts, email) {
  def currentCount = counts[email.from] ?: new Count(email, Helper)
  currentCount.count++
  currentCount.size += email.size
  if (email.isRead)
    currentCount.read++
  counts[email.from] = currentCount
}

for ( email in emails) {
  increment(counts, email)
}

def sorted = counts.sort {-it.value.count} //sort from highest to lowest
sorted = sorted.values().toList()

def result = sorted.size() <= 200 ? sorted : sorted[0..199] // truncate if too large

def html = Helper.execute("table.ms", [folder: folder, size: emails.size(), counts: result])

def mail = MyEmail.newMail()
mail.setSubject("Counts")
mail.setHtml(html)
//mail.setTo("[email protected]")  // Set this if you want to send to a different email address

MyEmail.send(mail)

This script runs through all emails in the Inbox folder and for each email sender keeps a count of

  • total number
  • total size in bytes
  • total read

It uses the (supplied by Emailscript) Helper object to run the data through the table.ms template:

def html = Helper.execute("table.ms", [folder: folder, size: emails.size(), counts: result])

It then sends out a mail with that generated html:

def mail = MyEmail.newMail()
mail.setSubject("Counts")
mail.setHtml(html)
//mail.setTo("[email protected]")  // Set this if you want to send to a different email address

MyEmail.send(mail)

Whitelist

This simple script lets you add all of your Google Contacts to your whitelist. It requires that you have a config file that looks something like:

!GoogleContacts
account: <myaccount>@gmail.com
password: <mypassword>                                                                                                                                                                                                  
nickname: GoogleContacts

Here is the code:

for( who in GoogleContacts.emails) {

  if (!who.hasTag("whitelist")) {
    logger.info("Adding to whitelist: $who")
    who.addTag("whitelist")
  }
  else{
    logger.info("already whitelisted: $who")
  }
}

Who

Emailscript represents people using a Who object. It simply contains one email and a name. The GoogleContacts api returns a different Who object for each email it finds. So if you have multiple emails for one person, this api will return multiple Who objects for that person.

Tags

Emailscript allows you to add "tags" to a Who object. This is extremely useful for things like whitelists and blacklists.

The code runs through all contacts found and adds a "whitelist" tag to any contacts that don't already have the tag.

Daily cleanup

This script is meant to be run daily, hence the name. This script cleans out your inbox and folders.Some emails are treated like notifications. That is, you only see, say, the most recent 2 notifications from any one sender (eg. your bank, or Amazon). All the older ones get moved to a "Notifications" folder. If any emails in the notifications folder are over, say, 3 months old they get deleted. This is all configured in the MailRules bean.

Helper.requires(["MyEmail","MailRules"])

def categories = MailRules.Categories

//
// First visit all of the folders associated with our categories and clear out old mail, etc.
//

categories.each { category, policy -> applyRules(category, policy) }

// For a given folder look for mails manually moved into the folder. Also clean out old emails if that is the
// policy for this folder

def applyRules(folder, policy) {

    logger.info("Applying rules for folder: $folder policy: $policy")
    if (policy.skipScan)
        return

	if (!MyEmail.hasFolder(folder)){
	    logger.warn("Configuration Error! Can not find folder: $folder")
	    return
	}

    // Check any emails added since last time

    MyEmail.readLatest(folder, { emails ->
        logger.info("${emails.size()} new emails found in $folder")
        for(email in emails) {

            if (email.moveHeader != folder) {
                logger.info("Email moved to $folder manually from folder: ${email.moveHeader}, updating info; from: ${email.from} subject: ${email.subject}")

                email.from.setValue("Category", folder)
                if (policy.whitelist)
                    email.from.addTag("whitelist")
                else
                    email.from.removeTag("whitelist")
            }
        }
    })

    // If this policy has a folderDays property set get rid of any mails older than that

    if (policy.folderDays) {

        def daysAgo = Helper.daysAgo(policy.folderDays)
        logger.info("looking for emails before: $daysAgo in $folder")
        emails = MyEmail.getEmailsBefore(folder, daysAgo)
        for(email in emails) {
            logger.info("removing old mail from: ${email.from} subject: ${email.subject} daysAgo: ${email.daysAgo}")
            email.delete()
        }
    }
}

//
// Next, go through inbox emails looking for mails we should move to a folder
//

def counts = [:]
def emails = MyEmail.getEmails("Inbox")
emails = emails.reverse()   //want newest to oldest

for(email in emails){

    def category = getCategory(email)
    if (!category)
        continue

    def rule = categories[category]
    if (!rule)
        continue

    counts[email.from] = (counts[email.from] ?: 0) + 1

    if (rule.saveUnread && !email.isRead)
        continue

    if (rule.inboxMax && counts[email.from] > rule.inboxMax) {
        logger.info("email from: ${email.from} subject: ${email.subject} count: ${counts[email.from]} > ${rule.inboxMax}")
        email.moveTo(category)
    }

    if (rule.inboxDays &&  email.daysAgo > rule.inboxDays){
        logger.info("mail from: ${email.from} subject: ${email.subject} daysAgo: ${email.daysAgo} > ${rule.inboxDays}")
        email.moveTo(category)
    }
}

def getCategory(email) {
    for(rule in MailRules.CategoryRules){
        if (rule.subjectContains && email.subject.contains(rule.subjectContains) )
            return rule.category
        else if (rule.host && email.from.host == rule.host)
            return rule.category
    }

    email.from.getValue("Category")
}

Helper.requires(["MyEmail","MailRules"]).

This is completely optional -- it simply makes sure that the objects "MyEmail" and "MailRules" are present. I consider it a kind of documentation.

Values

In addition to tags, you can set values on a Who object. This is simply a way to add named values. In this script we are adding "Category" values to the Who object. We are then using the categories to make decisions about what to do with an given email.

Configuration

This script demonstrates how to make a general purpose script that is configured using a .yml file added to the config directory.

In this case the config file is called "MailRules"

nickname: MailRules

Hardcore: false 

CategoryRules:
    - subjectContains: Emailscript
      category: Emailscript
    - host: facebookmail.com
      category: Social
    - subjectContains: Jaiya
      category: friends
    - subjectContains: Ulysses
      category: friends

Spam: Junk

Categories:
    Emailscript:
        skipScan: true
        whitelist: true
        saveUnread: true
        inboxDays: 7
    Notifications:
        inboxMax: 2
        whitelist: true
        folderDays: 60
    Newsletters:
        inboxMax: 2
        whitelist: true
    Bulk:
        folderDays: 60
    Junk:
        folderDays: 45
    Social:
        inboxMax: 2
        whitelist: true
    friends:
        saveUnread: true
        inboxDays: 7
        whitelist: true

This is a standard yaml file.

MailRules.CategoryRules represents a list of objects. The script runs through these objects to determine what category an email falls into.

MailRules.Categories represents a map of objects. The script uses this to determine what categories we are working with. Note that, for convenience the script is assuming that their is a corresponding folder for each category name. So, for example we are assuming that there are friends, Social, Junk, etc. folders.

The script checks that folders actually exist using:

 if (!MyEmail.hasFolder(folder)){

Running the code

This script actually moves things around so, I recommend you use the -dryrun flag when trying it out. This flag means that moves and deletes are only logged -- no emails will actually get moved or deleted.

Your can run it like this

java -jar emailscript.jar -dryrun daily.groovy

Continously scan

This is the script I use the most. This script runs continously. It scans three folders: Inbox, Sent, Junk (or whatever your Spam folder is). Any mail that is sent instantly causes all recipients to get whitelisted. Any mail that is manually moved into the Junk folder is instantly blacklisted.

If you have MailRules.hardcore == true, then any mails that are not explicitly whitelisted are sent to either Junk or Bulk depending on whether or not they have verified headers.

Helper.requires(["MyEmail","MailRules"])

//
// Spam
//

def spamFolder = MailRules.Spam?.folder

// Check for spam

if (spamFolder) {

    MyEmail.scanFolder(spamFolder){ emails ->
        for (email in emails) {
            email.from.removeTag("whitelist")
            email.from.setValue("Category", spamFolder)
        }
    }
}

//
// Sent message recipients are whitelisted
//

MyEmail.scanFolder("Sent"){ emails ->
    logger.info("scanning sent emails")

    for(email in emails) {
        def sentTo = email.to + email.cc + email.bcc
        logger.info("mail sent to $sentTo, subject ${email.subject}")

        for (who in sentTo){
            who.addTag("Sent")
            who.addTag("whitelist")
        }
    }
}

//
// Inbox
//

def getCategoryName(email) {
    if (MailRules.CategoryRules) {
        for(rule in MailRules.CategoryRules) {
            if (rule.subjectContains && email.subject.toLowerCase().contains(rule.subjectContains.toLowerCase()))
                return rule.category
            if (rule.host && email.from.host == rule.host)
                return rule.category
        }
    }

    email.from.getValue("Category")
}

def isBlacklisted(category, email) {
    if (category)
        category.blacklist == true
    else if (MailRules.Hardcore)
        !email.from.hasTag("whitelist")
    else
        email.from.hasTag("blacklist")
}

MyEmail.scanFolder("Inbox"){emails ->

    for(email in emails){

        def categoryName = getCategoryName(email)
        def category = MailRules.Categories[categoryName]

        def isBlacklisted = isBlacklisted(category, email)

        if (MailRuiles.Hardcore&& !isBlacklisted && !email.from.hasTag("whitelist")){
            logger.info("whitelisting $email.from")
            email.from.addTag("whitelist")
        }

        logger.info("Inbox; from: $email.from subject: ${email.subject} category: $categoryName blacklisted?: ${isBlacklisted}")

        // Check for mails manually moved back into Inbox

        if (email.moveHeader) {
            logger.info("Manually moved back into Inbox from  ${email.moveHeader}")
            email.from.addTag("whitelist")
            email.from.setValue("Category", "")
        }
        else if (isBlacklisted){
            if (category)
                email.moveTo(categoryName)
            else if (MailRules.BulkFolder && email.isVerifiedHost)
                email.moveTo(MailRules.BulkFolder)
            else
                email.moveTo(spamFolder)
        }

    }
}

The coolest thing about this script is the scan function.

    MyEmail.scanFolder(spamFolder){ emails ->
        for (email in emails) {
            email.from.removeTag("whitelist")
            email.from.setValue("Category", spamFolder)
        }
    }

When you call scanFolder with a closure (callback function), Emailscript launches a thread to continously scan for new emails in that folder.

scanFolder guarantees that (unless you tell it not to), the closure will process all emails in that folder. So the very first time you call it your script will get called right away with all the emails in that folder. Then it will sit and wait for new emails. When one appears your script will be immediately called again.

If you stop the progran and run it later, scanFolder remembers the last email you processed and sends your script any emails that have arrived since then. Again, the guarantee is that your script will see all emails in the folder.

Running the script

If you are just getting started, I recommend using the -dryrun flag first.

java -jar emailscript.jar -dryrun scan.groovy
⚠️ **GitHub.com Fallback** ⚠️