Skip to content

Commit

Permalink
Merge branch 'attachment-support'
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Oct 21, 2017
2 parents 2a64bad + 52acfb4 commit 28d5ac9
Show file tree
Hide file tree
Showing 6 changed files with 335 additions and 45 deletions.
8 changes: 4 additions & 4 deletions docs/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -175,22 +175,22 @@ meaning it will simply print information to the console when an event happens
The event actions can be changed by subclassing the :class:`Client`, and then overwriting the event methods::

class CustomClient(Client):
def onMessage(self, mid, author_id, message, thread_id, thread_type, ts, metadata, msg, **kwargs):
# Do something with the message here
def onMessage(self, mid, author_id, message_object, thread_id, thread_type, ts, metadata, msg, **kwargs):
# Do something with the message_object here
pass

client = CustomClient('<email>', '<password>')

**Notice:** The following snippet is as equally valid as the previous one::

class CustomClient(Client):
def onMessage(self, message, author_id, thread_id, thread_type, **kwargs):
def onMessage(self, message_object, author_id, thread_id, thread_type, **kwargs):
# Do something with the message here
pass

client = CustomClient('<email>', '<password>')

The change was in the parameters that our `onMessage` method took: ``message`` and ``author_id`` got swapped,
The change was in the parameters that our `onMessage` method took: ``message_object`` and ``author_id`` got swapped,
and ``mid``, ``ts``, ``metadata`` and ``msg`` got removed, but the function still works, since we included ``**kwargs``

.. note::
Expand Down
10 changes: 5 additions & 5 deletions examples/echobot.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

# Subclass fbchat.Client and override required methods
class EchoBot(Client):
def onMessage(self, author_id, message, thread_id, thread_type, **kwargs):
def onMessage(self, author_id, message_object, thread_id, thread_type, **kwargs):
self.markAsDelivered(author_id, thread_id)
self.markAsRead(author_id)

log.info("Message from {} in {} ({}): {}".format(author_id, thread_id, thread_type.name, message))
log.info("{} from {} in {}".format(message_object, thread_id, thread_type.name))

# If you're not the author, echo
if author_id != self.uid:
self.sendMessage(message, thread_id=thread_id, thread_type=thread_type)
# If you're not the author, and the message was a message containing text, echo
if author_id != self.uid and message_object.text is not None:
self.sendMessage(message_object.text, thread_id=thread_id, thread_type=thread_type)

client = EchoBot("<email>", "<password>")
client.listen()
6 changes: 3 additions & 3 deletions examples/removebot.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
from fbchat.models import *

class RemoveBot(Client):
def onMessage(self, author_id, message, thread_id, thread_type, **kwargs):
def onMessage(self, author_id, message_object, thread_id, thread_type, **kwargs):
# We can only kick people from group chats, so no need to try if it's a user chat
if message == 'Remove me!' and thread_type == ThreadType.GROUP:
if message_object.text == 'Remove me!' and thread_type == ThreadType.GROUP:
log.info('{} will be removed from {}'.format(author_id, thread_id))
self.removeUserFromGroup(author_id, thread_id=thread_id)
else:
# Sends the data to the inherited onMessage, so that we can still see when a message is recieved
super(type(self), self).onMessage(author_id=author_id, message=message, thread_id=thread_id, thread_type=thread_type, **kwargs)
super(RemoveBot, self).onMessage(author_id=author_id, message_object=message_object, thread_id=thread_id, thread_type=thread_type, **kwargs)

client = RemoveBot("<email>", "<password>")
client.listen()
121 changes: 110 additions & 11 deletions fbchat/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,23 @@ def fetchUnread(self):
"unseen_threads": j['payload']['unseen_thread_ids']
}

def fetchImageUrl(self, image_id):
"""Fetches the url to the original image from an image attachment ID
:param image_id: The image you want to fethc
:type image_id: str
:return: An url where you can download the original image
:rtype: str
:raises: FBChatException if request failed
"""
image_id = str(image_id)
j = checkRequest(self._get(ReqUrl.ATTACHMENT_PHOTO, query={'photo_id': str(image_id)}))

url = get_jsmods_require(j, 3)
if url is None:
raise FBChatException('Could not fetch image url from: {}'.format(j))
return url

"""
END FETCH METHODS
"""
Expand Down Expand Up @@ -905,11 +922,9 @@ def _doSendRequest(self, data):
raise FBchatException('Error when sending message: No message IDs could be found: {}'.format(j))

# update JS token if received in response
if j.get('jsmods') is not None and j['jsmods'].get('require') is not None:
try:
self.payloadDefault['fb_dtsg'] = j['jsmods']['require'][0][3][0]
except (KeyError, IndexError) as e:
log.warning('Error when updating fb_dtsg. Facebook might have changed protocol!')
fb_dtsg = get_jsmods_require(j, 2)
if fb_dtsg is not None:
self.payloadDefault['fb_dtsg'] = fb_dtsg

return message_id

Expand Down Expand Up @@ -1391,7 +1406,7 @@ def getThreadIdAndThreadType(msg_metadata):
delta_type = delta.get("type")
metadata = delta.get("messageMetadata")

if metadata is not None:
if metadata:
mid = metadata["messageId"]
author_id = str(metadata['actorFbId'])
ts = int(metadata.get("timestamp"))
Expand Down Expand Up @@ -1472,9 +1487,91 @@ def getThreadIdAndThreadType(msg_metadata):

# New message
elif delta.get("class") == "NewMessage":
message = delta.get('body', '')
mentions = []
if delta.get('data') and delta['data'].get('prng'):
try:
mentions = [Mention(str(mention.get('i')), offset=mention.get('o'), length=mention.get('l')) for mention in parse_json(delta['data']['prng'])]
except Exception:
log.exception('An exception occured while reading attachments')

attachments = []
if delta.get('attachments'):
try:
for a in delta['attachments']:
mercury = a['mercury']
blob = mercury.get('blob_attachment', {})
image_metadata = a.get('imageMetadata', {})
attach_type = mercury['attach_type']
if attach_type in ['photo', 'animated_image']:
attachments.append(ImageAttachment(
original_extension=blob.get('original_extension') or (blob['filename'].split('-')[0] if blob.get('filename') else None),
width=int(image_metadata['width']),
height=int(image_metadata['height']),
is_animated=attach_type=='animated_image',
thumbnail_url=mercury.get('thumbnail_url'),
preview=blob.get('preview') or blob.get('preview_image'),
large_preview=blob.get('large_preview'),
animated_preview=blob.get('animated_image'),
uid=a['id']
))
elif attach_type == 'file':
# Add more data here for audio files
attachments.append(FileAttachment(
url=mercury.get('url'),
size=int(a['fileSize']),
name=mercury.get('name'),
is_malicious=blob.get('is_malicious'),
uid=a['id']
))
elif attach_type == 'video':
attachments.append(VideoAttachment(
size=int(a['fileSize']),
width=int(image_metadata['width']),
height=int(image_metadata['height']),
duration=blob.get('playable_duration_in_ms'),
preview_url=blob.get('playable_url'),
small_image=blob.get('chat_image'),
medium_image=blob.get('inbox_image'),
large_image=blob.get('large_image'),
uid=a['id']
))
elif attach_type == 'sticker':
# Add more data here for stickers
attachments.append(StickerAttachment(
uid=mercury.get('metadata', {}).get('stickerID')
))
elif attach_type == 'share':
# Add more data here for shared stuff (URLs, events and so on)
attachments.append(ShareAttachment(
uid=a.get('id')
))
else:
attachments.append(Attachment(
uid=a.get('id')
))
except Exception:
log.exception('An exception occured while reading attachments: {}'.format(delta['attachments']))

emoji_size = None
if metadata and metadata.get('tags'):
for tag in metadata['tags']:
if tag.startswith('hot_emoji_size:'):
emoji_size = LIKES[tag.split(':')[1]]
break

message = Message(
text=delta.get('body'),
mentions=mentions,
emoji_size=emoji_size
)
message.uid = mid
message.author = author_id
message.timestamp = ts
message.attachments = attachments
#message.is_read = None
#message.reactions = []
thread_id, thread_type = getThreadIdAndThreadType(metadata)
self.onMessage(mid=mid, author_id=author_id, message=message,
self.onMessage(mid=mid, author_id=author_id, message=delta.get('body', ''), message_object=message,
thread_id=thread_id, thread_type=thread_type, ts=ts, metadata=metadata, msg=m)

# Unknown message type
Expand Down Expand Up @@ -1632,21 +1729,23 @@ def onListenError(self, exception=None):
return True


def onMessage(self, mid=None, author_id=None, message=None, thread_id=None, thread_type=ThreadType.USER, ts=None, metadata=None, msg=None):
def onMessage(self, mid=None, author_id=None, message=None, message_object=None, thread_id=None, thread_type=ThreadType.USER, ts=None, metadata=None, msg=None):
"""
Called when the client is listening, and somebody sends a message
:param mid: The message ID
:param author_id: The ID of the author
:param message: The message
:param message: (deprecated. Use `message_object.text` instead)
:param message_object: The message (As a `Message` object)
:param thread_id: Thread ID that the message was sent to. See :ref:`intro_threads`
:param thread_type: Type of thread that the message was sent to. See :ref:`intro_threads`
:param ts: The timestamp of the message
:param metadata: Extra metadata about the message
:param msg: A full set of the data recieved
:type message_object: models.Message
:type thread_type: models.ThreadType
"""
log.info("Message from {} in {} ({}): {}".format(author_id, thread_id, thread_type.name, message))
log.info("{} from {} in {}".format(message_object, thread_id, thread_type.name))

def onColorChange(self, mid=None, author_id=None, new_color=None, thread_id=None, thread_type=ThreadType.USER, ts=None, metadata=None, msg=None):
"""
Expand Down
Loading

0 comments on commit 28d5ac9

Please sign in to comment.