Skip to content

Instantly share code, notes, and snippets.

@tonyxiao
Created September 8, 2021 03:56
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 tonyxiao/a1b44f0788d7106a036698f0c5d70f8d to your computer and use it in GitHub Desktop.
Save tonyxiao/a1b44f0788d7106a036698f0c5d70f8d to your computer and use it in GitHub Desktop.
// @ts-check
const lodash = require('lodash')
const addrs = require('email-addresses')
/**
*
* @param {*} lib
* @param {string} email Used for last update key
* @param {boolean} reset Whether to clear the key
* @returns
*/
module.exports.getNewEmails = async function (lib, email, reset = false) {
const kLastUpdate = `LAST_UPDATE-${email}`
// Retrieve last e-mail data, if we need it.
// Limit it to last day of e-mail at worst case (1d * 24h * 60m * 60s)
console.log(`[${email}] Fetching last e-mail info...`)
if (reset) {
await lib.utils.kv['@0.1.16'].clear({key: kLastUpdate})
}
const lastUpdate = await lib.utils.kv['@0.1.16'].get({
key: kLastUpdate,
defaultValue: {
internalDate: ((new Date().valueOf() / 1000) | 0) - 1 * 24 * 60 * 60,
historyId: 0,
messageId: '',
},
})
let internalDate = lastUpdate.internalDate
let historyId = lastUpdate.historyId
let messageId = lastUpdate.messageId
// Get Gmail messages after 1s before your last e-mail
// This is incase we got two emails within the same 1s, but somehow missed one
console.log(`[${email}] Retrieving Gmail messages...`, lastUpdate)
/** @type {{id: string, threadId: string}[]} */
const msgAndThreadIds = await lib.gmail.messages['@0.2.7'].list({
query: `after:${internalDate - 1}`,
labelIds: ['INBOX'],
includeSpamTrash: false,
})
console.log(`[${email}] Getting e-mail data for ${msgAndThreadIds.length} messages...`)
let messages = await Promise.all(
msgAndThreadIds.map(async ({id: messageId}) => {
/** @type {GmailFullMessage} */
const msg = await lib.gmail.messages['@0.2.7'].retrieve({
id: messageId,
format: 'FULL',
})
// Parse these to integers
// internalDate is in ms, so divide by 1000
msg.internalDate = Math.floor(parseInt(msg.internalDate) / 1000)
msg.historyId = parseInt(msg.historyId)
// Set our latest date / historyId
internalDate = Math.max(internalDate, msg.internalDate)
historyId = Math.max(historyId, msg.historyId)
if (historyId === msg.historyId) {
messageId = msg.id
}
const headers = lodash
.chain(msg.payload.headers)
.keyBy((h) => h.name.toLowerCase())
.mapValues((v) => v.value)
.value()
/** @type {string} */
// @ts-ignore
const fromAddress = addrs.parseOneAddress(headers.from).address
return {...msg, subject: headers.subject, fromAddress}
}),
)
// Remove any potential duplicates of e-mails we've been notified about
messages = messages.filter(
(msg) =>
msg.historyId > lastUpdate.historyId && msg.id !== lastUpdate.messageId,
)
if (messages.length > 0) {
// Update our last e-mail settings, to use on next check
await lib.utils.kv['@0.1.16'].set({
key: kLastUpdate,
value: {
internalDate: internalDate,
historyId: historyId,
messageId: messageId,
},
})
}
return messages
}
/**
*
@typedef {{
id: string
threadId: string
labelIds: string[]
snippet: string
payload: {
partId: string
mimeType: string
filename: string
headers: {
name: string
value: string
}[]
body: Body
parts: any[]
}
sizeEstimate: number
historyId: string
internalDate: any
}} GmailFullMessage
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment