byeokim / gmailpush

Gmail API push notification handler for Node.js
MIT License
54 stars 7 forks source link

Gmailpush initialize function not getting called using example code #11

Open aivantg opened 3 years ago

aivantg commented 3 years ago

I followed the example code provided line for line, but it didn't seem like the topic was ever getting watched (and no history file was created). I debugged a little bit, and I don't think this._initialize() is being called using the setup that was provided in the readme. I'm sure I'm missing something simple, but any help on this?

From poking around the source code, I noticed that the initialize function takes in a notification, but I'm still not sure when it's meant to be called 🤔

aivantg commented 3 years ago

Looking through your readme (amazing documentation btw 🙏 so many useful links for setting up the gmail API), I noticed this part: image I also noticed that the initialize function is called whenever getMessages() is called.

Does this mean we should start watching and calling initialize on our own to start the process? I'm also curious whether your repo handles the scheduling aspect too. You mention node-scheduler but that's not a dependency so I wasn't sure how it worked. An example would be amazing!

byeokim commented 3 years ago

amazing documentation btw 🙏 so many useful links for setting up the gmail API

Thank you. I put a good deal of efforts into writing this doc. By the way I think my choice of word initialize for _initialize() is somewhat misleading. As you noticed it is called every time getMessages() method is called, instead of being called once when Gmailpush is instantiated. But you don't have to call gmail.users.watch() or gmailpush._initialize() by yourself because Gmailpush deals with them on your behalf.

For Node Schedule it's not part of Gmailpush but you can add it to your code like example below, or do you think it should be included in Gmailpush?

const schedule = require('node-schedule');
const fs = require('fs').promises;

...

app.post(
  '/pubsub-push-endpoint',
  express.json(),
  (req, res) => {
    res.sendStatus(200);

    const email = gmailpush.getEmailAddress(req.body);
    const token = users.find((user) => user.email === email).token;

    gmailpush
      .getMessages({
        notification: req.body,
        token
      })
      .then((messages) => {
        console.log(messages);

        fs.readFile('./gmailpush_history.json')
          .then((result) => {
            const prevHistories = JSON.parse(result);
            const prevHistory = prevHistories.find((prevHistory) => prevHistory.emailAddress === email);
            schedule.scheduleJob(new Date(prevHistory.watchExpiration - 1000 * 60 * 30), async () => {
              prevHistory.watchExpiration = await gmailpush._refreshWatch();
              fs.writeFile('./gmailpush_history.json', JSON.stringify(prevHistories));
            });
          });
      })
      .catch((err) => {
        console.log(err);
      });
  }
);
aivantg commented 3 years ago

But you don't have to call gmail.users.watch() or gmailpush._initialize() by yourself because Gmailpush deals with them on your behalf.

I'm a little confused about this. From what I understand of Google's PubSub API, it doesn't start sending notifications to the subscriptions until a server starts watching the topic. But the example code doesn't start watching until there is a call to getMessages(), which can only happen when there is a notification. This means that gmailpush needs

  1. An initial call to watch() to kick it off. Without this, it will never start watching and thus never get a notification that triggers a call to getMessages().
  2. An inbox update once every 7 days so that it knows to make another call to watch(). Without this, getMessages() will never be called and the gmail API will stop sending updates.

I'm not sure of all the use-cases of a library like this, but in my opinion, there could be 2 convenient things the library could do to help with this:

What do you think? I might have some misconceptions about how this library works, so let me know if I'm overcomplicating this in my head! My use case is setting up a personal email watcher so that I can run scripts whenever I get an email in my personal inbox. That's why I want to start watching and never stop watching.

aivantg commented 3 years ago

I might be able to help with a basic implementation of this! I'm sure I could dive into the library and understand the basics in an hour or two. But given the sample code you wrote, I'm sure the easier fix that I suggested above could hopefully be implemented pretty quickly. Just let me know!

byeokim commented 3 years ago

For "1. An initial call to watch() to kick it off", I will provide refreshWatch() method. Although Gmail API documentation says that calling watch() first is necessary to be able to get notifications, in my experience it is not necessary to call watch(): Gmail push notifications are sent without my having called watch(). (There is another example of mismatch between documentation and actual workings which has not yet been solved.) And watch() is an email address-specific method. So kicking off the initial watch() call when Gmailpush gets instantiated would make initialization syntax backward incompatible.

For "2. An inbox update once every 7 days so that it knows to make another call to watch()", I will implement Node Schedule into Gmailpush to call watch() before watchExpiration.

Thank you for your offer! But I think this should be done without taking your time.

aivantg commented 3 years ago

Makes sense! So I can just use _refreshWatch() to call the initial and any recurring calls to watch() (before node schedule is built in). From my understanding, there's no need to mess with the json file as long as the watch is refreshed regularly enough that no messages are ever missed.

aivantg commented 3 years ago

Okay, I just looked through the code, and I think I understand what you are saying when you said that the watch() function being email-specific. I think a workaround I could use for my single-email instance is saving the body of a notification to serve as a dummy notification that I can use to call _initialize(). Then, I can easily call _refreshWatch() as frequently as daily using something like node-schedule.

byeokim commented 3 years ago

You don't have to call watch() manually if you think your inbox receives at least one email (including spams) for seven-day-period. Every activity on Gmail would be notified to your PubSub subscription end point. And if you've put getMessages() into your app.post() route without any conditionals filtering out invocation of getMessages(), because Gmailpush automatically calls watch() for every getMessages() method, watch expiration would extend for seven days whenever any activities occur on Gmail.

By the way, looking into my code I had written one year ago I realized that json file is not necessary and information contained in it could be attached to Gmailpush class. It could make this library more usable. So next version would be either 1.1.0 or 2.0.0.