Use node-imap to retrieve emails - node.js

Here is a module retrieve_email.js which connects to my gmail account and download the UNSEEN emails after a date. The code is pretty much copied from the example of the [imap module]1.
const Imap = require('imap');
const inspect = require('util').inspect;
const simpleParser = require('mailparser').simpleParser;
const imap = new Imap({
user: 'mygmail#gmail.com',
password: 'mypassword',
host: 'imap.gmail.com',
port: 993,
tls: true
});
function openInbox(callback) {
imap.openBox('INBOX', true, callback);
};
async function parse_email(body) {
let parsed = simpleParser(body);
...............
};
module.exports = function() {
imap.once('ready', function() {
openInbox(function(err, box) {
if (error) throw err;
imap.search(['UNSEEN', ['SINCE', 'May 20, 2018']], function(err, results){
if (err) throw err;
var f = imap.fetch(results, {bodies: ''});
f.on('message', function(msg, seqno) {
console.log('Message #%d', seqno);
var prefix = '(#' + seqno + ') ';
msg.on('body', function(stream, info) {
if (info.which === 'TEXT')
console.log(prefix + 'Body [%s] found, %d total bytes', inspect(info.which), info.size);
var buffer = '', count = 0;
stream.on('data', function(chunk) {
count += chunk.length;
buffer += chunk.toString('utf8');
parse_email(buffer);
if (info.which === 'TEXT')
console.log(prefix + 'Body [%s] (%d/%d)', inspect(info.which), count, info.size);
});
stream.once('end', function() {
if (info.which !== 'TEXT')
console.log(prefix + 'Parsed header: %s', inspect(Imap.parseHeader(buffer)));
else
console.log(prefix + 'Body [%s] Finished', inspect(info.which));
});
});
msg.once('attributes', function(attrs) {
console.log(prefix + 'Attributes: %s', inspect(attrs, false, 8));
});
msg.once('end', function() {
console.log(prefix + 'Finished');
});
});
f.once('error', function(err) {
console.log('Fetch error: ' + err);
});
f.once('end', function() {
console.log('Done fetching all messages');
imap.end();
});
});
});
});
imap.once('error', function(err) {
console.log(err);
});
imap.once('end', function() {
console.log('Connection ended');
});
imap.connect();
};
When the module is called in index.js, I can see in debug that code is scanned from top to the bottom and the last line of code scanned is imap.connect() and then back to the next line in index.js, with no connection to the gmail account and no action of retrieving the emails. What is wrong with the code above?
UPDATED: status after socket.connect() in debug:

Have a look at this, this is the Gmail API reference from Google. On that page there is an example of how to connect to it using Node.js.
https://developers.google.com/gmail/api/quickstart/nodejs
And here is an example from the same docs that show you how to search and retrieve message list using the q parameter:
https://developers.google.com/gmail/api/v1/reference/users/messages/list
P.S. In my comment i was just asking you if you were sure that you did all the other configuration stuff needed to access your Gmail account by code, meaning creating the app, authorizing OAuth or in what seemed to be your case authorizing less secure application access, just have a look at the links you might find that you are missing something.
And do you really need to use IMAP package ???

The problem found was with Avast mail shield as middle man intercepting the IMAP traffic and causes the HTTPS fails. Also the IDE debugger stops at somewhere to keep the connecting active but not ready. Here is the detail of the solution.

Related

Searching for emails with specific string in subject with Node-IMAP

My goal is to use IMAP to retrieve and display emails (pushed to an array called 'email') from a Gmail account as long as they have a specific string in the subject line. This is my first time using Node-IMAP. So far, I am able to return all emails found in the inbox, but as soon as I try to use imap.search it returns an empty array. I assume I'm missing something, but I can't figure out what.
var email = [];
imap.once('ready', function() {
openInbox(function(err, box) {
if (err) throw err;
imap.search([[ 'HEADER', 'SUBJECT', 'ABCDEFG'] ], function(err, results) {
if (err) throw err;
var f = imap.seq.fetch(results, {
bodies: 'HEADER.FIELDS (FROM SUBJECT DATE)',
});
f.on('message', function(msg, seqno) {
var prefix = '(#' + seqno + ') ';
msg.on('body', function(stream, info) {
var buffer = '';
stream.on('data', function(chunk) {
buffer += chunk.toString('utf8');
});
stream.once('end', function() {
email.push(prefix + inspect(Imap.parseHeader(buffer)));
});
});
});

Check Mailbox in a Loop, Read Email and Create PDF

I have a code that reads unseen emails and creates pdf.
The problem is;
I cannot pull email if any new unseen email exist without executing code again.
var Imap = require('imap');
const MailParser = require('mailparser').MailParser;
var pdf = require('html-pdf');
var fs = require('fs');
var Promise = require("bluebird");
Promise.longStackTraces();
var imapConfig = {
user: '*****',
password: '*****',
host: 'imap.gmail.com',
port: 993,
tls: true
};
var imap = new Imap(imapConfig);
Promise.promisifyAll(imap);
imap.once("ready", execute);
imap.once("error", function(err) {
log.error("Connection error: " + err.stack);
});
imap.connect();
function execute() {
imap.openBox("INBOX", false, function(err, mailBox) {
if (err) {
console.error(err);
return;
}
imap.search(["UNSEEN"], function(err, results) {
if(!results || !results.length){console.log("No unread mails");imap.end();return;}
var f = imap.fetch(results, { bodies: "" });
f.on("message", processMessage);
f.once("error", function(err) {
return Promise.reject(err);
});
f.once("end", function() {
console.log("Done fetching all unseen messages.");
imap.end();
});
});
});
}
const options = { format: 'A2', width:"19in", height:"17in", orientation: "portrait" };
function processMessage(msg, seqno) {
console.log("Processing msg #" + seqno);
// console.log(msg);
var parser = new MailParser();
parser.on("headers", function(headers) {
console.log("Header: " + JSON.stringify(headers));
});
parser.on('data', data => {
if (data.type === 'text') {
console.log(seqno);
console.log(data.html); /* data.html*/
var test = data.html
pdf.create(test, options).toStream(function(err, stream){
stream.pipe(fs.createWriteStream('./foo.pdf'));
});
}
});
msg.on("body", function(stream) {
stream.on("data", function(chunk) {
parser.write(chunk.toString("utf8"));
});
});
msg.once("end", function() {
// console.log("Finished msg #" + seqno);
parser.end();
});
}
Also I have tried to use setInterval to check new unseen emails but I get
'Error: Not authenticated'
How can I pull new unseen emails in a loop and create pdf from that email?
Your observation is correct. You must poll your IMAP (or POP3) server on a regular schedule to keep up with incoming messages. Depending on your requirements, a schedule of once every few minutes is good.
continous polling, or polling every second or so, is very rude. The operator of the IMAP server may block your application if you try to do that: it looks to them like an attempt to overload the server.

I'm using IMAP npm library for fetching mails which says "invalid credentials"

I am using IMAP library to fetch email from gmail. I got reference from Read email body with node js imap and https://www.npmjs.com/package/imap.
My implementation is as following:
var Imap = require('imap'),
inspect = require('util').inspect;
var imap = new Imap({
user: 'xxx#gmail.com',
password: 'xxxxxx',
host: 'imap.gmail.com',
port: 993,
tls: true
});
function openInbox(cb) {
imap.openBox('INBOX', true, cb);
}
imap.once('ready', function() {
openInbox(function(err, box) {
if (err) throw err;
var f = imap.seq.fetch('1:3', {
bodies: 'HEADER.FIELDS (FROM TO SUBJECT DATE)',
struct: true
});
f.on('message', function(msg, seqno) {
console.log('Message #%d', seqno);
var prefix = '(#' + seqno + ') ';
msg.on('body', function(stream, info) {
var buffer = '';
stream.on('data', function(chunk) {
buffer += chunk.toString('utf8');
});
stream.once('end', function() {
console.log(prefix + 'Parsed header: %s', inspect(Imap.parseHeader(buffer)));
});
});
msg.once('attributes', function(attrs) {
console.log(prefix + 'Attributes: %s', inspect(attrs, false, 8));
});
msg.once('end', function() {
console.log(prefix + 'Finished');
});
});
f.once('error', function(err) {
console.log('Fetch error: ' + err);
});
f.once('end', function() {
console.log('Done fetching all messages!');
imap.end();
});
});
});
imap.once('error', function(err) {
console.log(err);
});
imap.once('end', function() {
console.log('Connection ended');
});
imap.connect();
Note: username/password is correct and I had enable IMAP/POP in my gmail account but still I'm getting following error:
{ Error: Invalid credentials (Failure)
at Connection._resTagged (xxx/server/node_modules/imap/lib/Connection.js:1502:11)
at Parser.<anonymous> (xxx/server/node_modules/imap/lib/Connection.js:194:10)
at Parser.emit (events.js:193:13)
at Parser._resTagged (xxx/server/node_modules/imap/lib/Parser.js:175:10)
at Parser._parse (xxx/server/node_modules/imap/lib/Parser.js:139:16)
at Parser._tryread (xxx/server/node_modules/imap/lib/Parser.js:82:15)
at TLSSocket.Parser._cbReadable (xxx/server/node_modules/imap/lib/Parser.js:53:12)
at TLSSocket.emit (events.js:193:13)
at emitReadable_ (_stream_readable.js:550:12)
at processTicksAndRejections (internal/process/task_queues.js:81:17)
type: 'no',
textCode: 'AUTHENTICATIONFAILED',
source: 'authentication' }
Connection ended
Note: If using a Gmail account, you will need to setup oAuth:
Generatting access token and fetching process in detailed here.
For anyone coming to this thread, if the previous recommendations do not help resolve your problem check your script to ensure that you have properly closed your IMAP session with imap.end().
In the above example imap.end() gets triggers if 'f' is enacted upon, reads messages and then 'ends'.
f.once('end', function() {
console.log('Done fetching all messages!');
imap.end();
});
However, if there are no messages, there is no other place in the script to handle closing the IMAP connection (see below):
imap.once('error', function(err) {
console.log(err);
});
imap.once('end', function() {
console.log('Connection ended');
});
imap.connect();
This will leave the session open. Most email systems have a max of 20 IMAP connections allowed. On the 21st try your script will receive a login failure even though the email and password are correct.
The IMAP block is temporary but could last many hours. Wait it out, fix the script and try again.
Set up 2 factor auth and provide app password for your gmail account.
Use that generated app password on your Imap passwords.
Worked or me!!

How to fetch msg body using node-imap module

I'm using node-imap npm module to fetch the email and i'm getting pretty everything but the content of msg.
I used an already provided example on their git, shown below
openInbox(function(err, box) {
if (err) throw err;
var f = imap.seq.fetch(box.messages.total + ':*', { bodies: ['HEADER.FIELDS (FROM)','TEXT'] });
f.on('message', function(msg, seqno) {
console.log('Message #%d', seqno);
var prefix = '(#' + seqno + ') ';
msg.on('body', function(stream, info) {
if (info.which === 'TEXT')
console.log(prefix + 'Body [%s] found, %d total bytes', inspect(info.which), info.size);
var buffer = '', count = 0;
stream.on('data', function(chunk) {
count += chunk.length;
buffer += chunk.toString('utf8');
console.log('BUFFER', buffer) //HEre i am able to view the body
if (info.which === 'TEXT')
console.log(prefix + 'Body [%s] (%d/%d)', inspect(info.which), count, info.size);
});
stream.once('end', function() {
if (info.which !== 'TEXT')
console.log(prefix + 'Parsed header: %s', inspect(Imap.parseHeader(buffer)));
else
console.log(prefix + 'Body [%s] Finished', inspect(info.which));
});
});
msg.once('attributes', function(attrs) {
console.log(prefix + 'Attributes: %s', inspect(attrs, false, 8));
});
msg.once('end', function() {
console.log(prefix + 'Finished');
});
});
f.once('error', function(err) {
console.log('Fetch error: ' + err);
});
f.once('end', function() {
console.log('Done fetching all messages!');
imap.end();
});
});
In this example i am able to console out the content of the msg but i am unable to return it as json.
The returned value is a buffer. If you want a string, you can call the toString() method on the buffer. You said you want JSON. If the email body is properly formatted JSON you could also decode it:
var json = JSON.parse(buffer.ToString());

Read email body with node js imap

I'm trying to read the body of an e-mail that is retrieved with node js. I'm using this npm module: https://github.com/mscdex/node-imap
And I can get pretty much all the information of the email, except reading the content of the body.
Any ideas?
Thanks,
You can use this code to fetch email body and mark it as seen
var Imap = require("imap");
var MailParser = require("mailparser").MailParser;
var Promise = require("bluebird");
Promise.longStackTraces();
var imapConfig = {
user: 'USERNAME',
password: 'PASSWORD',
host: 'HOST',
port: 993,
tls: true
};
var imap = new Imap(imapConfig);
Promise.promisifyAll(imap);
imap.once("ready", execute);
imap.once("error", function(err) {
log.error("Connection error: " + err.stack);
});
imap.connect();
function execute() {
imap.openBox("INBOX", false, function(err, mailBox) {
if (err) {
console.error(err);
return;
}
imap.search(["UNSEEN"], function(err, results) {
if(!results || !results.length){console.log("No unread mails");imap.end();return;}
/* mark as seen
imap.setFlags(results, ['\\Seen'], function(err) {
if (!err) {
console.log("marked as read");
} else {
console.log(JSON.stringify(err, null, 2));
}
});*/
var f = imap.fetch(results, { bodies: "" });
f.on("message", processMessage);
f.once("error", function(err) {
return Promise.reject(err);
});
f.once("end", function() {
console.log("Done fetching all unseen messages.");
imap.end();
});
});
});
}
function processMessage(msg, seqno) {
console.log("Processing msg #" + seqno);
// console.log(msg);
var parser = new MailParser();
parser.on("headers", function(headers) {
console.log("Header: " + JSON.stringify(headers));
});
parser.on('data', data => {
if (data.type === 'text') {
console.log(seqno);
console.log(data.text); /* data.html*/
}
// if (data.type === 'attachment') {
// console.log(data.filename);
// data.content.pipe(process.stdout);
// // data.content.on('end', () => data.release());
// }
});
msg.on("body", function(stream) {
stream.on("data", function(chunk) {
parser.write(chunk.toString("utf8"));
});
});
msg.once("end", function() {
// console.log("Finished msg #" + seqno);
parser.end();
});
}
hope this code will help you :)
The Body of the message is in the spot where i have console.log("BUFFER", buffer)
I'm using node-imap npm module
imap.once('ready', function() {
openInbox(function(err, box) {
if (err) throw err;
var f = imap.seq.fetch(box.messages.total + ':*', { bodies: ['HEADER.FIELDS (FROM)','TEXT'] });
f.on('message', function(msg, seqno) {
console.log('Message #%d', seqno);
var prefix = '(#' + seqno + ') ';
msg.on('body', function(stream, info) {
if (info.which === 'TEXT')
console.log(prefix + 'Body [%s] found, %d total bytes', inspect(info.which), info.size);
var buffer = '', count = 0;
stream.on('data', function(chunk) {
count += chunk.length;
buffer += chunk.toString('utf8');
console.log("BUFFER", buffer)
if (info.which === 'TEXT')
console.log(prefix + 'Body [%s] (%d/%d)', inspect(info.which), count, info.size);
});
stream.once('end', function() {
if (info.which !== 'TEXT')
console.log(prefix + 'Parsed header: %s', inspect(Imap.parseHeader(buffer)));
else
console.log(prefix + 'Body [%s] Finished', inspect(info.which));
});
});
msg.once('attributes', function(attrs) {
console.log(prefix + 'Attributes: %s', inspect(attrs, false, 8));
});
msg.once('end', function() {
console.log(prefix + 'Finished');
});
});
f.once('error', function(err) {
console.log('Fetch error: ' + err);
});
f.once('end', function() {
console.log('Done fetching all messages!');
imap.end();
});
});
});
Try:
msg.once('end', function() {
console.log(buffer);
});

Resources