botmasterai / botmaster-messenger

The Facebook Messenger Botmaster integration
MIT License
15 stars 9 forks source link

Support multiple pages per messenger bot #5

Closed jdwuarin closed 6 years ago

jdwuarin commented 7 years ago

Redirecting: https://github.com/botmasterai/botmaster/issues/27

jdwuarin commented 7 years ago

I've had a similar issue as yours @andremw a couple of times. But it always turned out that it was some issue regarding my credentials. Are you sure that you have properly set different pages and Apps and that those are properly used to create your messengerBot instances?

andremw commented 7 years ago

I created one app on Facebook and set one webhook to listen to both pages (I mean, in the credentials I reused the same webhook - because in the fb app page I can only configure one webhook URL - but with different pageToken).

I think that maybe the reply() is trying to send the response for the wrong messenger bot instance, and the sender ids don't match. I haven't debugged though. Does it make sense?

jdwuarin commented 7 years ago

No, that's definitely not what's happening. There is no storage going on within an incoming-outgoing middleware flow (unless you installed botmaster-session-ware, but even then, it wouldn't make any sense). Are you sure you have properly subscribed to both pages in your app dashboard? And that both users (I assume you are using two different accounts to send your messages as per the object definition in your initial issue where I see: "sender":{"id":"1354592824626036"} once and "sender":{"id":"1562151773797420"} the second time). If those were the same accounts, you would get the same sender.id here

andremw commented 7 years ago

I'm sending the messages from the same account. And the app is subscribed to both pages, and I can see both messages logged below.

But I noticed on thing in Facebook's webhook reference doc:

"When representing a user, these IDs are page-scoped IDs (PSID). This means that the IDs of users are unique for a given page." I think this explains the different ids for the same sender. Doesn't it?

The code is basically this:

botmaster.addBot(new MessengerBot({
  credentials: {
    verifyToken: '[verifyToken]',
    pageToken: '[page1Token]', // this is specific for the page
    fbAppSecret: '[appSecret]'
  },
  webhookEndpoint: 'webhook1234'
}));

botmaster.addBot(new MessengerBot({
  credentials: {
    verifyToken: '[verifyToken]',
    pageToken: '[page2Token]', // this is specific for the page
    fbAppSecret: '[appSecret]'
  },
  webhookEndpoint: 'webhook1234'
}));

botmaster.use({
  type: 'incoming',
  name: 'My incoming middleware',
  controller: (bot, update) => {
    console.log(`Hey! I got something for you: ${JSON.stringify(update)}`);
    return bot.reply(update, 'Hello world');
  }
});
jdwuarin commented 7 years ago

Right. Thanks for the code snippet. I couldn't really see what was going on before seeing that. Now the issue is obvious. The problem is that FB Messenger is trying to hit the same webhook endpoint with two different pageTokens. I.E. as two different recipients (the bot is the recipient when getting the update). However, you can't really do that out of the box. As the first endpoint setup is the one that will have priority. This means that whenever a request comes into this endPoint, it will think that the first bot was spoken to, even though the second one might have been.

It seems that what you want to do is have one bot with the exact same code run on two different pages. An enhancement would have to be made to this package in order to do that. Working code would then look something like this:

botmaster.addBot(new MessengerBot({
  credentials: {
    verifyToken: '[verifyToken]',
    pages: {
      [pageId1]: { pageToken: 'pageToken1' },
      [pageId2]: { pageToken: 'pageToken2' },
      etc...
    },
    fbAppSecret: '[appSecret]'
  },
  webhookEndpoint: 'webhook1234'
}));
botmaster.use({
  type: 'incoming',
  name: 'My incoming middleware',
  controller: (bot, update) => {
    console.log(`Hey! I got something for you: ${JSON.stringify(update)}`);
    return bot.reply(update, 'Hello world');
  }
});

By doing that, in the sendMessage function, we would then be able to find out what pageToken to use and change the code to something like:

  __sendMessage(message) {
    const options = {
      uri: baseMessageURL,
      qs: { access_token: this.credentials.pages[message.sender.id].pageToken },
      method: 'POST',
      json: message,
    };

    return request(options);
  }

This would also need to be done for: _messengerProfileRequest and __getUserInfo. On top of that, because each page has a different id (the update.recipient.id when receiving an update and the message.sender.id when sending), the bot.id value would have to be changed to either be the same as the new pages value, or probably more like the appId (seems like that would make more sense).

To set it to the appId, means that developers can set it to anything they want. Which I'm fine with (and is technically already possible).

=> to conclude. In order to remain backward compatible, the update would have to make it so that the constructor can take either something that looks like this:

botmaster.addBot(new MessengerBot({
  credentials: {
    verifyToken: '[verifyToken]',
    pages: {
      [pageId1]: { pageToken: 'pageToken1' },
      [pageId2]: { pageToken: 'pageToken2' },
      etc...
    },
    fbAppSecret: '[appSecret]'
  },
  webhookEndpoint: 'webhook1234',
  id: [appId_or_anything_I_want_really] // compulsory if pages is defined
}));

or

botmaster.addBot(new MessengerBot({
  credentials: {
    verifyToken: '[verifyToken]',
    pageToken: '[pageToken]', // for backwards compatibility
    fbAppSecret: '[appSecret]'
  },
  webhookEndpoint: 'webhook1234',
  id: [appId_or_anything_I_want_really] // not compulsory in this case (again, for backward compatibility)
}));

If that's something that is important to you, you can have a stab at a pull request to to this (having a go at doing some tests for that would be nice too). Or you can wait (probably a couple weeks or so for me to get around to having the time to do that). Or you can just create another app for the other page that points to a different endpoint on the same server.

andremw commented 7 years ago

Thanks a lot for taking your time to analyze the issue and explain these things. I'll definitely take a look on that. Would be a great feature to have in the module.

Rikhart commented 6 years ago

Hi, any advance about this feature¿?.

andremw commented 6 years ago

Unfortunately not. I commented on https://github.com/botmasterai/botmaster-messenger/pull/7#issuecomment-329933845

jdwuarin commented 6 years ago

Fixed by: 477650a77d18a5bf104562bdb66b734f57bd97e3. Have a look at the updated Readme to see how to use.