├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── package.json └── sample ├── simple-mail-notifier.js └── toggled-mail-notifier.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 jerome creignou 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | mail-notifier 2 | ============= 3 | 4 | > Notify your nodejs scripts of incoming imap mail. 5 | 6 | introduction 7 | ------------ 8 | Send `mail` event for each new email in IMAP INBOX. 9 | 10 | synopsis 11 | -------- 12 | Start listening new mails : 13 | 14 | ```javascript 15 | const notifier = require('mail-notifier'); 16 | 17 | const imap = { 18 | user: "yourimapuser", 19 | password: "yourimappassword", 20 | host: "imap.host.com", 21 | port: 993, // imap port 22 | tls: true,// use secure connection 23 | tlsOptions: { rejectUnauthorized: false } 24 | }; 25 | 26 | notifier(imap) 27 | .on('mail', mail => console.log(mail)) 28 | .start(); 29 | ``` 30 | 31 | Keep it running forever : 32 | 33 | ```javascript 34 | const n = notifier(imap); 35 | n.on('end', () => n.start()) // session closed 36 | .on('mail', mail => console.log(mail.from[0].address, mail.subject)) 37 | .start(); 38 | ``` 39 | **Note:** If using a Gmail account, you will need to do two things: 40 | 1. Enable IMAP in your Gmail account settings, which is detailed [here](https://support.google.com/mail/answer/7126229?hl=en). 41 | 2. Authorize "less secure apps", which you is laid out in "Option 2" [here](https://support.google.com/accounts/answer/6010255?hl=en). 42 | 43 | installation 44 | ------------ 45 | 46 | $ npm install mail-notifier 47 | 48 | API 49 | === 50 | 51 | notifier(config, customDbg) 52 | ---------------- 53 | The constructor function creates a new `notifier`. Parameter provide options needed for imap connection. 54 | `config` : 55 | 56 | * `host` : imap server host 57 | * `port` : imap server port number 58 | * `user` : imap user name 59 | * `password` : imap password 60 | * `tls` : need a tle connection to server 61 | * `tlsOptions` : see `tls` module options 62 | * `markSeen`: mark mail as read defaults to true 63 | * `box` : mail box read from defaults to 'INBOX' 64 | * `search`: search query defaults to ['UNSEEN'] 65 | * `connTimeout` : Number of milliseconds to wait for a connection to be established. Default: 10000 66 | * `authTimeout` : Number of milliseconds to wait to be authenticated after a connection has been established. Default: 5000 67 | * `debug`: *function* - if set, the function will be called with one argument, a string containing some debug info. Default: debug output if [enabled](#debugging). 68 | 69 | Options from [node-imap](https://github.com/mscdex/node-imap#connection-instance-methods) are also avaliable. 70 | 71 | For backward compatibility `username` is supported. 72 | 73 | `custommDbg`: *function* - if set, the function will be called with args that contain some mail-notifier debug info. Default: debug output if [enabled](#debugging). 74 | 75 | example: 76 | ```javascript 77 | const n = notifier(config, (...args) => { 78 | const msg = util.format(...args); 79 | customLogFn(msg); 80 | }); 81 | ``` 82 | 83 | .start() 84 | ------------------------------------ 85 | Start listening for incomming emails. 86 | 87 | .stop() 88 | ------------------------------------ 89 | Stop listening and close IMAP connection. 90 | 91 | Events 92 | ====== 93 | 94 | 'connected' 95 | ----- 96 | Sent when a connection to the server has been made. 97 | 98 | 'mail' 99 | ----- 100 | Sent on incoming new unread email. The parsed Mail is given as first parameter to the event listener. 101 | 102 | 'error' 103 | ----- 104 | Sent when an error occurs with the IMAP connection. The first parameter is the `err` object. 105 | 106 | 'end' 107 | ----- 108 | Sent when the IMAP connection is closed. This usually happens after a `stop` method call. 109 | 110 | Dependencies 111 | ============ 112 | 113 | This module relies heavily on [node-imap](https://github.com/mscdex/node-imap). For more advanced usage, please consider 114 | using it directly. 115 | 116 | 117 | Debugging 118 | ========= 119 | 120 | Debugging is enabled via the [visionmedia/debug](https://github.com/visionmedia/debug) module. 121 | 122 | To enable debug info add `mailnotifier` to the DEBUG env variable : 123 | 124 | ```sg 125 | $>DEBUG=mailnotifier node sample/simple-mail-notifier.js 126 | ``` 127 | 128 | Or to also have imap module debug info : 129 | 130 | ```sg 131 | $>DEBUG=mailnotifier,imap node sample/simple-mail-notifier.js 132 | ``` 133 | 134 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true, vars: true, indent: 4 */ 2 | 'use strict'; 3 | 4 | var util = require('util'), 5 | Imap = require('imap'), 6 | debug = require('debug'), 7 | async = require('async'), 8 | MailParser = require('mailparser').MailParser, 9 | EventEmitter = require('events').EventEmitter; 10 | 11 | var dbg = debug('mailnotifier'); 12 | 13 | function Notifier(opts, dbg) { 14 | EventEmitter.call(this); 15 | var self = this; 16 | self.options = opts; 17 | if (self.options.username) { //backward compat 18 | self.options.user = self.options.username; 19 | } 20 | self.options.box = self.options.box || 'INBOX'; 21 | self.options.debug = self.options.debug || debug('imap'); 22 | 23 | if(dbg) { 24 | self.dbg = dbg; 25 | } 26 | } 27 | util.inherits(Notifier, EventEmitter); 28 | 29 | module.exports = function (opts, customDbg) { 30 | return new Notifier(opts, customDbg); 31 | }; 32 | 33 | Notifier.prototype.start = function () { 34 | var self = this; 35 | 36 | var q = async.queue(function(task, callback) { 37 | self.dbg('process queue ' + task.name); 38 | self.scan(callback); 39 | }, 1); 40 | 41 | // assign a callback 42 | q.drain = function() { 43 | self.dbg('all items have been processed'); 44 | }; 45 | 46 | self.imap = new Imap(self.options); 47 | self.imap.once('end', function () { 48 | self.dbg('imap end'); 49 | self.emit('end'); 50 | }); 51 | self.imap.once('error', function (err) { 52 | self.dbg('imap error : %s', err); 53 | self.emit('error', err); 54 | }); 55 | self.imap.once('close', function (haserr) { 56 | self.dbg('imap close : %s', haserr ? 'errored ' + haserr : 'normal'); 57 | self.emit('end'); 58 | }); 59 | self.imap.on('uidvalidity', function (uidvalidity) { 60 | self.dbg('new uidvalidity : %s', uidvalidity); 61 | }); 62 | self.imap.once('ready', function () { 63 | self.emit('connected'); 64 | self.imap.openBox(self.options.box, false, function (err, box) { 65 | if (err) { 66 | self.dbg('unable to open box : %s', err); 67 | self.emit('error', err); 68 | return; 69 | } 70 | 71 | q.push({name: 'scan initial'}, function(err) { 72 | self.dbg('finished processing scan initial'); 73 | }); 74 | 75 | self.imap.on('mail', function (id) { 76 | self.dbg('mail event : %s', id); 77 | q.push({name: 'scan', id : id}, function(err) { 78 | self.dbg('finished processing scan '+id); 79 | }); 80 | }); 81 | }); 82 | }); 83 | self.imap.connect(); 84 | return this; 85 | }; 86 | 87 | Notifier.prototype.scan = function (callback) { 88 | var self = this, search = self.options.search || ['UNSEEN']; 89 | self.dbg('scanning %s with filter `%s`.', self.options.box, search); 90 | self.imap.search(search, function (err, seachResults) { 91 | if (err) { 92 | self.emit('error', err); 93 | callback(); 94 | return; 95 | } 96 | if (!seachResults || seachResults.length === 0) { 97 | self.dbg('no new mail in %s', self.options.box); 98 | callback(); 99 | return; 100 | } 101 | self.dbg('found %d new messages', seachResults.length); 102 | var fetch = self.imap.fetch(seachResults, { 103 | markSeen: self.options.markSeen !== false, 104 | bodies: '' 105 | }); 106 | fetch.on('message', function (msg) { 107 | var uid, flags; 108 | msg.on('attributes', function(attrs) { 109 | uid = attrs.uid; 110 | flags = attrs.flags; 111 | self.dbg("Message uid", attrs.uid); 112 | }); 113 | var mp = new MailParser(); 114 | mp.once('end', function (mail) { 115 | mail.uid = uid; 116 | mail.flags = flags; 117 | self.emit('mail', mail); 118 | self.dbg('found mail '+mail.headers["message-id"]); 119 | }); 120 | msg.once('body', function (stream, info) { 121 | stream.pipe(mp); 122 | }); 123 | }); 124 | fetch.once('end', function () { 125 | self.dbg('Done fetching all messages!'); 126 | callback(); 127 | }); 128 | fetch.once('error', function (err) { 129 | self.dbg('fetch error : ', err); 130 | self.emit('error', err); 131 | callback(); 132 | }); 133 | }); 134 | return this; 135 | }; 136 | 137 | Notifier.prototype.stop = function () { 138 | var self = this; 139 | self.dbg('imap.state before stopping: %s', this.imap.state); 140 | 141 | if (this.imap.state !== 'disconnected') { 142 | this.imap.end(); 143 | } 144 | 145 | self.dbg('notifier stopped'); 146 | return this; 147 | }; 148 | 149 | Notifier.prototype.dbg = dbg; 150 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mail-notifier", 3 | "description": "Nodejs library to get notified on new incoming email.", 4 | "keywords": [ 5 | "imap", 6 | "mail", 7 | "notifier" 8 | ], 9 | "version": "0.4.1", 10 | "author": { 11 | "name": "jerome creignou", 12 | "url": "https://github.com/jcreigno/nodejs-mail-notifier" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git://github.com/jcreigno/nodejs-mail-notifier.git" 17 | }, 18 | "dependencies": { 19 | "async": "^2.5.0", 20 | "debug": "^2.2.0", 21 | "imap": "~0.8.9", 22 | "mailparser": "0.6.2" 23 | }, 24 | "main": "./index.js", 25 | "devDependencies": {} 26 | } 27 | -------------------------------------------------------------------------------- /sample/simple-mail-notifier.js: -------------------------------------------------------------------------------- 1 | var notifier = require('../index.js'); 2 | 3 | var imap = { 4 | user: "jerome.creignou", 5 | password: "password", 6 | host: "imap.host.com", 7 | port: 993, // imap port 8 | tls: true,// use secure connection 9 | tlsOptions: { rejectUnauthorized: false } 10 | }; 11 | 12 | notifier(imap).on('mail',function(mail){console.log(mail);}).start(); 13 | -------------------------------------------------------------------------------- /sample/toggled-mail-notifier.js: -------------------------------------------------------------------------------- 1 | var notifier = require('../index.js'); 2 | 3 | var started = true; 4 | 5 | function toggleState(){ 6 | if(started){ 7 | n.stop(); 8 | }else{ 9 | n.start(); 10 | } 11 | started = !started; 12 | setTimeout(toggleState,3000); 13 | } 14 | 15 | var imap = { 16 | user: "jerome.creignou", 17 | password: "password", 18 | host: "imap.host.com", 19 | port: 993, // imap port 20 | tls: true,// use secure connection 21 | tlsOptions: { rejectUnauthorized: false } 22 | }; 23 | 24 | var n = notifier(imap).on('mail', function(mail){ 25 | console.log(mail); 26 | }); 27 | 28 | n.on('end',function(){ 29 | console.log('...notification ended...'); 30 | }); 31 | 32 | n.on('error',function(err){ 33 | console.log('...notification error : %s', err); 34 | }); 35 | 36 | n.start(); 37 | 38 | setTimeout(toggleState,3000); 39 | 40 | --------------------------------------------------------------------------------