node package manager

mailbot

MailBot

This module will help you build an imap bot reacting to incoming emails.

You mainly provide two functions:

  • A trigger which will be called for each received e-mail and should return a promise of a truthy if mail should trigger some job
  • A mailHandler which will be called for each triggered e-mail and will do its job

Warning: Missing tests

There are actually no test because I found a bit too time-consuming to mock an Imap server for testing. They will come later.

Installation

npm install --save mailbot

Usage

const { createBot } = require('mailbot')
 
const bot = createBot(options)
 
bot.start()

API

// Start watching, returns a Promise 
bot.start()
 
// Stops watching, returns a Promise 
// if argument is true, it will destroy the connection immediately 
// you're strongly advised to gracefully disconnect by not setting this parameter 
bot.stop(destroy = false)
 
// Restarts 
bot.restart(destroy = false)
 
// Update configuration option 
// Note: if you update 'imap', 'mailbox', or 'filter', it will restart the bot 
// unless said otherwise 
bot.configure('imap', connectionInfo, autoRestart = true, destroy = false)

Options

{
 
// IMAP configuration, see module "imap" 
imap: {
    user: "user@gmail.com",
    password: "password",
    host: "imap.googlemail.com",
    port: 993,
    keepalive: true,
    tls: true,
    tlsOptions: {
        rejectUnauthorized: false
    },
},
 
// Watched inbox 
mailbox: 'INBOX',
 
// Should bot mark fetched emails as read? 
// If true, you're sure you will never fetch same mail twice even when restarting 
// If false, you'll mess with server emails 
markSeen: true,
 
// Search filter to fetch emails 
filter: ['UNSEEN'],
 
// Should the trigger be checked when receiving headers, or when body has been parsed? 
// If your control depends only on headers (subject, recipient, sender…), you can set it to true 
// Warning: in 'headers' phase, headers are not parsed yet and you may need helpers 
triggerOnHeaders: false,
 
// The trigger: this function is called for each e-mail 
// Input: a mail object (if triggerOnHeaders is true, it will only have 'headers' property) 
// Output: a value or a promise of a value 
// The mail will be "handled" only if final value is truthy 
trigger ({ headers }) {
    // Example: work with e-mails whose subject is 'BOT: <something>' 
    const match = headers.subject.match(/BOT: (.*)$/)
    return match && match[1]
},
 
// The mail handler: this is the "job executor" 
// Input: a mail object, and the trigger value 
mailHandler (mail, trigger) {
    console.log({
        subject: mail.headers.subject,
        trigger
    })
},
 
// The error handler, called for each error occurring during processes 
// As there may be error thrown from very different places, 
// the function is called with a "context", which can be one of: 
// - 'IMAP_ERROR': global error 
// - 'SEARCH': failed searching or fetching mails 
// - 'TRIGGER': when trying to calculate trigger result (*) 
// - 'MAIL': when trying to handle mail (*) 
// (*) in those cases the third parameter will be a complete mail object 
// allowing you to access sender, subject, etc… 
errorHandler (error, context, mail) {
    console.error(context, error)
    if (mail) {
        sendErrorMailTo(mail.from)
    }
},
 
// IMAP client reconnection 
autoReconnect: true,
autoReconnectTimeout: 5000,
 
// false: attachments contents will be directly accessible as Buffer in 'content' property 
// true: attachments will be streamed via 'stream' property 
// Note: you can safely set it to false if you use triggerOnHeaders, otherwise you should work with streams 
streamAttachments: true,
 
// If true, mail.text will not contain signature 
// Properties 'textSignature' and 'textOriginal' will be added 
removeTextSignature: true,
 
// If true, if embedded images are found in signature of mail.html 
// they will be dropped from 'attachments' and moved to 'ignoredAttachments' 
ignoreAttachmentsInSignature: true,
 
// If true, property 'cleanSubject' will contain the subject without all messy prefixes 
cleanSubject: true,
 
// Set to a strictly positive number to define period (milliseconds) between automatic search 
// Note that this delay starts AFTER a batch has been handled 
// Any falsey value will disable periodic search 
searchPeriod: false,
 
}

Mail objects

Mail objects are generated by mailparser (version 0.x, not the buggy 2.0): see full description of parsed mail object.

Following custom properties are added:

  • ignoredAttachments
  • textSignature
  • textOriginal
  • cleanSubject

Helpers

parse addresses in raw headers

Working with triggerOnHeaders: true is interesting for performance purpose, but you get unparsed headers. This function will help you working with to/cc/bcc headers:

{
    triggerOnHeaders: true,
    trigger: ({ headers }) => {
        console.log(headers.to) // "Bob" <bob@a.b> 
        parseAddresses(headers)
        console.log(headers.to) // [ { address: 'bob@a.b', raw: '"Bob" <bob@a.b>', phrase: '"Bob"' } ] 
    }
}

Extract signature from text body

This function will help you remove signature from e-mail body, using talon:

const { text, signature } = extractSignature(mail.text)

Strip HTML tags

This function will remove any HTML tag from a string, using striptags internally:

const text = stripTags(mail.html)

Debugging

This module uses debug internally, and you can enable internal debug messages adding mailbot to your environment variable DEBUG:

env DEBUG=mailbot node mybot.js

Full sample

See sample.js in repository: it's a mail bot which will react on every mail which subject starts with 'upload to …'. It will fetch all attachments and save it to <upload dir>/<sender address>/<requested path>.

This illustrates how you can easily create that type of bot.