Getting user data in Facebook Messenger

Hello everyone!

As I am currently working on a messenger bot, getting some of the user’s data would be pretty nice. It would allow to greet the user with his name etc. but the most important part for me would be getting the user’s id, in order to make use of the FB API to send templates and other stuff like that (e.g:Generic Template - Messenger Platform - Documentation - Facebook for Developers).

Since these templates require a recipient id(which would obviously be the user), I need to get that user’s ID and I can’t seem to understand how. Any help or ideas?

Thanks a lot :slight_smile:

Hi @geriskenderi,

you can use dispatcher.utter_button_message to send buttons to Messenger. Unfortunately, you can’t use the Generic template_type natively with the current version of rasa_core.

However, you can easily extend / customize the send_custom_message method of the MessengerBot class. Here’s how I modified it in order to create all kind of buttons Messenger has to offer. I created a file called facebook.py in my project directory under channels/facebook.py

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

import logging
from typing import Text, List, Dict, Any, Callable
import yaml
import os

from flask import Blueprint, request, jsonify
import requests
from rasa_core.channels.facebook import Messenger as FBMessenger, MessengerBot as FBMessengerBot, FacebookInput as FBFacebookInput, UserMessage

class Messenger(FBMessenger):
    """Implement a fbmessenger to parse incoming webhooks and send msgs."""

    def __init__(self, page_access_token, on_new_message):
        super(Messenger, self).__init__(page_access_token, on_new_message)

    def _handle_user_message(self, text, sender_id):
        # type: (Text, Text) -> None
        """Pass on the text to the dialogue engine for processing."""

        out_channel = MessengerBot(self.client)
        user_msg = UserMessage(text, out_channel, sender_id,
                               input_channel=self.name())

        # noinspection PyBroadException
        try:
            self.on_new_message(user_msg)
        except Exception:
            logger.exception("Exception when trying to handle webhook "
                             "for facebook message.")
            pass


class MessengerBot(FBMessengerBot):
    """A bot that uses fb-messenger to communicate."""

    def __init__(self, messenger_client):
        # type: (MessengerClient) -> None
        super(MessengerBot, self).__init__(messenger_client)

    def send_custom_message(self, recipient_id, elements):
        # type: (Text, List[Dict[Text, Any]]) -> None
        """Sends elements to the output."""

        payload = {
            "attachment": {
                "type": "template",
                "payload": {
                    "template_type": "generic",
                    "elements": elements
                }
            }
        }
        self.messenger_client.send(payload,
                                   self._recipient_json(recipient_id),
                                   'RESPONSE')


class FacebookInput(FBFacebookInput):
    """Facebook input channel implementation. Based on the HTTPInputChannel."""

    def __init__(self, fb_verify, fb_secret, fb_access_token):
        super(FacebookInput, self).__init__(fb_verify, fb_secret, fb_access_token)

    def blueprint(self, on_new_message):

        fb_webhook = Blueprint('fb_webhook', __name__)

        @fb_webhook.route("/", methods=['GET'])
        def health():
            return jsonify({"status": "ok"})

        @fb_webhook.route("/webhook", methods=['GET'])
        def token_verification():
            if request.args.get("hub.verify_token") == self.fb_verify:
                return request.args.get("hub.challenge")
            else:
                logger.warning(
                        "Invalid fb verify token! Make sure this matches "
                        "your webhook settings on the facebook app.")
                return "failure, invalid token"

        @fb_webhook.route("/webhook", methods=['POST'])
        def webhook():
            signature = request.headers.get("X-Hub-Signature") or ''
            if not self.validate_hub_signature(self.fb_secret, request.data,
                                               signature):
                logger.warning("Wrong fb secret! Make sure this matches the "
                               "secret in your facebook app settings")
                return "not validated"

            messenger = Messenger(self.fb_access_token, on_new_message)

            messenger.handle(request.get_json(force=True))
            return "success"

        return fb_webhook

Then I added this custom channel in my credentials.yml via:

channels.facebook.FacebookInput:
    verify: ...
    secret: ...
    page-access-token: ...
2 Likes

Wow thanks for the detailed answer Simon.

I see so the solution would be to create your own implementation of the Facebook channel then. It looks cool and I will give this approach a try, thanks a lot.

By the way, I would suppose that here you are using the same credentials as the normal facebook channel, but what about running the bot? Do you still run it via:

`python -m rasa_core.run -c facebook -d models/dialogue -u models/current/nlu --port 5002 --credentials fb_credentials.yml --endpoints endpoints.yml`

or is there some else to add/remove?

Thanks again :slight_smile:

Instead of passing the channels as arguments, I’m passing a credentials.yml like described here: credentials.yml

In there you can define and specify all the details of the channels you want to use, then you can just pass the credentials.yml with rasa_core.run -c <path-to-credentials.yml>. The rest stays the same.

The credentials.yml with this custom channel just looks like I mentioned above:

channels.facebook.FacebookInput:
    verify: ...
    secret: ...
    page-access-token: ...
1 Like

Ok that’s perfect. I was actually taking a look at the rasa core’s Facebook.py and the code you showed me her is quite similiar (not trying to make it sound negative).

But then how would you use this in a custom action. Should I just import facebook.py in my actions.py and then call send_custom_message or is there something else to do?

Sorry if I’m being bothersome by the way.

Yes, my facebook.py is pretty much exactly the native rasa_core/channels/facebook.py except that three methods were customized:

  • MessengerBot.send_custom_message does not include the self._add_postback_info(element['buttons']) line anymore because this forces every button to be of type postback. Thus, you e.g. cannot create a generic button.
  • Messenger._handle_user_message uses the custom MessengerBot instead of the rasa_core’s
  • FacebookInput.blueprint.webhook instantiates the custom Messenger instead of rasa_core’s Messenger

The last two are literally just there to make sure the custom MessengerBot is inside FacebookInput instead of rasa_core’s.

In your actions.py you can just use it like this in any of the run methods:

# Create your (button) elements according to Messenger Platform API specs
elements = [
    {
        "title": "title",
        "image_url": "url",
        "buttons": [...]
    }
]
dispatcher.utter_custom_message(*elements)

So I tried implementing this but nothing comes up on messenger. On my terminal it actually says that it send the message but nothing is visualized on messenger.

Any ideas as to why this happens?

I experienced that most of the time, buttons not showing up are caused by errors in the template. I think maybe you forget to add some buttons to your elements: Generic template

In my case, an element looks like this and shows up on Messenger:

{
    "title": product["name"],
    "subtitle": "Product",  # workaround for cmdline button interpretation
    "image_url": product["imageUrl"],
    "buttons": [
        {
            "type": "postback",
            "title": "Add",
            "payload": '/user.selected_products',
        },
    ]
}
1 Like

Oh it seems I had a round of extra square brackets [].

Thank you so so much Simon, you really helped me out here. I would upvote your answers 1000 times if I could.

One last question. In this case are we able to only use postback buttons? So for example if I would like a button that redirects to a certain url could I do that?

BTW for everyone else looking for a solution to this problem, this whole process we discussed here works.

Of course, glad I could help.

Essentially, now you can use all of Messenger’s templates and buttons as described in the official docs.

I’m not sure I understand what you mean by redirect. Do you just want to send a link to the user that they should open? Then you might want a URL button.

1 Like

HI @smn-snkl Can I implement Login Button from your code?

After implementing your method I am unable to get button even I changed my custom_action code as you mentioned Getting user data in Facebook Messenger and Getting user data in Facebook Messenger but it didn’t work.

@smn-snkl Actually, i am able to implement postback type as you mentioned but unable to implement account_link type in generic template. Need some help.

Terminal logs shows that some response sent but no button/generic template for account_link showed on messenger.

Hi @abhishakskilrock,

sorry for my late reply. Here’s how I’m doing it now with buttons:

button = {
    "type": "account_link",
    "url": LOGIN_URL,
}
dispatcher.utter_button_template(
    'utter_login',
    buttons=[button],
    tracker=tracker
) 

You should also be able to do it with utter_custom_message (which uses the generic_template) though. Have you checked whether you removed the following lines:

for element in elements:
  self._add_postback_info(element['buttons'])

It’s because of the _add_postback_info method that all buttons are “switched” to postback buttons, therefore causing other buttons to malfunction.

How to remove this line without changing main source code ? Because whenever I am trying to send generic template it’s give me error.

For solution : How to send facebook Generic Template from custom action? Like sending buttons or images in message

1 Like

Hi @smn-snkl

I am try to build food ordering bot like how you define even me also define like that, in my JavaScript I can able to get title, subtitle, image_url values, but that button title I am not able to get.

This is my JavaScript code:

function createCardsCarousel(cardsData) {

var card = "";



for (i = 0; i < cardsData.length; i++) {

    title = cardsData[i].name;

    //data = cardsData[i];

    cost = cardsData[i].price;

    image = cardsData[i].image_url;

    button = cardsData[i].button;

    text = "";

    for (i=0; i < button.length; i++) {

        text += button[i].title;

    }

    item= '<div class="card-owl"><div class="img"><img src="'+ image +'"></div><div class="content-owl"><div class="title-owl">'+ title +'</div><p>Rs.'+ cost +'/-</p><div class="btn-owl"><button id="order-food">'+text+'</button></div></div></div>';

    card += item;

}

cardsData is = [{ “title”: product[“name”], “subtitle”: “Product”, # workaround for cmdline button interpretation “image_url”: product[“imageUrl”], “buttons”: [ { “type”: “postback”, “title”: “Add”, “payload”: ‘/user.selected_products’, }, ] }, { “title”: product[“name”], “subtitle”: “Product”, # workaround for cmdline button interpretation “image_url”: product[“imageUrl”], “buttons”: [ { “type”: “postback”, “title”: “Add”, “payload”: ‘/user.selected_products’, }, ] } ]

please help me on this, Thanks in advance