chadxz / imap-simple

Wrapper over node-imap, providing a simpler api for common use cases
MIT License
245 stars 79 forks source link

Running 24/7 waiting for new emails #79

Closed bsantos09 closed 3 years ago

bsantos09 commented 4 years ago

Hi I've been trying to achieve this for days now using setInterval although somewhere around 4am if the email account doesn't receive any new email it will stop receiving? My question is can someone give me some pointers on how to run it 24/7 waiting for new emails the proper way. Maybe an example code?

Thank you!

bsantos09 commented 4 years ago

Current Code

var config = {
  imap: {
    user: process.env.USEREMAIL,
    password: process.env.PASSWORD,
    host: process.env.MAILSERVER,
    port: process.env.IMAPPORT,
    tls: true,
    authTimeout: 3000,
    keepalive: {
      interval: 500,
      idleInterval: 500,
      forceNoop: true,
    },
  },
};
function fetchEmails(connection) {
  var searchCriteria = ['UNSEEN'];
  var fetchOptions = {
    bodies: ['HEADER', 'TEXT', ''],
    markSeen: true,
  };
  connection.search(searchCriteria, fetchOptions).then(function (messages) {
    messages.forEach(function (item) {
      var all = _.find(item.parts, { which: '' });
      var id = item.attributes.uid;
      var idHeader = 'Imap-Id: ' + id + '\r\n';
      simpleParser(idHeader + all.body, async (err, mail) => {});
    });
  });
}
imaps.connect(config).then(function (connection) {
  return connection.openBox('INBOX').then(function () {
    setInterval(() => {
      fetchEmails(connection);
    }, 100);
  });
});
ghost commented 4 years ago

Use onmail handler to handle new mails arrived in the currently opened box. Use onerror and onclose to handle connection interruption. Very likely your ISP force you offline to swap your IP address every day 4AM.

krynble commented 3 years ago

Hello @Plausible0Bug I have been facing similar issues and I am not sure how I should handle these errors.

My goal is to auto reconnect whenever possible and keep it running forever.

Any tips on how to handle auto reconnect strategies?

ghost commented 3 years ago

I'm not sure the exact way you want to auto reconnect since you did not provide any code snippet. I'm assuming you would be fine with my implementation.

My application is designed to be running in clustered environment where unlimited instances would be running and restart of any instance will be handled by process monitor, for example, systemd or docker restart policy.

By far my application has been running more than a year and the average pod life span is over 6 month, I don't think there are any major problem in my current implementation.

If you have similar setup, simply make sure you kill the process whenever error is encountered:

const init = () => {
  const cleanUpAndExitWithCode = (code = 1, connection = null, cb = null) => {
    // check if connection is an instance of imaps
    if (!!connection && connection.prototype === imaps.prototype) {
      connection.closeBox(false).catch(error); // Pass in false to avoid delete-flagged mails being removed
      connection.end();
    }
    if (typeof cb === 'function') {
      cb();
    }
    process.exit(code);
  };
  const onerror = (err) => {
    cleanUpAndExitWithCode(1, null, () => error(`Server connection has encountered an error: `, err));
  };
  const onalert = (alert) => {
    info(`Server has sent an alert: `, alert);
  };
  const onclose = (closeOnError) => {
    if (!!closeOnError) {
      cleanUpAndExitWithCode(1, null, () => error(`Server connection closed because of an error`));
    }
  };
  const onend = () => {
    cleanUpAndExitWithCode(1, null, () => error(`Server connection ended by remote`));
  };
  imaps.connect(imapConfig).then((connection) => {
    connection.on('error', onerror);
    connection.on('alert', onalert);
    connection.on('close', onclose);
    connection.on('end', onend);

    const signalHandler = (signal) => {
      info(`${signal} signal received, closing...`);
      cleanUpAndExitWithCode(0, connection, () => info(`Goodbye`));
    };

    process.on('SIGINT', signalHandler);
    process.on('SIGTERM', signalHandler);

=== OMITTED ===

Above code come with absolutely NO warranty that it will work as expected, it is purely demo purpose and should not be used directly in production environment.

patryk-matis commented 3 years ago

I'm not sure the exact way you want to auto reconnect since you did not provide any code snippet. I'm assuming you would be fine with my implementation.

My application is designed to be running in clustered environment where unlimited instances would be running and restart of any instance will be handled by process monitor, for example, systemd or docker restart policy.

By far my application has been running more than a year and the average pod life span is over 6 month, I don't think there are any major problem in my current implementation.

If you have similar setup, simply make sure you kill the process whenever error is encountered:

const init = () => {
  const cleanUpAndExitWithCode = (code = 1, connection = null, cb = null) => {
    // check if connection is an instance of imaps
    if (!!connection && connection.prototype === imaps.prototype) {
      connection.closeBox(false).catch(error); // Pass in false to avoid delete-flagged mails being removed
      connection.end();
    }
    if (typeof cb === 'function') {
      cb();
    }
    process.exit(code);
  };
  const onerror = (err) => {
    cleanUpAndExitWithCode(1, null, () => error(`Server connection has encountered an error: `, err));
  };
  const onalert = (alert) => {
    info(`Server has sent an alert: `, alert);
  };
  const onclose = (closeOnError) => {
    if (!!closeOnError) {
      cleanUpAndExitWithCode(1, null, () => error(`Server connection closed because of an error`));
    }
  };
  const onend = () => {
    cleanUpAndExitWithCode(1, null, () => error(`Server connection ended by remote`));
  };
  imaps.connect(imapConfig).then((connection) => {
    connection.on('error', onerror);
    connection.on('alert', onalert);
    connection.on('close', onclose);
    connection.on('end', onend);

    const signalHandler = (signal) => {
      info(`${signal} signal received, closing...`);
      cleanUpAndExitWithCode(0, connection, () => info(`Goodbye`));
    };

    process.on('SIGINT', signalHandler);
    process.on('SIGTERM', signalHandler);

=== OMITTED ===

Above code come with absolutely NO warranty that it will work as expected, it is purely demo purpose and should not be used directly in production environment.

Sorry for bothering you, but could you take a look at my issue? https://github.com/chadxz/imap-simple/issues/91 I don't know why onend/onclose/onerror is not working in my case?

ghost commented 3 years ago

@Krulaks please read my code carefully, the event handlers are NOT a part of config.

patryk-matis commented 3 years ago

@Krulaks please read my code carefully, the event handlers are NOT a part of config.

You are definitely right. I was mistaken that I can use onend/close handlers exactly as onupdate/onmail. Everything works fine. Thanks!