See commit description
- Deprecated `sendMessage` and `sendEmoji` in favor of `send` - (Almost) Fully integrated attachment support - Updated tests - General cleanup
This commit is contained in:
@@ -17,7 +17,7 @@ from .client import *
|
||||
|
||||
|
||||
__copyright__ = 'Copyright 2015 - {} by Taehoon Kim'.format(datetime.now().year)
|
||||
__version__ = '1.0.25'
|
||||
__version__ = '1.1.0'
|
||||
__license__ = 'BSD'
|
||||
__author__ = 'Taehoon Kim; Moreels Pieter-Jan; Mads Marquart'
|
||||
__email__ = 'carpedm20@gmail.com'
|
||||
|
225
fbchat/client.py
225
fbchat/client.py
@@ -5,7 +5,6 @@ import requests
|
||||
import urllib
|
||||
from uuid import uuid1
|
||||
from random import choice
|
||||
from datetime import datetime
|
||||
from bs4 import BeautifulSoup as bs
|
||||
from mimetypes import guess_type
|
||||
from .utils import *
|
||||
@@ -730,7 +729,7 @@ class Client(object):
|
||||
"""
|
||||
Get the last messages in a thread
|
||||
|
||||
:param thread_id: User/Group ID to default to. See :ref:`intro_threads`
|
||||
:param thread_id: User/Group ID to get messages from. See :ref:`intro_threads`
|
||||
:param limit: Max. number of messages to retrieve
|
||||
:param before: A timestamp, indicating from which point to retrieve messages
|
||||
:type limit: int
|
||||
@@ -740,6 +739,8 @@ class Client(object):
|
||||
:raises: FBchatException if request failed
|
||||
"""
|
||||
|
||||
thread_id, thread_type = self._getThread(thread_id, None)
|
||||
|
||||
j = self.graphql_request(GraphQL(doc_id='1386147188135407', params={
|
||||
'id': thread_id,
|
||||
'message_limit': limit,
|
||||
@@ -867,45 +868,53 @@ class Client(object):
|
||||
SEND METHODS
|
||||
"""
|
||||
|
||||
def _getSendData(self, thread_id=None, thread_type=ThreadType.USER):
|
||||
def _oldMessage(self, message):
|
||||
return message if isinstance(message, Message) else Message(text=message)
|
||||
|
||||
def _getSendData(self, message=None, thread_id=None, thread_type=ThreadType.USER):
|
||||
"""Returns the data needed to send a request to `SendURL`"""
|
||||
messageAndOTID = generateOfflineThreadingID()
|
||||
timestamp = now()
|
||||
date = datetime.now()
|
||||
data = {
|
||||
'client': self.client,
|
||||
'author' : 'fbid:' + str(self.uid),
|
||||
'timestamp' : timestamp,
|
||||
'timestamp_absolute' : 'Today',
|
||||
'timestamp_relative' : str(date.hour) + ":" + str(date.minute).zfill(2),
|
||||
'timestamp_time_passed' : '0',
|
||||
'is_unread' : False,
|
||||
'is_cleared' : False,
|
||||
'is_forward' : False,
|
||||
'is_filtered_content' : False,
|
||||
'is_filtered_content_bh': False,
|
||||
'is_filtered_content_account': False,
|
||||
'is_filtered_content_quasar': False,
|
||||
'is_filtered_content_invalid_app': False,
|
||||
'is_spoof_warning' : False,
|
||||
'source' : 'source:chat:web',
|
||||
'source_tags[0]' : 'source:chat',
|
||||
'html_body' : False,
|
||||
'ui_push_phase' : 'V3',
|
||||
'status' : '0',
|
||||
'offline_threading_id': messageAndOTID,
|
||||
'message_id' : messageAndOTID,
|
||||
'threading_id': generateMessageID(self.client_id),
|
||||
'ephemeral_ttl_mode:': '0',
|
||||
'manual_retry_cnt' : '0',
|
||||
'signatureID' : getSignatureID()
|
||||
'ephemeral_ttl_mode:': '0'
|
||||
}
|
||||
|
||||
# Set recipient
|
||||
if thread_type in [ThreadType.USER, ThreadType.PAGE]:
|
||||
data["other_user_fbid"] = thread_id
|
||||
data['other_user_fbid'] = thread_id
|
||||
elif thread_type == ThreadType.GROUP:
|
||||
data["thread_fbid"] = thread_id
|
||||
data['thread_fbid'] = thread_id
|
||||
|
||||
if message is None:
|
||||
message = Message()
|
||||
|
||||
if message.text or message.sticker or message.emoji_size:
|
||||
data['action_type'] = 'ma-type:user-generated-message'
|
||||
|
||||
if message.text:
|
||||
data['body'] = message.text
|
||||
|
||||
for i, mention in enumerate(message.mentions):
|
||||
data['profile_xmd[{}][id]'.format(i)] = mention.thread_id
|
||||
data['profile_xmd[{}][offset]'.format(i)] = mention.offset
|
||||
data['profile_xmd[{}][length]'.format(i)] = mention.length
|
||||
data['profile_xmd[{}][type]'.format(i)] = 'p'
|
||||
|
||||
if message.emoji_size:
|
||||
if message.text:
|
||||
data['tags[0]'] = 'hot_emoji_size:' + message.emoji_size.name.lower()
|
||||
else:
|
||||
data['sticker_id'] = message.emoji_size.value
|
||||
|
||||
if message.sticker:
|
||||
data['sticker_id'] = message.sticker.uid
|
||||
|
||||
return data
|
||||
|
||||
@@ -928,67 +937,34 @@ class Client(object):
|
||||
|
||||
return message_id
|
||||
|
||||
def sendMessage(self, message, mention=None, thread_id=None,
|
||||
thread_type=ThreadType.USER):
|
||||
def send(self, message, thread_id=None, thread_type=ThreadType.USER):
|
||||
"""
|
||||
Sends a message to a thread
|
||||
|
||||
:param message: Message to send
|
||||
:param thread_id: User/Group ID to send to. See :ref:`intro_threads`
|
||||
:param thread_type: See :ref:`intro_threads`
|
||||
:type message: models.Message
|
||||
:type thread_type: models.ThreadType
|
||||
:mention is in this format {userID : (start, end)},
|
||||
where start is relative start position of @mention in a message
|
||||
and end is relative end position of @mention
|
||||
:return: :ref:`Message ID <intro_message_ids>` of the sent message
|
||||
:raises: FBchatException if request failed
|
||||
"""
|
||||
thread_id, thread_type = self._getThread(thread_id, thread_type)
|
||||
data = self._getSendData(thread_id, thread_type)
|
||||
|
||||
data['action_type'] = 'ma-type:user-generated-message'
|
||||
data['body'] = message or ''
|
||||
if mention:
|
||||
n = 0
|
||||
for key, value in mention.items():
|
||||
data['profile_xmd[%d][id]'%n] = key
|
||||
data['profile_xmd[%d][offset]'%n] = value[0]
|
||||
data['profile_xmd[%d][length]'%n] = value[1] - value[0]
|
||||
data['profile_xmd[%d][type]'%n] = 'p'
|
||||
n += 1
|
||||
data['has_attachment'] = False
|
||||
data['specific_to_list[0]'] = 'fbid:' + thread_id
|
||||
data['specific_to_list[1]'] = 'fbid:' + self.uid
|
||||
data = self._getSendData(message=message, thread_id=thread_id, thread_type=thread_type)
|
||||
|
||||
return self._doSendRequest(data)
|
||||
|
||||
def sendMessage(self, message, thread_id=None, thread_type=ThreadType.USER):
|
||||
"""
|
||||
Deprecated. Use :func:`fbchat.Client.send` instead
|
||||
"""
|
||||
return self.send(Message(text=message), thread_id=thread_id, thread_type=thread_type)
|
||||
|
||||
def sendEmoji(self, emoji=None, size=EmojiSize.SMALL, thread_id=None, thread_type=ThreadType.USER):
|
||||
"""
|
||||
Sends an emoji to a thread
|
||||
|
||||
:param emoji: The chosen emoji to send. If not specified, the default `like` emoji is sent
|
||||
:param size: If not specified, a small emoji is sent
|
||||
:param thread_id: User/Group ID to send to. See :ref:`intro_threads`
|
||||
:param thread_type: See :ref:`intro_threads`
|
||||
:type size: models.EmojiSize
|
||||
:type thread_type: models.ThreadType
|
||||
:return: :ref:`Message ID <intro_message_ids>` of the sent emoji
|
||||
:raises: FBchatException if request failed
|
||||
Deprecated. Use :func:`fbchat.Client.send` instead
|
||||
"""
|
||||
thread_id, thread_type = self._getThread(thread_id, thread_type)
|
||||
data = self._getSendData(thread_id, thread_type)
|
||||
data['action_type'] = 'ma-type:user-generated-message'
|
||||
data['has_attachment'] = False
|
||||
data['specific_to_list[0]'] = 'fbid:' + thread_id
|
||||
data['specific_to_list[1]'] = 'fbid:' + self.uid
|
||||
|
||||
if emoji:
|
||||
data['body'] = emoji
|
||||
data['tags[0]'] = 'hot_emoji_size:' + size.name.lower()
|
||||
else:
|
||||
data["sticker_id"] = size.value
|
||||
|
||||
return self._doSendRequest(data)
|
||||
return self.send(Message(text=emoji, emoji_size=size), thread_id=thread_id, thread_type=thread_type)
|
||||
|
||||
def _uploadImage(self, image_path, data, mimetype):
|
||||
"""Upload an image and get the image_id for sending in a message"""
|
||||
@@ -1008,25 +984,13 @@ class Client(object):
|
||||
|
||||
def sendImage(self, image_id, message=None, thread_id=None, thread_type=ThreadType.USER, is_gif=False):
|
||||
"""
|
||||
Sends an already uploaded image to a thread. (Used by :func:`Client.sendRemoteImage` and :func:`Client.sendLocalImage`)
|
||||
|
||||
:param image_id: ID of an image that's already uploaded to Facebook
|
||||
:param message: Additional message
|
||||
:param thread_id: User/Group ID to send to. See :ref:`intro_threads`
|
||||
:param thread_type: See :ref:`intro_threads`
|
||||
:param is_gif: if sending GIF, True, else False
|
||||
:type thread_type: models.ThreadType
|
||||
:return: :ref:`Message ID <intro_message_ids>` of the sent image
|
||||
:raises: FBchatException if request failed
|
||||
Deprecated. Use :func:`fbchat.Client.send` instead
|
||||
"""
|
||||
thread_id, thread_type = self._getThread(thread_id, thread_type)
|
||||
data = self._getSendData(thread_id, thread_type)
|
||||
data = self._getSendData(message=message, thread_id=thread_id, thread_type=thread_type)
|
||||
|
||||
data['action_type'] = 'ma-type:user-generated-message'
|
||||
data['body'] = message or ''
|
||||
data['has_attachment'] = True
|
||||
data['specific_to_list[0]'] = 'fbid:' + str(thread_id)
|
||||
data['specific_to_list[1]'] = 'fbid:' + str(self.uid)
|
||||
|
||||
if not is_gif:
|
||||
data['image_ids[0]'] = image_id
|
||||
@@ -1083,7 +1047,7 @@ class Client(object):
|
||||
:raises: FBchatException if request failed
|
||||
"""
|
||||
thread_id, thread_type = self._getThread(thread_id, None)
|
||||
data = self._getSendData(thread_id, ThreadType.GROUP)
|
||||
data = self._getSendData(thread_id=thread_id, thread_type=ThreadType.GROUP)
|
||||
|
||||
data['action_type'] = 'ma-type:log-message'
|
||||
data['log_message_type'] = 'log:subscribe'
|
||||
@@ -1138,7 +1102,7 @@ class Client(object):
|
||||
# The thread is a user, so we change the user's nickname
|
||||
return self.changeNickname(title, thread_id, thread_id=thread_id, thread_type=thread_type)
|
||||
else:
|
||||
data = self._getSendData(thread_id, thread_type)
|
||||
data = self._getSendData(thread_id=thread_id, thread_type=thread_type)
|
||||
|
||||
data['action_type'] = 'ma-type:log-message'
|
||||
data['log_message_data[name]'] = title
|
||||
@@ -1240,11 +1204,17 @@ class Client(object):
|
||||
"""
|
||||
Sets an event reminder
|
||||
|
||||
:param thread_id: :ref:`Thread ID <intro_thread_ids>` to send event to
|
||||
..warning::
|
||||
Does not work in Python2.7
|
||||
|
||||
..todo::
|
||||
Make this work in Python2.7
|
||||
|
||||
:param thread_id: User/Group ID to send event to. See :ref:`intro_threads`
|
||||
:param time: Event time (unix time stamp)
|
||||
:param title: Event title
|
||||
:param location: Event location
|
||||
:param location_ir: Event location ID
|
||||
:param location: Event location name
|
||||
:param location_id: Event location ID
|
||||
:raises: FBchatException if request failed
|
||||
"""
|
||||
full_data = {
|
||||
@@ -1506,82 +1476,43 @@ class Client(object):
|
||||
except Exception:
|
||||
log.exception('An exception occured while reading attachments')
|
||||
|
||||
sticker = None
|
||||
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')
|
||||
))
|
||||
if mercury.get('attach_type'):
|
||||
image_metadata = a.get('imageMetadata', {})
|
||||
attach_type = mercury['attach_type']
|
||||
attachment = graphql_to_attachment(mercury.get('blob_attachment', {}))
|
||||
|
||||
if attach_type == ['file', 'video']:
|
||||
# TODO: Add more data here for audio files
|
||||
attachment.size = int(a['fileSize'])
|
||||
elif attach_type == 'share':
|
||||
# TODO: Add more data here for shared stuff (URLs, events and so on)
|
||||
pass
|
||||
attachments.append(attachment)
|
||||
if a['mercury'].get('sticker_attachment'):
|
||||
sticker = graphql_to_sticker(a['mercury']['sticker_attachment'])
|
||||
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
|
||||
emoji_size = get_emojisize_from_tags(metadata.get('tags'))
|
||||
|
||||
message = Message(
|
||||
text=delta.get('body'),
|
||||
mentions=mentions,
|
||||
emoji_size=emoji_size
|
||||
emoji_size=emoji_size,
|
||||
sticker=sticker,
|
||||
attachments=attachments
|
||||
)
|
||||
message.uid = mid
|
||||
message.author = author_id
|
||||
message.timestamp = ts
|
||||
message.attachments = attachments
|
||||
#message.is_read = None
|
||||
#message.reactions = []
|
||||
#message.reactions = {}
|
||||
thread_id, thread_type = getThreadIdAndThreadType(metadata)
|
||||
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)
|
||||
|
@@ -61,26 +61,88 @@ def get_customization_info(thread):
|
||||
rtn['own_nickname'] = pc[1].get('nickname')
|
||||
return rtn
|
||||
|
||||
|
||||
def graphql_to_sticker(s):
|
||||
if not s:
|
||||
return None
|
||||
sticker = Sticker(
|
||||
uid=s['id']
|
||||
)
|
||||
if s.get('pack'):
|
||||
sticker.pack = s['pack'].get('id')
|
||||
if s.get('sprite_image'):
|
||||
sticker.is_animated = True
|
||||
sticker.medium_sprite_image = s['sprite_image'].get('uri')
|
||||
sticker.large_sprite_image = s['sprite_image_2x'].get('uri')
|
||||
sticker.frames_per_row = s.get('frames_per_row')
|
||||
sticker.frames_per_col = s.get('frames_per_column')
|
||||
sticker.frame_rate = s.get('frame_rate')
|
||||
sticker.url = s.get('url')
|
||||
sticker.width = s.get('width')
|
||||
sticker.height = s.get('height')
|
||||
if s.get('label'):
|
||||
sticker.label = s['label']
|
||||
return sticker
|
||||
|
||||
def graphql_to_attachment(a):
|
||||
_type = a['__typename']
|
||||
if _type in ['MessageImage', 'MessageAnimatedImage']:
|
||||
return ImageAttachment(
|
||||
original_extension=a.get('original_extension') or (a['filename'].split('-')[0] if a.get('filename') else None),
|
||||
width=a.get('original_dimensions', {}).get('width'),
|
||||
height=a.get('original_dimensions', {}).get('height'),
|
||||
is_animated=_type=='MessageAnimatedImage',
|
||||
thumbnail_url=a.get('thumbnail', {}).get('uri'),
|
||||
preview=a.get('preview') or a.get('preview_image'),
|
||||
large_preview=a.get('large_preview'),
|
||||
animated_preview=a.get('animated_image'),
|
||||
uid=a.get('legacy_attachment_id')
|
||||
)
|
||||
elif _type == 'MessageVideo':
|
||||
return VideoAttachment(
|
||||
width=a.get('original_dimensions', {}).get('width'),
|
||||
height=a.get('original_dimensions', {}).get('height'),
|
||||
duration=a.get('playable_duration_in_ms'),
|
||||
preview_url=a.get('playable_url'),
|
||||
small_image=a.get('chat_image'),
|
||||
medium_image=a.get('inbox_image'),
|
||||
large_image=a.get('large_image'),
|
||||
uid=a.get('legacy_attachment_id')
|
||||
)
|
||||
elif _type == 'MessageFile':
|
||||
return FileAttachment(
|
||||
url=a.get('url'),
|
||||
name=a.get('filename'),
|
||||
is_malicious=a.get('is_malicious'),
|
||||
uid=a.get('message_file_fbid')
|
||||
)
|
||||
else:
|
||||
return Attachment(
|
||||
uid=a.get('legacy_attachment_id')
|
||||
)
|
||||
|
||||
def graphql_to_message(message):
|
||||
if message.get('message_sender') is None:
|
||||
message['message_sender'] = {}
|
||||
if message.get('message') is None:
|
||||
message['message'] = {}
|
||||
is_read = None
|
||||
if message.get('unread') is not None:
|
||||
is_read = not message['unread']
|
||||
return Message(
|
||||
message.get('message_id'),
|
||||
author=message.get('message_sender').get('id'),
|
||||
timestamp=message.get('timestamp_precise'),
|
||||
is_read=is_read,
|
||||
reactions=message.get('message_reactions'),
|
||||
rtn = Message(
|
||||
text=message.get('message').get('text'),
|
||||
mentions=[Mention(m.get('entity', {}).get('id'), offset=m.get('offset'), length=m.get('length')) for m in message.get('message').get('ranges', [])],
|
||||
sticker=message.get('sticker'),
|
||||
attachments=message.get('blob_attachments'),
|
||||
extensible_attachment=message.get('extensible_attachment')
|
||||
emoji_size=get_emojisize_from_tags(message.get('tags_list')),
|
||||
sticker=graphql_to_sticker(message.get('sticker'))
|
||||
)
|
||||
rtn.uid = str(message.get('message_id'))
|
||||
rtn.author = str(message.get('message_sender').get('id'))
|
||||
rtn.timestamp = message.get('timestamp_precise')
|
||||
if message.get('unread') is not None:
|
||||
rtn.is_read = not message['unread']
|
||||
rtn.reactions = {str(r['user']['id']):MessageReaction(r['reaction']) for r in message.get('message_reactions')}
|
||||
if message.get('blob_attachments') is not None:
|
||||
rtn.attachments = [graphql_to_attachment(attachment) for attachment in message['blob_attachments']]
|
||||
# TODO: This is still missing parsing:
|
||||
# message.get('extensible_attachment')
|
||||
return rtn
|
||||
|
||||
def graphql_to_user(user):
|
||||
if user.get('profile_picture') is None:
|
||||
|
107
fbchat/models.py
107
fbchat/models.py
@@ -164,51 +164,40 @@ class Page(Thread):
|
||||
|
||||
class Message(object):
|
||||
#: The actual message
|
||||
text = str
|
||||
text = None
|
||||
#: A list of :class:`Mention` objects
|
||||
mentions = list
|
||||
#: The message ID
|
||||
uid = str
|
||||
#: ID of the sender
|
||||
author = int
|
||||
#: Timestamp of when the message was sent
|
||||
timestamp = str
|
||||
#: Whether the message is read
|
||||
is_read = bool
|
||||
#: A list of message reactions
|
||||
reactions = list
|
||||
#: The actual message
|
||||
text = str
|
||||
#: A list of :class:`Mention` objects
|
||||
mentions = list
|
||||
#: An ID of a sent sticker
|
||||
sticker = str
|
||||
mentions = []
|
||||
#: A :class:`EmojiSize`. Size of a sent emoji
|
||||
emoji_size = None
|
||||
#: The message ID
|
||||
uid = None
|
||||
#: ID of the sender
|
||||
author = None
|
||||
#: Timestamp of when the message was sent
|
||||
timestamp = None
|
||||
#: Whether the message is read
|
||||
is_read = None
|
||||
#: A dict with user's IDs as keys, and their :class:`MessageReaction` as values
|
||||
reactions = {}
|
||||
#: The actual message
|
||||
text = None
|
||||
#: A :class:`Sticker`
|
||||
sticker = None
|
||||
#: A list of attachments
|
||||
attachments = list
|
||||
attachments = []
|
||||
|
||||
def __init__(self, uid, author=None, timestamp=None, is_read=None, reactions=None, text=None, mentions=None, sticker=None, attachments=None, extensible_attachment=None, emoji_size=None):
|
||||
def __init__(self, text=None, mentions=None, emoji_size=None, sticker=None, attachments=None):
|
||||
"""Represents a Facebook message"""
|
||||
self.uid = uid
|
||||
self.author = author
|
||||
self.timestamp = timestamp
|
||||
self.is_read = is_read
|
||||
if reactions is None:
|
||||
reactions = []
|
||||
self.reactions = reactions
|
||||
self.text = text
|
||||
if mentions is None:
|
||||
mentions = []
|
||||
self.mentions = mentions
|
||||
self.emoji_size = emoji_size
|
||||
self.sticker = sticker
|
||||
if attachments is None:
|
||||
attachments = []
|
||||
self.attachments = attachments
|
||||
if extensible_attachment is None:
|
||||
extensible_attachment = {}
|
||||
self.extensible_attachment = extensible_attachment
|
||||
self.emoji_size = emoji_size
|
||||
self.reactions = {}
|
||||
|
||||
def __repr__(self):
|
||||
return self.__unicode__()
|
||||
@@ -220,14 +209,40 @@ class Attachment(object):
|
||||
#: The attachment ID
|
||||
uid = str
|
||||
|
||||
def __init__(self, uid=None, mime_type=None):
|
||||
def __init__(self, uid=None):
|
||||
"""Represents a Facebook attachment"""
|
||||
self.uid = uid
|
||||
|
||||
class StickerAttachment(Attachment):
|
||||
def __init__(self, **kwargs):
|
||||
"""Represents a sticker that has been sent as a Facebook attachment - *Currently Incomplete!*"""
|
||||
super(StickerAttachment, self).__init__(**kwargs)
|
||||
class Sticker(Attachment):
|
||||
#: The sticker-pack's ID
|
||||
pack = None
|
||||
#: Whether the sticker is animated
|
||||
is_animated = False
|
||||
|
||||
# If the sticker is animated, the following should be present
|
||||
#: URL to a medium spritemap
|
||||
medium_sprite_image = None
|
||||
#: URL to a large spritemap
|
||||
large_sprite_image = None
|
||||
#: The amount of frames present in the spritemap pr. row
|
||||
frames_per_row = None
|
||||
#: The amount of frames present in the spritemap pr. coloumn
|
||||
frames_per_col = None
|
||||
#: The frame rate the spritemap is intended to be played in
|
||||
frame_rate = None
|
||||
|
||||
#: URL to the sticker's image
|
||||
url = None
|
||||
#: Width of the sticker
|
||||
width = None
|
||||
#: Height of the sticker
|
||||
height = None
|
||||
#: The sticker's label/name
|
||||
label = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Represents a Facebook sticker that has been sent to a Facebook thread as an attachment"""
|
||||
super(Sticker, self).__init__(*args, **kwargs)
|
||||
|
||||
class ShareAttachment(Attachment):
|
||||
def __init__(self, **kwargs):
|
||||
@@ -268,7 +283,7 @@ class ImageAttachment(Attachment):
|
||||
#: Whether the image is animated
|
||||
is_animated = bool
|
||||
|
||||
#: Url to a thumbnail of the image
|
||||
#: URL to a thumbnail of the image
|
||||
thumbnail_url = str
|
||||
|
||||
#: URL to a medium preview of the image
|
||||
@@ -300,7 +315,11 @@ class ImageAttachment(Attachment):
|
||||
"""
|
||||
super(ImageAttachment, self).__init__(**kwargs)
|
||||
self.original_extension = original_extension
|
||||
if width is not None:
|
||||
width = int(width)
|
||||
self.width = width
|
||||
if height is not None:
|
||||
height = int(height)
|
||||
self.height = height
|
||||
self.is_animated = is_animated
|
||||
self.thumbnail_url = thumbnail_url
|
||||
@@ -385,19 +404,25 @@ class VideoAttachment(Attachment):
|
||||
|
||||
|
||||
class Mention(object):
|
||||
#: The user ID the mention is pointing at
|
||||
user_id = str
|
||||
#: The thread ID the mention is pointing at
|
||||
thread_id = str
|
||||
#: The character where the mention starts
|
||||
offset = int
|
||||
#: The length of the mention
|
||||
length = int
|
||||
|
||||
def __init__(self, user_id, offset=0, length=10):
|
||||
def __init__(self, thread_id, offset=0, length=10):
|
||||
"""Represents a @mention"""
|
||||
self.user_id = user_id
|
||||
self.thread_id = thread_id
|
||||
self.offset = offset
|
||||
self.length = length
|
||||
|
||||
def __repr__(self):
|
||||
return self.__unicode__()
|
||||
|
||||
def __unicode__(self):
|
||||
return '<Mention {}: offset={} length={}>'.format(self.thread_id, self.offset, self.length)
|
||||
|
||||
class Enum(enum.Enum):
|
||||
"""Used internally by fbchat to support enumerations"""
|
||||
def __repr__(self):
|
||||
|
@@ -215,3 +215,14 @@ def get_jsmods_require(j, index):
|
||||
except (KeyError, IndexError) as e:
|
||||
log.warning('Error when getting jsmods_require: {}. Facebook might have changed protocol'.format(j))
|
||||
return None
|
||||
|
||||
def get_emojisize_from_tags(tags):
|
||||
if tags is None:
|
||||
return None
|
||||
tmp = [tag for tag in tags if tag.startswith('hot_emoji_size:')]
|
||||
if len(tmp) > 0:
|
||||
try:
|
||||
return LIKES[tmp[0].split(':')[1]]
|
||||
except (KeyError, IndexError):
|
||||
log.exception('Could not determine emoji size from {} - {}'.format(tags, tmp))
|
||||
return None
|
||||
|
Reference in New Issue
Block a user