UNPKG

imap-mailbox

Version:

Simple library to use and watch IMAP mailboxes

321 lines (320 loc) 13.6 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __asyncValues = (this && this.__asyncValues) || function (o) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var m = o[Symbol.asyncIterator], i; return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = require("events"); const imapflow_1 = require("imapflow"); const Logger_1 = __importDefault(require("./Logger")); const Mail_1 = __importDefault(require("./Mail")); const mailparser_1 = __importDefault(require("mailparser")); const utils_1 = require("./utils"); const DEFAULT_RECONNECT_INTERVAL = 1000 * 60; const DEFAULT_MAILBOXES_INTERVAL = 1000 * 60; class Imap { /** * Await run() method before any action on this object * @param {ImapConfig} config Imap configuration */ constructor(config) { this.config = config; this.eventEmitter = new events_1.EventEmitter(); this.logger = new Logger_1.default(config.logging); this.mailboxes = new Map(); } /** * Run IMAP service * You should await this method before any action on Imap object */ run() { return __awaiter(this, void 0, void 0, function* () { yield this.connect(); this.watchErrors(); yield this.loadMailboxes(); yield this.watchMailboxes(); }); } on(event, callback) { switch (event) { case 'newMail': this.eventEmitter.addListener('newMail', callback); break; case 'loadedMail': this.eventEmitter.addListener('loadedMail', callback); break; case 'deletedMail': this.eventEmitter.addListener('deletedMail', callback); break; } } /** * Delete mails from uids or mail objects * @param {string} mailboxPath Mailbox path where mails to delete are * @param {UidsList} toDelete Mails or uids of mails to delete * @returns {boolean} true if mails are deleted */ deleteMails(mailboxPath, toDelete) { return __awaiter(this, void 0, void 0, function* () { const uids = (0, utils_1.uidsAndMailsToUids)(toDelete); const mailbox = yield this.client.getMailboxLock(mailboxPath); if (mailbox) { const result = yield this.client.messageDelete(uids.join(','), { uid: true, }); if (result === true) { uids.map((uid) => this.eventEmitter.emit('deletedMail', uid)); } mailbox.release(); return result; } return false; }); } /** * Mark mails as seen from uids or mail object * @param {string} mailboxPath Mailbox path where mails to see are * @param {UidsList} toSee Mails or uids of mails to see * @returns {boolean} true if mails are marked seen */ seeMails(mailboxPath, toSee) { return __awaiter(this, void 0, void 0, function* () { const uids = (0, utils_1.uidsAndMailsToUids)(toSee); const mailbox = yield this.client.getMailboxLock(mailboxPath); if (mailbox) { const result = yield this.client.messageFlagsAdd(uids.join(','), ['\\Seen'], { uid: true, }); mailbox.release(); return result; } return false; }); } /** * Mark mails as unseen from uids or mail object * @param {string} mailboxPath Mailbox path where mails to unsee are * @param {UidsList} toUnsee Mails or uids of mails to unsee * @returns {boolean} true if mails are marked unseen */ unseeMails(mailboxPath, toUnsee) { return __awaiter(this, void 0, void 0, function* () { const uids = (0, utils_1.uidsAndMailsToUids)(toUnsee); const mailbox = yield this.client.getMailboxLock(mailboxPath); if (mailbox && uids.length > 0) { const result = yield this.client.messageFlagsRemove(uids.join(','), ['\\Seen'], { uid: true, }); mailbox.release(); return result; } return false; }); } /** * Get a list of unseen mails from a mailbox * @param {string} mailboxPath Path to mailbox (or name) * @returns {Mail[]} List of mails */ getUnseenMails(mailboxPath) { return __awaiter(this, void 0, void 0, function* () { const mails = []; for (const msg of yield this.search(mailboxPath, { seen: false })) { const mail = yield this.getNewMail(mailboxPath, msg); mails.push(mail); } return mails; }); } /** * Get a list of seen mails from a mailbox * @param {string} mailboxPath Path to mailbox (or name) * @returns {Mail[]} List of mails */ getSeenMails(mailboxPath) { return __awaiter(this, void 0, void 0, function* () { const mails = []; for (const msg of yield this.search(mailboxPath, { seen: true })) { const mail = yield this.getNewMail(mailboxPath, msg); mails.push(mail); } return mails; }); } /** * Get all the mails from a mailbox * @param {string} mailboxPath Path to mailbox (or name) * @returns {Mail[]} List of mails */ getAllMails(mailboxPath) { return __awaiter(this, void 0, void 0, function* () { const mails = []; for (const msg of yield this.search(mailboxPath, '1:*')) { const mail = yield this.getNewMail(mailboxPath, msg); mails.push(mail); } return mails; }); } /** * Get list of mailboxes * @returns {Map<string, Mailbox>} Map of mailboxes */ getMailboxes() { return this.mailboxes; } connect() { return __awaiter(this, void 0, void 0, function* () { try { this.logger.log(`Trying to connect to <${this.config.auth.user}>...`, 'white'); this.client = new imapflow_1.ImapFlow(this.config); yield this.client.connect(); this.logger.log(`Connected to <${this.config.auth.user}>`, 'green'); } catch (e) { this.restart(); } }); } restart() { this.logger.log(`Could not connect to <${this.config.auth.user}>, check credentials and host`, 'red'); setTimeout(this.run.bind(this), this.config.reconnectInterval || DEFAULT_RECONNECT_INTERVAL); } watchErrors() { return __awaiter(this, void 0, void 0, function* () { this.client.on('error', () => { const timeout = (this.config.reconnectInterval || DEFAULT_RECONNECT_INTERVAL) / 1000; this.logger.log(`Error detected. Restarting in ${timeout} seconds...`, 'red'); setTimeout(this.run.bind(this), timeout); }); }); } loadMailboxes() { return __awaiter(this, void 0, void 0, function* () { const listResponses = yield this.client.list(); for (const list of listResponses) { const mailbox = { path: list.path, lastUid: yield this.getLastUid(list.path, true), }; this.mailboxes.set(mailbox.path, mailbox); } this.logger.log(`Loaded ${this.mailboxes.size} mailboxes for <${this.config.auth.user}>`, 'green'); }); } watchMailboxes() { return __awaiter(this, void 0, void 0, function* () { if (this.config.mailboxesToWatch) { for (const mailboxToWatch of this.config.mailboxesToWatch) { this.logger.log(`Watching mailbox [${mailboxToWatch}] every ${(this.config.mailboxesWatchInterval || DEFAULT_MAILBOXES_INTERVAL) / 1000} seconds for <${this.config.auth.user}>`, 'yellow'); yield this.watchMailbox(mailboxToWatch); setInterval(this.watchMailbox.bind(this, mailboxToWatch), this.config.mailboxesWatchInterval || DEFAULT_MAILBOXES_INTERVAL); } } }); } watchMailbox(mailboxToWatch) { return __awaiter(this, void 0, void 0, function* () { try { const mailbox = this.mailboxes.get(mailboxToWatch); if (mailbox) { const mails = yield this.getLastMails(mailbox); if (mails && mails.length > 0) { mailbox.lastUid = mails[mails.length - 1].uid; this.mailboxes.set(mailbox.path, mailbox); for (const mail of mails) { this.eventEmitter.emit('newMail', mail); } } } } catch (e) { this.logger.log(`Error while watching "${mailboxToWatch}" for <${this.config.auth.user}>, but still watching`, 'yellow'); } }); } getNewMail(mailboxPath, msg) { return __awaiter(this, void 0, void 0, function* () { const headers = yield mailparser_1.default.simpleParser(msg.headers); const bodyPart = msg.bodyParts.get('text'); let parsedMail = headers; if (bodyPart) { const body = yield mailparser_1.default.simpleParser(bodyPart); parsedMail = Object.assign(headers, body); } return new Mail_1.default(this, mailboxPath, msg, parsedMail); }); } getLastUid(mailboxPath, loading = false) { return __awaiter(this, void 0, void 0, function* () { let lastUid = 1; for (const msg of yield this.search(mailboxPath, `1:*`)) { if (msg.uid > lastUid) { lastUid = msg.uid; } if (loading) { const mail = yield this.getNewMail(mailboxPath, msg); this.eventEmitter.emit('loadedMail', mail); } } return lastUid; }); } getLastMails(mailbox) { return __awaiter(this, void 0, void 0, function* () { const mails = []; for (const msg of yield this.search(mailbox.path, `1:*`)) { if (msg.uid > mailbox.lastUid) { const mail = yield this.getNewMail(mailbox.path, msg); mails.push(mail); } } return mails; }); } search(mailboxPath, range) { var e_1, _a; return __awaiter(this, void 0, void 0, function* () { const msgs = []; const mailbox = yield this.client.getMailboxLock(mailboxPath); if (mailbox) { try { for (var _b = __asyncValues(this.client.fetch(range, { uid: true, headers: true, bodyParts: ['TEXT'], })), _c; _c = yield _b.next(), !_c.done;) { let msg = _c.value; msgs.push(msg); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) yield _a.call(_b); } finally { if (e_1) throw e_1.error; } } mailbox.release(); } return msgs; }); } } exports.default = Imap;