Skip to content

Mail Bot: IMAP client sorting entering emails and triggering operations of your choice

License

Notifications You must be signed in to change notification settings

byteclubfr/mailbot

Repository files navigation

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: "[email protected]",
	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" <[email protected]>
		parseAddresses(headers)
		console.log(headers.to) // [ { address: '[email protected]', raw: '"Bob" <[email protected]>', 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.

About

Mail Bot: IMAP client sorting entering emails and triggering operations of your choice

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published