GithubHelp home page GithubHelp logo

conbus / fbmq Goto Github PK

View Code? Open in Web Editor NEW
170.0 20.0 65.0 6.99 MB

(Deprecated) Facebook Messenger Platform Python Library (Facebook Chatbot Library)

License: MIT License

Python 99.45% HTML 0.55%
facebook-chatbot facebook-messenger-bot facebook-messenger-platform facebook-messenger-api chatbot

fbmq's Introduction

(Deprecated Project)

FBMQ (Facebook Messenger Platform Python Library)

PyPI Build Status Coverage Status PyPI

A Python Library For Using The Facebook Messenger Platform API (Python Facebook Chat & Chatbot Library) Facebook messenger platform api full features are supported

Table of Contents

Install

pip install fbmq

Handle webhook

how to handle messages from user to facebook page

Usage (with flask)

from flask import Flask, request
from fbmq import Page

page = Page(PAGE_ACCESS_TOKEN)

@app.route('/webhook', methods=['POST'])
def webhook():
  page.handle_webhook(request.get_data(as_text=True))
  return "ok"

@page.handle_message
def message_handler(event):
  """:type event: fbmq.Event"""
  sender_id = event.sender_id
  message = event.message_text
  
  page.send(sender_id, "thank you! your message is '%s'" % message)

@page.after_send
def after_send(payload, response):
  """:type payload: fbmq.Payload"""
  print("complete")

handlers

A spec in detail - https://developers.facebook.com/docs/messenger-platform/webhook-reference

@page.handle_message - This callback will occur when a message has been sent to your page. (quick reply is also handled in here)

@page.handle_echo - This callback will occur when a message has been sent by your page

@page.handle_delivery - This callback will occur when a message a page has sent has been delivered.

@page.handle_optin - This callback will occur when the Send-to-Messenger plugin has been tapped

@page.handle_postback - Postbacks occur when a Postback button, Get Started button, Persistent menu or Structured Message is tapped.

@page.handle_read - This callback will occur when a message a page has sent has been read by the user.

@page.handle_account_linking - This callback will occur when the Linked Account or Unlink Account call-to-action have been tapped.

@page.after_send - This callback will occur when page.send function has been called.

Event parameter (fbmq.Event class)

event.sender_id str : message sender id, user id

event.recipient_id str : message receiver id, page id

event.timestamp number : timestamp when message is received

event.message dict : message dict that is received. more detail

event.message_text str : event.message.get('text')

event.message_attachments str : event.message.get('attachments')

event.quick_reply dict : quick reply dict that is received. more detail

event.quick_reply_payload str : `event.quick_reply.get('payload')

event.postback dict : postback dict that is received. more detail

event.postback_payload str : `event.postback.get('payload')

event.optin dict : dict that is received. more detail

event.account_linking dict: dict that is received. more detail

event.delivery dict: dict that is received. more detail

event.read dict: dict that is received. more detail

event.is_* bool - True if event type is valid

if you don't need a decorator

page = fbmq.Page(PAGE_ACCESS_TOKEN, after_send=after_send)

@app.route('/webhook', methods=['POST'])
def webhook():
  page.handle_webhook(request.get_data(as_text=True),
                      message=message_handler)
  return "ok"

def message_handler(event):
  """:type event: fbmq.Event"""
  sender_id = event.sender_id
  message = event.message_text
  
  page.send(sender_id, "thank you! your message is '%s'" % message)

def after_send(payload, response):
  """:type event: fbmq.Payload"""
  print("complete")

Send a message

how to send a message from facebook page to user

Basic

Import
from fbmq import Attachment, Template, QuickReply, Page
Text
page.send(recipient_id, "hello world!")
Image

jpg, png, gif support

page.send(recipient_id, Attachment.Image(image_url))
Audio
page.send(recipient_id, Attachment.Audio(audio_url))
Video
page.send(recipient_id, Attachment.Video(video_url))
File
page.send(recipient_id, Attachment.File(file_url))
quick reply
quick_replies = [
  QuickReply(title="Action", payload="PICK_ACTION"),
  QuickReply(title="Comedy", payload="PICK_COMEDY")
]

# you can use a dict instead of a QuickReply class
#
# quick_replies = [{'title': 'Action', 'payload': 'PICK_ACTION'},
#                {'title': 'Comedy', 'payload': 'PICK_COMEDY'}]


page.send(recipient_id, 
          "What's your favorite movie genre?",
          quick_replies=quick_replies,
          metadata="DEVELOPER_DEFINED_METADATA")
quick reply callback

you can define easily a quick reply callback method.

@page.callback(['PICK_ACTION', 'PICK_COMEDY'])
def callback_picked_genre(payload, event):
  print(payload, event)
  
# Also supported regex, it works corretly
# @page.callback(['PICK_(.+)'])

if you want to handle only quick_reply callback without button postback

@page.callback(['PICK_ACTION', 'PICK_COMEDY'], types=['QUICK_REPLY'])
typing on/off
page.typing_on(recipient_id)
page.typing_off(recipient_id)

Templates

Template : Button
buttons = [
  Templates.ButtonWeb("Open Web URL", "https://www.oculus.com/en-us/rift/"),
  Templates.ButtonPostBack("trigger Postback", "DEVELOPED_DEFINED_PAYLOAD"),
  Templates.ButtonPhoneNumber("Call Phone Number", "+16505551234")
]

# you can use a dict instead of a Button class
#
# buttons = [{'type': 'web_url', 'title': 'Open Web URL', 'value': 'https://www.oculus.com/en-us/rift/'},
#          {'type': 'postback', 'title': 'trigger Postback', 'value': 'DEVELOPED_DEFINED_PAYLOAD'},
#          {'type': 'phone_number', 'title': 'Call Phone Number', 'value': '+16505551234'}]

page.send(recipient_id, Template.Buttons("hello", buttons))
button callback

you can define easily a button postback method (it works only postback type buttons).

@page.callback(['DEVELOPED_DEFINED_PAYLOAD'])
def callback_clicked_button(payload, event):
  print(payload, event)
  
# Also supported regex, it works corretly
# @page.callback(['DEVELOPED_DEFINE(.+)'])

if you want to handle only button's postback without quick_reply callback

@page.callback(['DEVELOPED_DEFINED_PAYLOAD'], types=['POSTBACK'])
Template : Generic
page.send(recipient_id, Template.Generic([
  Template.GenericElement("rift",
                          subtitle="Next-generation virtual reality",
                          item_url="https://www.oculus.com/en-us/rift/",
                          image_url=CONFIG['SERVER_URL'] + "/assets/rift.png",
                          buttons=[
                              Template.ButtonWeb("Open Web URL", "https://www.oculus.com/en-us/rift/"),
                              Template.ButtonPostBack("tigger Postback", "DEVELOPED_DEFINED_PAYLOAD"),
                              Template.ButtonPhoneNumber("Call Phone Number", "+16505551234")
                          ]),
  Template.GenericElement("touch",
                          subtitle="Your Hands, Now in VR",
                          item_url="https://www.oculus.com/en-us/touch/",
                          image_url=CONFIG['SERVER_URL'] + "/assets/touch.png",
                          buttons=[
                              Template.ButtonWeb("Open Web URL", "https://www.oculus.com/en-us/rift/"),
                              Template.ButtonPostBack("tigger Postback", "DEVELOPED_DEFINED_PAYLOAD"),
                              Template.ButtonPhoneNumber("Call Phone Number", "+16505551234")
                          ])
]))
Template : Receipt
    element = Template.ReceiptElement(title="Oculus Rift",
                                      subtitle="Includes: headset, sensor, remote",
                                      quantity=1,
                                      price=599.00,
                                      currency="USD",
                                      image_url=CONFIG['SERVER_URL'] + "/assets/riftsq.png"
                                      )

    address = Template.ReceiptAddress(street_1="1 Hacker Way",
                                      street_2="",
                                      city="Menlo Park",
                                      postal_code="94025",
                                      state="CA",
                                      country="US")

    summary = Template.ReceiptSummary(subtotal=698.99,
                                      shipping_cost=20.00,
                                      total_tax=57.67,
                                      total_cost=626.66)

    adjustment = Template.ReceiptAdjustment(name="New Customer Discount", amount=-50)

    page.send(recipient_id, Template.Receipt(recipient_name='Peter Chang',
                                            order_number='1234',
                                            currency='USD',
                                            payment_method='Visa 1234',
                                            timestamp="1428444852",
                                            elements=[element],
                                            address=address,
                                            summary=summary,
                                            adjustments=[adjustment]))

Options

notification type

support notification_type as a option

NotificationType.REGULAR (default), NotificationType.SILENT_PUSH, NotificationType.NO_PUSH

page.send(recipient_id, 'hello', notification_type=NotificationType.NO_PUSH)
callback

you can set a callback function to each page.send

def callback(payload, response):
  print('response : ' + response.text)
  
page.send(recipient_id, 'hello', callback=callback)

Thread settings

Greeting text

page.greeting("Welcome!")

Get started button

page.show_starting_button("START_PAYLOAD")

@page.callback(['START_PAYLOAD'])
def start_callback(payload, event):
  print("Let's start!")

Persistent menu

page.show_persistent_menu([Template.ButtonPostBack('MENU1', 'MENU_PAYLOAD/1'),
                           Template.ButtonPostBack('MENU2', 'MENU_PAYLOAD/2')])

@page.callback(['MENU_PAYLOAD/(.+)'])
def click_persistent_menu(payload, event):
  click_menu = payload.split('/')[1]
  print("you clicked %s menu" % click_menu)

Fetch user/page profile

page_id = page.page_id
page_name = page.page_name
user_profile = page.get_user_profile(event.sender_id) # return dict
print(user_profile)

#{"first_name":"...", "last_name":"...", "profile_pic":"...", "locale":"...", "timezone":9, "gender":"..."}

Example

  1. fill example/config.py
  2. run server
cd example
virtualenv env
source env/bin/activate
pip install -r requirements.txt
python server.py

fbmq's People

Contributors

anton-nayshtut avatar benderv avatar fr1sk avatar hollalll726 avatar hultner avatar hwonyo avatar igo avatar josxa avatar kimwz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fbmq's Issues

when send in text, both text and attachment got check up

AFTER_SEND : {"message": {"attachment": "test", "metadata": null, "quick_replies": null, "text": "test"}, "notification_type": null, "recipient": {"id": "1208595212586466", "phone_number": null}, "sender_action": null}

RESPONSE : {"error":{"message":"(#100) Only one of text or attachment can be specified","type":"OAuthException","code":100,"error_subcode":2018018,"fbtrace_id":"Ft9OlIgxEe4"}}
127.0.0.1 - - [06/Mar/2017 21:03:57] "POST /webhook HTTP/1.1" 200 -

I need nested persistent menu

Hi :)

Thanks alot for sharing this awesome library.

I would like to use the nested persistent menu, but is it possible in the current fbmq version?

Here is what I suggest:

add ButtonPersistentNested(title, buttons)

nested = [
Template.ButtonPostBack("Nested1", 'MENU_PAYLOAD,nested1'),
Template.ButtonPostBack("Nested2", 'MENU_PAYLOAD,nested2'),
]
page.show_persistent_menu([
Template.ButtonPostBack("Menu1", 'MENU_PAYLOAD,menu1'),
Template.ButtonPersistentNested("Nested", nested),
])

I have quick reply loop problem.

After I hit a quick reply button, my page keeps answer me with quick reply callback again and again. I try to figure out why, but it seems not from my logic.

now, i try to solve this by ignores all seq that less or equal to previous one.

I want to know the cause of this problem. If you see it in my coding, pls tell me.

if you require any more information,pls ask me
logInteracEngine.txt

my callback function

@page.callback(['CONFIRM_ANIME:.+'],types=['QUICK_REPLY'])
def comfirmAnime(payload, event):
    title = payload.split(':',maxsplit=1)[-1]
    sendto = event.sender_id
    if title == "3-gatsu no lion":
        page.send(sendto,'เรื่องนี้สนุกมากเลยนะเจ้าค่ะ แนะนำเลยค่ะ ฉันถึงกับไปศึกษาวิธีเล่นโชงิ เลยนะเจ้าค่ะ')
    # elif title == "Natsume Yuujinchou Go":
    #     page.send(sendto, 'ซีซันนี้ เรื่องนี้ถือว่าเด็ดมากๆเลยเจ้าค่ะ')
    elif title == "Hibike! Euphonium 2":
        page.send(sendto, 'ฉันสังสัยสุดๆเลยค่ะว่า นางเองทำผมยังไง น่ารักสุดๆ')
    search_anime(sendto,title)

a relate function

def search_anime(user_id,query):
    log.debug('search anime: '+query)
    # log.debug(caller)
    conn = mgDatabase()
    animes = conn.searchAnime(query)
    if len(animes) == 0:
        page.send(user_id, "เมดขอโทษนะค่ะ ต่อหาไม่เจอจริงๆ")
    else:
        lst = []
        for anime in animes:
            taggedtitle = ''
            if anime['isSubbedTh']:
                taggedtitle += '(ซับไทย) '
            elif anime['isDubbedTh']:
                taggedtitle += '(พากย์ไทย) '
            taggedtitle += anime['title_mixed']
            if anime['isEnded'] or anime['isMovie']:
                a = Template.GenericElement(taggedtitle,
                                            subtitle=anime.get('desc'),
                                            item_url=anime['link'],
                                            image_url=anime['coverPicURL'],
                                            buttons=[
                                                Template.ButtonWeb("เปิดวาป ไปwebsite", anime['link']),
                                                Template.ButtonPostBack("กล่องความทรงจำ",
                                                                        "OtherInfo:" + str(user_id) + ',' + str(
                                                                            anime['_id'])),
                                            ])
                lst.append(a)
            elif not conn.didUserSubscribeAnime(user_id,anime['_id']):
                a = Template.GenericElement(taggedtitle,
                                    subtitle=anime.get('desc'),
                                    item_url=anime['link'],
                                    image_url=anime['coverPicURL'],
                                    buttons=[
                                        Template.ButtonPostBack("ตอนใหม่ออก บอกด้วยนะ", "SubscribeAnime:"+str(anime['_id'])),
                                        Template.ButtonWeb("เปิดวาป ไปwebsite", anime['link']),
                                        Template.ButtonPostBack("กล่องความทรงจำ", "OtherInfo:"+str(anime['_id'])),
                                    ])
                lst.append(a)
            else:
                a = Template.GenericElement(taggedtitle,
                                            subtitle=anime.get('desc'),
                                            item_url=anime['link'],
                                            image_url=anime['coverPicURL'],
                                            buttons=[
                                                Template.ButtonPostBack("เลิกเตือน",
                                                                        "๊UnsubscribeAnime:" + str(
                                                                            anime['_id'])),
                                                Template.ButtonWeb("เปิดวาป ไปwebsite", anime['link']),
                                                Template.ButtonPostBack("กล่องความทรงจำ",
                                                                        "OtherInfo:"  + str(
                                                                            anime['_id'])),
                                            ])
                lst.append(a)
        page.send(user_id, "นี่ค่ะนายท่าน")
        page.send(user_id, Template.Generic(lst[:10]))

    conn.accessWaitFnUser(user_id,wait=0)

Sending phone number in recipient causes OAuthException due to insufficient permissions

Background

I started playing around with this library over christmas and were having problems getting it to work while the node version Facebook provided worked flawless.

Error

I was getting error messages along the lines of

'{"object":"page","entry":[{"id":"xxxxxxxxxxxxxxxxxx","time":1514482715763,"messaging":[{"sender":{"id":"xxxxxxxxxxxxxxxxxx"},"recipient":{"id":"xxxxxxxxxxxxxxxxxx"},"timestamp":1514482714981,"message":{"mid":"mid.$cAAEiNjh2mVNmz3mRZVgnjRIp5XRk","seq":xxxxxx,"sticker_id":369239263222822,"attachments":[{"type":"image","payload":{"url":"https:\\/\\/scontent-atl3-1.xx.fbcdn.net\\/v\\/t39.1997-6\\/851557_369239266556155_759568595_n.png?_nc_ad=z-m&_nc_cid=0&oh=3692392632228223692392632&oe=759568595","sticker_id":369239263222822}}]}}]}]}'
Received message for user xxxxxxxxxxxxxxxxxx and page xxxxxxxxxxxxxxxxxx at 1514482714981 with message:
{'mid': 'mid.$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'seq': xxxxxxxxxxxxxxxxxx, 'sticker_id': 369239263222822, 'attachments': [{'type': 'image', 'payload': {'url': 'https://scontent-atl3-1.xx.fbcdn.net/v/t39.1997-6/851557_369239266556155_759568595_n.png?_nc_ad=z-m&_nc_cid=0&oh=3692392632228223692392632&oe=759568595', 'sticker_id': 369239263222822}}]}
{"error":{"message":"(#230) Requires pages_messaging_phone_number permission to manage the object","type":"OAuthException","code":230,"fbtrace_id":"xxxxxxxxxxxxxxxxxx"}}
AFTER_SEND : {"message": {"attachment": null, "metadata": null, "quick_replies": null, "text": "Message with attachment received"}, "notification_type": null, "recipient": {"id": "xxxxxxxxxxxxxxxxxx", "phone_number": null}, "sender_action": null}
RESPONSE : {"error":{"message":"(#230) Requires pages_messaging_phone_number permission to manage the object","type":"OAuthException","code":230,"fbtrace_id":"xxxxxxxxxxxxxxxxxx"}}

Now that was quite verbose but I cleaning it up a bit I could clearly see the problem as:
Requires pages_messaging_phone_number permission to manage the object combined with "recipient": {…, "phone_number": null}, something which isn't used in the reference implementation provided by Facebook.

Solution

The trivial solution were to just comment out line 31 and voila the bot worked as expected, if it's used I could add a conditional.

self.phone_number = phone_number

Conlusion

Is the phone_number field ever used?
And if so is there any reason to send it when it's null?
Adding a conditional check or simply removing it from the Recipient class would both simplify development when working building a bot from scratch but would also remove unnecessary permission dependencies.

If there's no particular reason to send null when it isn't send I'd be happy to send a pull-request with a fix.

Postback stop sending till refresh

Whenever i try to interact with the persistent menu, the first 2 interactions it does what it is supposed to do, but after those 2, if i want it to work i have to refresh the whole page.
Any help would be appreciated.

page.show_persistent_menu([Template.ButtonPostBack('Menu', 'MENU_PAYLOAD')])

`@page.handle_postback
def received_postback(event):
sender_id = event.sender_id
recipient_id = event.recipient_id
time_of_postback = event.timestamp

payload = event.postback_payload

print("Received postback for user %s and page %s with payload '%s' at %s"
      % (sender_id, recipient_id, payload, time_of_postback))

page.send(sender_id, "Postback called")`

Question: How to test Page.handle_webhook

Hello,
I took over a repository that is using your package to connect to the facebook messenger. I'm trying to fix an issue with a message not being handled properly (there's no message handler). While I imagine some way of debugging would be possible (I'm dealing with a Flask app deployed to AWS using Zappa), the current setup is not easily debuggable (at least to my knowledge).

So I would like to take the approach of writing a unit test verifying the expected behaviour. The problem is my knowledge of the Python ecosystem is not very strong and by looking at the code it seems the repository is tightly coupled with the facebook graph endpoint.

Is there a way to efficiently test the scenario described above?

Thank you

Images in Quick Replies Fail

According to the Messenger API, one should be able to add images in their quick replies.

Trying to add an image_url in current version returns
TypeError: __init__() got an unexpected keyword argument 'image_url'

Postbacks being ignored

I'm developing a python bot with this module. And when i'm working with postback buttons, get started button, or the persistent menu, it doesn't do anything. Even in the logs it doesn't appear, could someone help?

page.show_starting_button("START_PAYLOAD")

@page.callback(['START_PAYLOAD']) def start_callback(payload="START_PAYLOAD"): print("Let's start!Test") #Callback

Help would be appreciated

Persistent menu: it does not work correctly @page.callback

When I press an option in the persistent menu, it does not give an operation signal.

def show_persistent_menu():
    page.show_persistent_menu([Template.ButtonPostBack('Cotizaciones', 'MENU_PAYLOAD/1'),
                               Template.ButtonPostBack('Postulacion', 'MENU_PAYLOAD/2'),
                               Template.ButtonPostBack('Contacto', 'MENU_PAYLOAD/3')])

@page.callback(['MENU_PAYLOAD/(.+)'])
def click_persistent_menu(payload, event):
    click_menu = payload.split('/')[1]
    print("you clicked %s menu" % click_menu)

`Any ideas?

heads up - messaging_type property required from 7th may 2018

https://developers.facebook.com/docs/messenger-platform/send-messages

Breaking Change Notice - messaging_type Property Required (Effective May 7, 2018)

As of the release of Messenger Platform v2.2, we are requesting developers to include the messaging_type property in all message sends.

Beginning May 7, 2018, this property will be required for all message sends. After this date, message sends that do not include messaging_type will return an error and not be delivered.

For more information on the messaging_type property, see Messaging Types below.

Typing wont work

Whats wrong with typing effect, couple days before it worked and right now there is no typing

webviews

How can I send webviews GUI with fbmq?

Open url / webview on persistent menu click.

Hi,
How can I make my persistent menu open a web url through the webview ?

EDIT : I just used ButtonWeb Template instead of ButtonPostBack and it works like a charm.

page.show_persistent_menu([fbmq.Template.ButtonWeb('Title','http://www.myurl.com'),

Attachment should be Template

Your README has a mistake regarding buttons.

buttons = [
  Attachment.ButtonWeb("Open Web URL", "https://www.oculus.com/en-us/rift/"),
  Attachment.ButtonPostBack("trigger Postback", "DEVELOPED_DEFINED_PAYLOAD"),
  Attachment.ButtonPhoneNumber("Call Phone Number", "+16505551234")
]

Those Attachments should be Templates.

Does not play nice with classes

I just wanted to raise the issue. I created a class that I mount in cherryp. Unluckily the decorators do not play nice with the classes.

Do you know how to get around that?

Example:

`class FBBot(object):
def init(self):
self.config = {}

@cherrypy.expose()
def webhook(self, *args, **kwargs):

    try:
        if cherrypy.request.method == "GET" and 'hub.verify_token' in kwargs and kwargs['hub.verify_token'] == VERIFY_TOKEN:
            return int(kwargs['hub.challenge'])
    except ValueError:
         return 'Invalid verification token'

    cl = cherrypy.request.headers['Content-Length']
    payload = cherrypy.request.body.read(int(cl)).decode()

    print("FBBOT + {}".format(payload))
    page.handle_webhook(payload)
    return 'ok'

@page.handle_message
def message_handler(self, event):
    """:type event: fbmq.Event"""
    sender_id = event.sender_id
    message = event.message_text`

when the message_handler function is called, the self is creating a problem:

2017-10-26T14:55:38.120444+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/cherrypy/_cprequest.py", line 670, in respond
2017-10-26T14:55:38.120445+00:00 app[web.1]: response.body = self.handler()
2017-10-26T14:55:38.120445+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/cherrypy/lib/encoding.py", line 220, in call
2017-10-26T14:55:38.120446+00:00 app[web.1]: self.body = self.oldhandler(*args, **kwargs)
2017-10-26T14:55:38.120447+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/cherrypy/_cpdispatch.py", line 60, in call
2017-10-26T14:55:38.120448+00:00 app[web.1]: return self.callable(*self.args, **self.kwargs)
2017-10-26T14:55:38.120448+00:00 app[web.1]: File "/app/random_match/bot/facebook.py", line 44, in webhook
2017-10-26T14:55:38.120449+00:00 app[web.1]: page.handle_webhook(payload,message=self.message_handler)
2017-10-26T14:55:38.120449+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/fbmq/fbmq.py", line 219, in handle_webhook
2017-10-26T14:55:38.120450+00:00 app[web.1]: self._call_handler('message', message, event)
2017-10-26T14:55:38.120451+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/fbmq/fbmq.py", line 183, in _call_handler
2017-10-26T14:55:38.120451+00:00 app[web.1]: self._webhook_handlers[name](*args, **kwargs)
2017-10-26T14:55:38.120452+00:00 app[web.1]: TypeError: message_handler() missing 1 required positional argument: 'event'

Which domain should I trust with fbmq library ?

Hi, When I public web server I got a problem with some company policy which server is on premise and then theirs need to trusted with some domain ( Because they can't allow all whitelists for us 🙈).

so which domain and ports I needed ?

page.handle_webhook does not handle empty message

Not sure if this is due to an API change. For webhook POST payload:

{"object":"page","entry":[{"id":"214236215771147","time":1502997235746,"standby":[{"recipient":{"id":"214236215771147"},"timestamp":1502997235746,"sender":{"id":"1437928559577569"},"postback":{"title":"Get Started"}}]}]}

I get the following exception:

2017-08-17T19:13:59.463799+00:00 app[web.1]: [2017-08-17 19:13:59,463] ERROR in app: Exception on /webhook [POST]
2017-08-17T19:13:59.463801+00:00 app[web.1]: Traceback (most recent call last):
2017-08-17T19:13:59.463802+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/flask/app.py", line 1982, in wsgi_app
2017-08-17T19:13:59.463803+00:00 app[web.1]:     response = self.full_dispatch_request()
2017-08-17T19:13:59.463804+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/flask/app.py", line 1614, in full_dispatch_request
2017-08-17T19:13:59.463805+00:00 app[web.1]:     rv = self.handle_user_exception(e)
2017-08-17T19:13:59.463805+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/flask/app.py", line 1517, in handle_user_exception
2017-08-17T19:13:59.463806+00:00 app[web.1]:     reraise(exc_type, exc_value, tb)
2017-08-17T19:13:59.463806+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/flask/_compat.py", line 33, in reraise
2017-08-17T19:13:59.463807+00:00 app[web.1]:     raise value
2017-08-17T19:13:59.463808+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/flask/app.py", line 1612, in full_dispatch_request
2017-08-17T19:13:59.463809+00:00 app[web.1]:     rv = self.dispatch_request()
2017-08-17T19:13:59.463809+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/flask/app.py", line 1598, in dispatch_request
2017-08-17T19:13:59.463810+00:00 app[web.1]:     return self.view_functions[rule.endpoint](**req.view_args)
2017-08-17T19:13:59.463811+00:00 app[web.1]:   File "/app/server/server.py", line 48, in webhook
2017-08-17T19:13:59.463811+00:00 app[web.1]:     page.handle_webhook(payload)
2017-08-17T19:13:59.463812+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/fbmq/fbmq.py", line 204, in handle_webhook
2017-08-17T19:13:59.463813+00:00 app[web.1]:     for event in get_events(data):
2017-08-17T19:13:59.463813+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/fbmq/fbmq.py", line 200, in get_events
2017-08-17T19:13:59.463814+00:00 app[web.1]:     for messaging in entry.get("messaging"):
2017-08-17T19:13:59.463846+00:00 app[web.1]: TypeError: 'NoneType' object is not iterable

It seems that the case that webhook payload does not have message is not handled here

Encoding issues

Hi,

I recently implemented the bot example using django. However the fb send api returned an error of internal failure related to oauth.

{"error":{"message":"(#-1) Send API unexpected internal error","type":"OAuthException","code":-1,"error_subcode":2018012,"fbtrace_id":"BZ8p\/q9haBY"}}

The problem is related to the message object ,as you might read, text must be UTF-8 encoded and less than 640char.

All the library is beautifully written and respects UNICODE, however text must be encoded as follows:

page.send(sender_id,message.encode('utf-8'))

so that it can reliably echo back text messages.

If you agree I can make a pull request modifying Page.send(..) such that

text = message if isinstance(message, str) else None
is changed for

text = message if isinstance(message, str) else message.encode('utf-8') if isinstance(message,unicode) else None

This will allow it to support unicode strings and respect facebook´s api.

Message Sending Error

I am getting this error,

{"error":{"message":"(#-1) Send API unexpected internal error","type":"OAuthException","code":-1,"error_subcode":2018012,"fbtrace_id":"CQMDI4deN+O"}}

sending any kind video attachment results in OAuthException: (#389) Unable to fetch video file from URL

REPRO: run example/server.py - I suspect the FB API has changed in some way as doing a REST call using CURL results in the same error?

  • I have checked that the asset URL is reachable by the public internet.
  • I have verified the Page Access Token is valid
  • I have tried the same REST request using CURL and another python bot framework

I am posting here as fbmq is the best maintained library so thought if there is a workaround or a that a change needs to be made, this is the most likely place it will be posted.

<REMOVED> identifying IDs/Tokens etc in this post.

{  
   "object":"page",
   "entry":[  
      {  
         "id":"<REMOVED>",
         "time":1524198913269,
         "messaging":[  
            {  
               "sender":{  
                  "id":"<REMOVED>"
               },
               "recipient":{  
                  "id":"<REMOVED>"
               },
               "timestamp":1524198912677,
               "message":{  
                  "mid":"<REMOVED>",
                  "seq":132537,
                  "text":"video",
                  "nlp":{  
                     "entities":{  
                        "bye":[  
                           {  
                              "confidence":0.0035418537522868,
                              "value":"true",
                              "_entity":"bye"
                           }
                        ],
                        "thanks":[  
                           {  
                              "confidence":0.00018130744237493,
                              "value":"true",
                              "_entity":"thanks"
                           }
                        ],
                        "greetings":[  
                           {  
                              "confidence":0.0296290972742,
                              "value":"true",
                              "_entity":"greetings"
                           }
                        ]
                     }
                  }
               }
            }
         ]
      }
   ]
}
Received message for user <REMOVED> and page <REMOVED> at <REMOVED> with message:
{  
   u'text':u'video',
   u'mid':u'mid.<REMOVED>',
   u'seq':132537,
   u'nlp':{  
      u'entities':{  
         u'bye':[  
            {  
               u'_entity':u'bye',
               u'confidence':0.0035418537522868,
               u'value':u'true'
            }
         ],
         u'thanks':[  
            {  
               u'_entity':u'thanks',
               u'confidence':0.00018130744237493,
               u'value':u'true'
            }
         ],
         u'greetings':[  
            {  
               u'_entity':u'greetings',
               u'confidence':0.0296290972742,
               u'value':u'true'
            }
         ]
      }
   }
}{  
   "error":{  
      "message":"(#389) Unable to fetch video file from URL.",
      "type":"OAuthException",
      "code":389,
      "fbtrace_id":"<REMOVED>"
   }
}

AFTER_SEND:
{  
   "message":{  
      "attachment":{  
         "payload":{  
            "url":"https://<REMOVED>/assets/allofus480.mov"
         },
         "type":"video"
      },
      "metadata":null,
      "quick_replies":null,
      "text":null
   },
   "notification_type":null,
   "recipient":{  
      "id":"<REMOVED>"
   },
   "sender_action":null,
   "tag":null
}RESPONSE:{  
   "error":{  
      "message":"(#389) Unable to fetch video file from URL.",
      "type":"OAuthException",
      "code":389,
      "fbtrace_id":"<REMOVED>"
   }
}

get_user_profile suddently returns None

after I updated from fbmq 2.4.3 to fbmq 2.4.4 yesterday I suddently experience this issue. I am not sure if it's related to the new version, but I suspect it. I did some debugging and havent found any errors.

user_profile = Page(facebook_token).get_user_profile(event.sender_id)
print(user_profile)

None

should return {'first_name':'Michael'........}

Can't get example to work

I have been trying all morning to get the example 'server.py' to work. I followed the instructions per the README.md file as follows:

pip install fbmq
cd example
virtualenv env
source env/bin/activate
pip install -r requirements.txt
python server.py

I am on Ubuntu 14.04 using python2.7

I get the following error:

env) michaels@michaels-ThinkPad-W520:~/work/python/plugins/fbmq/example$ python server.py
Traceback (most recent call last):
File "server.py", line 8, in
import example.messenger
ImportError: No module named example.messenger

Typing after each message sent

Hi,
First, thank you for this great job, I were able to build almost perfect chatbot with your library 👍
I just have one issue (in addition to missing pictures in quick-replies):

My chatbot is typing almost after every message is sent (just a few seconds and then typing becomes off). I don't know why. I tried to add a page.typing_off(recipient_id) after every page.send(...) but it does nothing. My bot is still typing after each message...

Do you have any solution?

Thanks

EDIT : Moreover, I do not understand how it could be possible to see typing on with my chatbot regarding the fact that I actually never called page.typing_on() in all my code. Is there an "auto typing on" implemented with your library? I can't find it...

Page.handle_message is called before matching function decorated with Page.callback

I've discovered that the Page.handle_message callback gets called before any function decorated with @Page.callback. I would expect that this function is called last and after other callback is executed no other callbacks are called including unless this callback returns True or something.

Here is a minimal example:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import logging
from os import getenv

from fbmq import Page, QuickReply
from flask import Flask, request

app = Flask(__name__)
page = Page(getenv('FB_PAGE_TOKEN'))

page.show_starting_button('HOLA')
logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s: %(message)s',
    level=logging.DEBUG)


@app.route('/webhook', methods=['GET', 'POST'])
def webhook():
    if request.method == 'GET':
        if request.args.get('hub.verify_token') == getenv('FB_VERIFY_TOKEN'):
            return request.args.get('hub.challenge') or 'FAIL'
        return 'FB_VERIFY_TOKEN does not match.'
    elif request.method == 'POST':
        # Maneja las solicitudes enviadas desde Facebook
        if "facebookexternalhit" in request.headers.get('User-Agent'):
            page.handle_webhook(request.get_data(as_text=True))
        return 'OK'


@page.callback(['HOLA'])
def start_callback(payload, event):
    replies = [QuickReply("Hello World!", "PAYLOAD")]
    page.send(event.sender_id, "Say:", replies)


@page.callback(['PAYLOAD'])
def callback_qr_vitrina(payload, event):
    print("message proceed")


@page.handle_message
def message_handler(event):
    print("message was not processed?!")

When this example is ran, the following happens:

127.0.0.1 - - [23/Mar/2018 20:07:59] "POST /webhook HTTP/1.0" 200 -
2018-03-23 20:07:59,961 - werkzeug - INFO: 127.0.0.1 - - [23/Mar/2018 20:07:59] "POST /webhook HTTP/1.0" 200 -
message was not processed?!
2018-03-23 20:08:02,554 - urllib3.connectionpool - DEBUG: Starting new HTTPS connection (1): graph.facebook.com
2018-03-23 20:08:04,484 - urllib3.connectionpool - DEBUG: https://graph.facebook.com:443 "POST /v2.6/me/messages?access_token=<access token> HTTP/1.1" 200 85
message proceed

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.