Merge pull request #342 from kapi2289/quick_replies

[Feature] Quick replies
This commit is contained in:
Mads Marquart
2019-01-31 19:26:03 +01:00
committed by GitHub
3 changed files with 170 additions and 1 deletions

View File

@@ -984,6 +984,30 @@ class Client(object):
plan = graphql_to_plan(j["payload"])
return plan
def _getPrivateData(self):
j = self.graphql_request(GraphQL(doc_id='1868889766468115'))
return j['viewer']
def getPhoneNumbers(self):
"""
Fetches a list of user phone numbers.
:return: List of phone numbers
:rtype: list
"""
data = self._getPrivateData()
return [j['phone_number']['universal_number'] for j in data['user']['all_phones']]
def getEmails(self):
"""
Fetches a list of user emails.
:return: List of emails
:rtype: list
"""
data = self._getPrivateData()
return [j['display_email'] for j in data['all_emails']]
"""
END FETCH METHODS
"""
@@ -1040,6 +1064,25 @@ class Client(object):
if message.sticker:
data['sticker_id'] = message.sticker.uid
if message.quick_replies:
xmd = {"quick_replies": []}
for quick_reply in message.quick_replies:
q = dict()
q["content_type"] = quick_reply._type
q["payload"] = quick_reply.payload
q["external_payload"] = quick_reply.external_payload
q["data"] = quick_reply.data
if quick_reply.is_response:
q["ignore_for_webhook"] = False
if isinstance(quick_reply, QuickReplyText):
q["title"] = quick_reply.title
if not isinstance(quick_reply, QuickReplyLocation):
q["image_url"] = quick_reply.image_url
xmd["quick_replies"].append(q)
if len(message.quick_replies) == 1 and message.quick_replies[0].is_response:
xmd["quick_replies"] = xmd["quick_replies"][0]
data['platform_xmd'] = json.dumps(xmd)
return data
def _doSendRequest(self, data, get_thread_id=False):
@@ -1111,6 +1154,36 @@ class Client(object):
data['specific_to_list[0]'] = "fbid:{}".format(thread_id)
return self._doSendRequest(data)
def quickReply(self, quick_reply, payload=None, thread_id=None, thread_type=None):
"""
Replies to a chosen quick reply
:param quick_reply: Quick reply to reply to
:param payload: Optional answer to the quick reply
:param thread_id: User/Group ID to send to. See :ref:`intro_threads`
:param thread_type: See :ref:`intro_threads`
:type quick_reply: models.QuickReply
:type thread_type: models.ThreadType
:return: :ref:`Message ID <intro_message_ids>` of the sent message
:raises: FBchatException if request failed
"""
quick_reply.is_response = True
if isinstance(quick_reply, QuickReplyText):
return self.send(Message(text=quick_reply.title, quick_replies=[quick_reply]))
elif isinstance(quick_reply, QuickReplyLocation):
if not isinstance(payload, LocationAttachment): raise ValueError("Payload must be an instance of `fbchat.models.LocationAttachment`")
return self.sendLocation(payload, thread_id=thread_id, thread_type=thread_type)
elif isinstance(quick_reply, QuickReplyEmail):
if not payload: payload = self.getEmails()[0]
quick_reply.external_payload = quick_reply.payload
quick_reply.payload = payload
return self.send(Message(text=payload, quick_replies=[quick_reply]))
elif isinstance(quick_reply, QuickReplyPhoneNumber):
if not payload: payload = self.getPhoneNumbers()[0]
quick_reply.external_payload = quick_reply.payload
quick_reply.payload = payload
return self.send(Message(text=payload, quick_replies=[quick_reply]))
def unsend(self, mid):
"""
Unsends a message (removes for everyone)

View File

@@ -267,6 +267,24 @@ def graphql_to_plan(a):
rtn.invited = [m.get('node').get('id') for m in guests if m.get('guest_list_state') == "INVITED"]
return rtn
def graphql_to_quick_reply(q, is_response=False):
data = dict()
_type = q.get('content_type').lower()
if q.get('payload'): data["payload"] = q["payload"]
if q.get('data'): data["data"] = q["data"]
if q.get('image_url') and _type is not QuickReplyLocation._type: data["image_url"] = q["image_url"]
data["is_response"] = is_response
if _type == QuickReplyText._type:
if q.get('title') is not None: data["title"] = q["title"]
rtn = QuickReplyText(**data)
elif _type == QuickReplyLocation._type:
rtn = QuickReplyLocation(**data)
elif _type == QuickReplyPhoneNumber._type:
rtn = QuickReplyPhoneNumber(**data)
elif _type == QuickReplyEmail._type:
rtn = QuickReplyEmail(**data)
return rtn
def graphql_to_message(message):
if message.get('message_sender') is None:
message['message_sender'] = {}
@@ -290,6 +308,12 @@ def graphql_to_message(message):
}
if message.get('blob_attachments') is not None:
rtn.attachments = [graphql_to_attachment(attachment) for attachment in message['blob_attachments']]
if message.get('platform_xmd_encoded'):
quick_replies = json.loads(message['platform_xmd_encoded']).get('quick_replies')
if isinstance(quick_replies, list):
rtn.quick_replies = [graphql_to_quick_reply(q) for q in quick_replies]
elif isinstance(quick_replies, dict):
rtn.quick_replies = [graphql_to_quick_reply(quick_replies, is_response=True)]
if message.get('extensible_attachment') is not None:
attachment = graphql_to_extensible_attachment(message['extensible_attachment'])
if isinstance(attachment, UnsentMessage):

View File

@@ -190,10 +190,12 @@ class Message(object):
sticker = None
#: A list of attachments
attachments = None
#: A list of :class:`QuickReply`
quick_replies = None
#: Whether the message is unsent (deleted for everyone)
unsent = None
def __init__(self, text=None, mentions=None, emoji_size=None, sticker=None, attachments=None):
def __init__(self, text=None, mentions=None, emoji_size=None, sticker=None, attachments=None, quick_replies=None):
"""Represents a Facebook message"""
self.text = text
if mentions is None:
@@ -204,6 +206,9 @@ class Message(object):
if attachments is None:
attachments = []
self.attachments = attachments
if quick_replies is None:
quick_replies = []
self.quick_replies = quick_replies
self.reactions = {}
self.read_by = []
self.deleted = False
@@ -521,6 +526,73 @@ class Mention(object):
def __unicode__(self):
return '<Mention {}: offset={} length={}>'.format(self.thread_id, self.offset, self.length)
class QuickReply(object):
#: Payload of the quick reply
payload = None
#: External payload for responses
external_payload = None
#: Additional data
data = None
#: Whether it's a response for a quick reply
is_response = None
def __init__(self, payload=None, data=None, is_response=False):
"""Represents a quick reply"""
self.payload = payload
self.data = data
self.is_response = is_response
def __repr__(self):
return self.__unicode__()
def __unicode__(self):
return '<{}: payload={!r}>'.format(self.__class__.__name__, self.payload)
class QuickReplyText(QuickReply):
#: Title of the quick reply
title = None
#: URL of the quick reply image (optional)
image_url = None
#: Type of the quick reply
_type = "text"
def __init__(self, title=None, image_url=None, **kwargs):
"""Represents a text quick reply"""
super(QuickReplyText, self).__init__(**kwargs)
self.title = title
self.image_url = image_url
class QuickReplyLocation(QuickReply):
#: Type of the quick reply
_type = "location"
def __init__(self, **kwargs):
"""Represents a location quick reply (Doesn't work on mobile)"""
super(QuickReplyLocation, self).__init__(**kwargs)
self.is_response = False
class QuickReplyPhoneNumber(QuickReply):
#: URL of the quick reply image (optional)
image_url = None
#: Type of the quick reply
_type = "user_phone_number"
def __init__(self, image_url=None, **kwargs):
"""Represents a phone number quick reply (Doesn't work on mobile)"""
super(QuickReplyPhoneNumber, self).__init__(**kwargs)
self.image_url = image_url
class QuickReplyEmail(QuickReply):
#: URL of the quick reply image (optional)
image_url = None
#: Type of the quick reply
_type = "user_email"
def __init__(self, image_url=None, **kwargs):
"""Represents an email quick reply (Doesn't work on mobile)"""
super(QuickReplyEmail, self).__init__(**kwargs)
self.image_url = image_url
class Poll(object):
#: ID of the poll
uid = None