node simple-imap | node imap client | test search messages imap | Search

This code provides a function to programmatically search and retrieve emails from an IMAP server, allowing for filtering by sender, subject, date range, and mailbox.

Run example

npm run import -- "search for messages using simple-imap"

search for messages using simple-imap

var chrono = require('chrono-node');
var importer = require('../Core');
var imapClient = importer.import("node imap client");
var util = require('util');

function flatten(messages) {
    return messages.map(message => {
        var header = message.parts.filter(function (part) {
            return part.which.indexOf('HEADER') > -1;
        });
        var text = message.parts.filter(function (part) {
            return part.which.indexOf('TEXT') > -1;
        });
        var subject = header[0].body.subject[0];
        var from = header[0].body.from[0];
        var to = header[0].body.to[0];
        var date = header[0].body.date[0];
        from = ((/.+<(.*?)>/ig).exec(from) || [])[1] || from;
        to = ((/.+<(.*?)>/ig).exec(to) || [])[1] || to;
        return {
            id: message.attributes.uid,
            email: from,
            subject,
            body: text[0].body,
            date: chrono.parseDate(date),
            to
        };
    })
}

function searchImap(from = '*', subject = '*', days = 7, mailbox = 'INBOX') {
    // Fetch emails from the last number of {days}
    var delay = 24 * 3600 * 1000 * days;
    var lastWeek = new Date();
    lastWeek.setTime(Date.now() - delay);
    lastWeek = lastWeek.toISOString();
    var searchCriteria = [['SINCE', lastWeek]];
    if(from !== '*') {
        searchCriteria.push(['FROM', from])
    }
    if(subject !== '*') {
        searchCriteria.push(['SUBJECT', subject])
    }
    var fetchOptions = {
        bodies: ['HEADER.FIELDS (FROM TO SUBJECT DATE)', 'TEXT'],
        struct: true
    };
    var imap;
    return imapClient()
        .then(c => {
            imap = c;
            if(mailbox !== 'INBOX') {
                return util.promisify(imap.openBox.bind(imap))(mailbox)
            }
        })
        .then(() => util.promisify(imap.search.bind(imap))(searchCriteria, fetchOptions))
        .then(r => {
            r.flatten = () => flatten(r);
            return r;
        })
        .catch(e => console.log(e))
}

module.exports = searchImap;

What the code could have been:

const chrono = require('chrono-node');
const { openBox, search, searchCriteria } = require('node-imap-client');
const { promisify } = require('util');
const moment = require('moment');

/**
 * Flattens an array of email messages into a simple object format
 * @param {Object[]} messages - Array of email messages
 * @returns {Object[]} - Array of flattened email objects
 */
function flatten(messages) {
    // Use array.map to create a new array of flattened email objects
    return messages.map(message => {
        // Extract the email header and body parts
        const header = message.parts.find(part => part.which.indexOf('HEADER') > -1);
        const text = message.parts.find(part => part.which.indexOf('TEXT') > -1);

        // Extract email metadata from the header
        const subject = header.body.subject[0];
        const from = header.body.from[0];
        const to = header.body.to[0];
        const date = header.body.date[0];

        // Parse email addresses and dates
        from = ((/.+<(.*?)>/ig).exec(from) || [])[1] || from;
        to = ((/.+<(.*?)>/ig).exec(to) || [])[1] || to;
        date = chrono.parseDate(date);

        // Return the flattened email object
        return {
            id: message.attributes.uid,
            email: from,
            subject,
            body: text.body,
            date,
            to
        };
    });
}

/**
 * Searches an IMAP mailbox for emails based on specified criteria
 * @param {string} [from='*'] - Email address to search by
 * @param {string} [subject='*'] - Email subject to search by
 * @param {number} [days=7] - Number of days to search back in time
 * @param {string} [mailbox='INBOX'] - IMAP mailbox to search
 * @returns {Promise} - Promise resolving to an array of flattened email objects
 */
async function searchImap({ from = '*', subject = '*', days = 7, mailbox = 'INBOX' } = {}) {
    try {
        // Calculate the search date based on the number of days
        const delay = 24 * 3600 * 1000 * days;
        const lastWeek = moment().subtract(days, 'days').toDate();
        const lastWeekIso = lastWeek.toISOString();

        // Create the search criteria
        const searchCriteria = searchCriteria([
            ['SINCE', lastWeekIso]
        ]);

        // Add search criteria for email address and subject if specified
        if (from!== '*') {
            searchCriteria.push(['FROM', from]);
        }
        if (subject!== '*') {
            searchCriteria.push(['SUBJECT', subject]);
        }

        // Set up the fetch options
        const fetchOptions = {
            bodies: ['HEADER.FIELDS (FROM TO SUBJECT DATE)', 'TEXT'],
            struct: true
        };

        // Initialize and connect to the IMAP client
        const imap = await promisify(openBox.bind(imapClient))(mailbox);

        // Search for emails based on the specified criteria
        const searchResult = await promisify(search.bind(imap))(searchCriteria, fetchOptions);

        // Flatten the email search result
        searchResult.flatten = () => flatten(searchResult);

        // Return the flattened email array
        return searchResult;
    } catch (e) {
        console.error(e);
        return Promise.reject(e);
    }
}

module.exports = searchImap;

This code defines a function searchImap that retrieves emails from an IMAP server based on specified criteria.

Here's a breakdown:

  1. Dependencies:

  2. flatten Function:

  3. searchImap Function:

In essence, this code provides a way to programmatically search and retrieve emails from an IMAP server based on customizable criteria.