guidone / node-red-contrib-chatbot

Visually build a full featured chat bot for Telegram, Facebook Messenger, Whatsapp and Slack with Node-RED. Almost no coding skills required.
http://red-bot.io
933 stars 189 forks source link

Facebook: Pass-thru for Messenger .. or second output pin? #217

Closed jflaflamme closed 6 years ago

jflaflamme commented 6 years ago

Hello @guidone

I saw the pass-thru branch and some work regarding different receivers but not for other types than messages. Any plans to make it work in a near future?

Otherwise, I was thinking to do another output pin for all unsupported event types in facebook receiver node. I forked and tried to implement via chat-platform.js to use a new function "unsupportedMessage" instead of inboundMessage but a bit stuck on the return part, seem not possible to return directly there :-)

chat-platform.js

  function unsupportedMessage(payload, chatServer) {
    if (chatServer.isDebug()) {
      // eslint-disable-next-line no-console
      console.log(orange('-- INBOUND MESSAGE --'));
      // eslint-disable-next-line no-console
      console.log(prettyjson.render(payload));
      // eslint-disable-next-line no-console
      console.log('');
    }
    return [null, payload]
  }

facebook-chat.js

        var unsupportedEvents = entry.changes; // for non-messenging (other webhooks...)
            _(unsupportedEvents).each(function (unsupportedEvent) {
              chatServer.receiveUnsupported(unsupportedEvent);
            }

I could use http request node but I would lose all the magic of the FB receiver node :) use-case: same webhook URL for page events and messenger.

Let me know your thoughts and if out of scope. Cheers for all the great work as usual!

guidone commented 6 years ago

Hi Jean-Francois,

I understand the need to extend the receiver, the chat-platform.js uses a pattern similar to express.js, it creates a server that can be expanded chaining middlewares. So for its own nature it can be extended, unfortunately the documentation is missing, but I can work on that.

I don’t like very much the idea of the second pin, the first reason is above, the second is that once the extended feature is included in the core of RedBot, it breaks older installation since the new message starts going through the first pin rather then the second catch-all pin.

I’ve a couple ideas about that, let’s catch up on monday and help me figure out which is the best.

Thanks for you interest in RedBot and have a good week end

Guido Il 22 giu 2018, 17:30 +0200, Jean-Francois Laflamme notifications@github.com, ha scritto:

Hello @guidone I saw the pass-thru branch and some work regarding different receivers but not for other types than messages. Any plans to make it work in a near future? Otherwise, I was thinking to do another output pin for all unsupported event types in facebook receiver node. I forked and tried to implement via chat-platform.js to use a new function "unsupportedMessage" instead of inboundMessage but a bit stuck on the return part, seem not possible to return directly there :-) chat-platform.js function unsupportedMessage(payload, chatServer) { if (chatServer.isDebug()) { // eslint-disable-next-line no-console console.log(orange('-- INBOUND MESSAGE --')); // eslint-disable-next-line no-console console.log(prettyjson.render(payload)); // eslint-disable-next-line no-console console.log(''); } return [null, payload] } facebook-chat.js var unsupportedEvents = entry.changes; // for non-messenging (other webhooks...) _(unsupportedEvents).each(function (unsupportedEvent) { chatServer.receiveUnsupported(unsupportedEvent); } I could use http request node but I would lose all the magic of the FB receiver node :) use-case: same webhook URL for page events and messenger. Let me know your thoughts and if out of scope. Cheers for all the great work as usual! — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.

guidone commented 6 years ago

Ciao Jean-Francois,

If you give a look at facebook-chat.js you see that every kind of input message is handled by middlewares

For example

// get plain text messages
Facebook.in(function(message) {
  return new Promise(function(resolve, reject) {
    var chatContext = message.chat();
    if (_.isString(message.originalMessage.message.text) && !_.isEmpty(message.originalMessage.message.text)) {
      message.payload.content = message.originalMessage.message.text;
      message.payload.type = 'message';
      when(chatContext.set('message', message.payload.content))
        .then(function () {
          resolve(message);
        }, function (error) {
          reject(error);
        });
    } else {
      resolve(message);
    }
  });
});

This intercepts the text message. It's possible to chain as many middleware as you like, the first middleware that resolve putting a value in message.payload.type skips the rest of the chain.

So handling unsupported message types it's just a matter of adding the right middleware. My idea is to put in the Facebook/Telegram/Slack configuration the same editor of the function node where it's possible to add middlewares

schermata 2018-06-26 alle 09 27 28

For example

server.in(function(message) {
  return new Promise(function(resolve, reject) {
    if (...) {
      message.payload.content = 'some content';
      message.payload.type = 'my-new-type';
    }
    resolve(message);
  });
}); 

In the similar way it's possible to expand for outgoing messages. It's quite flexible, it's even possible to add asynchronous request on inbound/outbound requests, for example to send tracking info to collect stats in an external platform.

And if at some point the changes are merged into the main project, nothing breaks.

What do you think?

jflaflamme commented 6 years ago

Grazie mille! Merci mille-fois!

I think it is a clever way of doing it without breaking any past/future message types and allowing new middlewares. That answer 100% of my problem and the problem also of debugging new message types in the future as a bonus.

I was currently running an express "reverse proxy" in front to send the request to the right endpoint depending on node-red on the content but I for sure prefer to keep this maintainable.

Happy to test a branch :-)

guidone commented 6 years ago

Hi,

where this entry.changes come from, I was not able to find them in the documentation?

var unsupportedEvents = entry.changes; // for non-messenging (other webhooks...)
            _(unsupportedEvents).each(function (unsupportedEvent) {
              chatServer.receiveUnsupported(unsupportedEvent);
            }
jflaflamme commented 6 years ago

Hello @guidone

This is part of the payload from Facebook.

https://developers.facebook.com/docs/graph-api/webhooks/

guidone commented 6 years ago

Hi, here is the beta

npm install node-red-contrib-chatbot@0.13.4-beta-1

Here some documentation

https://github.com/guidone/node-red-contrib-chatbot/wiki/Extend-node

Is adds a new node where it's possible to extend the connector.

In facebook messenger these messages (https://developers.facebook.com/docs/graph-api/webhooks/) are routed to the connector and can be handled by a middleware in the extend node

jflaflamme commented 6 years ago

Thanks for that @guidone that is the perfect approach.

However, I have errors even before going thru the extend node.

I think this condition should check of message exists before (in this case, it is a changes type)

I

if (_.isString(message.originalMessage.message.text) && !_.isEmpty(message.originalMessage.message.text)) {

Full debug info

-- INBOUND MESSAGE --
changes:
  -
    field: feed
    value:
      from:
        id:   1431850056628523
        name: Getloy
      item:         status
      post_id:      1431850056128523_1719052135208312
      verb:         add
      published:    0
      created_time: 1530235035
      message:      hello
id:      123456789
time:    1530235038

-------------------------- Error in chat-platform.js ---------------------------
TypeError: Cannot read property 'text' of undefined
    at /usr/src/node-red/node_modules/node-red-contrib-chatbot/lib/chat-platform/chat-platform.js:307:13
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)

function (message) {
  return new Promise(function(resolve, reject) {
    var chatContext = message.chat();
    if (_.isString(message.originalMessage.message.text) && !_.isEmpty(message.originalMessage.message.text)) {
      message.payload.content = message.originalMessage.message.text;
      message.payload.type = 'message';
      when(chatContext.set('message', message.payload.content))
        .then(function () {
          resolve(message);
        }, function (error) {
          reject(error);
        });
    } else {
      resolve(message);
    }
  });
}

29 Jun 01:17:22 - [error] [chatbot-facebook-node:a4313c6.1b49cc] TypeError: Cannot read property 'text' of undefined
    at Object.dump (/usr/src/node-red/node_modules/node-red-contrib-chatbot/lib/helpers/lcd.js:27:13)
    at /usr/src/node-red/node_modules/node-red-contrib-chatbot/lib/chat-platform/chat-platform.js:307:13
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)

I have created a pull request. https://github.com/guidone/node-red-contrib-chatbot/pull/219

guidone commented 6 years ago

Ok, I've cherry picked your commits, some check here and there and I'll merge.

I'd like to improve the list of handled message types in a way that if the user improve a platform by adding an handler for a message type, this type appears on the types dropdown in the Rules node. But I think I'll add this in the next branch

jflaflamme commented 6 years ago

Thank you for that, awesome collaboration, I am closing the issue :)