Skip to content

Instantly share code, notes, and snippets.

@jerbear2008
Created April 25, 2023 23:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jerbear2008/4e2e9383d454695e9810f531498b39c3 to your computer and use it in GitHub Desktop.
Save jerbear2008/4e2e9383d454695e9810f531498b39c3 to your computer and use it in GitHub Desktop.
import Imap from 'imap'
import { simpleParser } from 'mailparser'
import util from 'util'
// Modernized verison, with modern JS, async/await, util.promisify, etc.
class MailListener {
constructor({
username,
password,
host,
port,
tls,
tlsOptions = {},
connTimeout = null,
authTimeout = null,
debug = null,
xoauth2,
mailbox = 'INBOX',
searchFilter = ['UNSEEN'],
markSeen,
fetchUnreadOnStart,
attachments = false,
attachmentOptions = {},
mailParserOptions = {},
}) {
this.markSeen = markSeen
this.mailbox = mailbox
if ('string' === typeof searchFilter) {
this.searchFilter = [searchFilter]
} else {
this.searchFilter = searchFilter
}
this.fetchUnreadOnStart = fetchUnreadOnStart
this.mailParserOptions = mailParserOptions
if (attachments && attachmentOptions && attachmentOptions.stream) {
this.mailParserOptions.streamAttachments = true
}
this.attachmentOptions = attachmentOptions
this.attachments = attachments
this.attachmentOptions.directory = this.attachmentOptions.directory ?? ''
this.imap = new Imap({
xoauth2,
user: username,
password,
host,
port,
tls,
tlsOptions,
connTimeout,
authTimeout,
debug,
})
this.imap.once('ready', this.imapReady)
this.imap.once('close', this.imapClose)
this.imap.on('error', this.imapError)
}
start() {
this.imap.connect()
}
stop() {
this.imap.end()
}
imapReady() {
this.imap.openBox(this.mailbox, false, (err, mailbox) => {
if (err) {
this.emit('error', err)
} else {
this.emit('server:connected')
if (this.fetchUnreadOnStart) {
this.parseUnread()
}
this.imap.on('mail', this.imapMail)
this.imap.on('update', this.imapMail)
}
})
}
imapClose() {
this.emit('server:disconnected')
}
imapError(err) {
this.emit('error', err)
}
imapMail() {
this.parseUnread()
}
async parseUnread() {
// modern version without async library, using modern async/await syntax instead (with util.promisify to convert imap functions to promises)
const search = util.promisify(this.imap.search).bind(this.imap)
const fetch = util.promisify(this.imap.fetch).bind(this.imap)
const results = await search(this.searchFilter)
if (results.length === 0) return
for (const result of results) {
const fetchResult = await fetch(result, {
bodies: '',
markSeen: this.markSeen,
})
fetchResult.on('message', async (message, sequenceNumber) => {
let attributes = null
let emailBuffer = Buffer.from('')
message.on('attributes', (attrs) => {
attributes = attrs
})
const parsed = await simpleParser(emailBuffer)
this.emit('mail', parsed, sequenceNumber, attributes)
})
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment