diff --git a/fbchat/_attachment.py b/fbchat/_attachment.py index e7fe37e..4ee40b2 100644 --- a/fbchat/_attachment.py +++ b/fbchat/_attachment.py @@ -1,76 +1,48 @@ # -*- coding: UTF-8 -*- from __future__ import unicode_literals +import attr + +@attr.s(cmp=False) class Attachment(object): """Represents a Facebook attachment""" #: The attachment ID - uid = None - - def __init__(self, uid=None): - self.uid = uid + uid = attr.ib(None) +@attr.s(cmp=False) class UnsentMessage(Attachment): """Represents an unsent message attachment""" - def __init__(self, *args, **kwargs): - super(UnsentMessage, self).__init__(*args, **kwargs) - +@attr.s(cmp=False) class ShareAttachment(Attachment): """Represents a shared item (eg. URL) that has been sent as a Facebook attachment""" #: ID of the author of the shared post - author = None + author = attr.ib(None) #: Target URL - url = None + url = attr.ib(None) #: Original URL if Facebook redirects the URL - original_url = None + original_url = attr.ib(None) #: Title of the attachment - title = None + title = attr.ib(None) #: Description of the attachment - description = None + description = attr.ib(None) #: Name of the source - source = None + source = attr.ib(None) #: URL of the attachment image - image_url = None + image_url = attr.ib(None) #: URL of the original image if Facebook uses `safe_image` - original_image_url = None + original_image_url = attr.ib(None) #: Width of the image - image_width = None + image_width = attr.ib(None) #: Height of the image - image_height = None + image_height = attr.ib(None) #: List of additional attachments - attachments = None + attachments = attr.ib(factory=list, converter=lambda x: [] if x is None else x) - def __init__( - self, - author=None, - url=None, - original_url=None, - title=None, - description=None, - source=None, - image_url=None, - original_image_url=None, - image_width=None, - image_height=None, - attachments=None, - **kwargs - ): - super(ShareAttachment, self).__init__(**kwargs) - self.author = author - self.url = url - self.original_url = original_url - self.title = title - self.description = description - self.source = source - self.image_url = image_url - self.original_image_url = original_image_url - self.image_width = image_width - self.image_height = image_height - if attachments is None: - attachments = [] - self.attachments = attachments + # Put here for backwards compatibility, so that the init argument order is preserved + uid = attr.ib(None) diff --git a/fbchat/_file.py b/fbchat/_file.py index c73ae8d..85b2055 100644 --- a/fbchat/_file.py +++ b/fbchat/_file.py @@ -1,51 +1,45 @@ # -*- coding: UTF-8 -*- from __future__ import unicode_literals +import attr from ._attachment import Attachment +@attr.s(cmp=False) class FileAttachment(Attachment): """Represents a file that has been sent as a Facebook attachment""" #: Url where you can download the file - url = None + url = attr.ib(None) #: Size of the file in bytes - size = None + size = attr.ib(None) #: Name of the file - name = None + name = attr.ib(None) #: Whether Facebook determines that this file may be harmful - is_malicious = None + is_malicious = attr.ib(None) - def __init__(self, url=None, size=None, name=None, is_malicious=None, **kwargs): - super(FileAttachment, self).__init__(**kwargs) - self.url = url - self.size = size - self.name = name - self.is_malicious = is_malicious + # Put here for backwards compatibility, so that the init argument order is preserved + uid = attr.ib(None) +@attr.s(cmp=False) class AudioAttachment(Attachment): """Represents an audio file that has been sent as a Facebook attachment""" #: Name of the file - filename = None + filename = attr.ib(None) #: Url of the audio file - url = None + url = attr.ib(None) #: Duration of the audioclip in milliseconds - duration = None + duration = attr.ib(None) #: Audio type - audio_type = None + audio_type = attr.ib(None) - def __init__( - self, filename=None, url=None, duration=None, audio_type=None, **kwargs - ): - super(AudioAttachment, self).__init__(**kwargs) - self.filename = filename - self.url = url - self.duration = duration - self.audio_type = audio_type + # Put here for backwards compatibility, so that the init argument order is preserved + uid = attr.ib(None) +@attr.s(cmp=False, init=False) class ImageAttachment(Attachment): """Represents an image that has been sent as a Facebook attachment @@ -54,38 +48,38 @@ class ImageAttachment(Attachment): """ #: The extension of the original image (eg. 'png') - original_extension = None + original_extension = attr.ib(None) #: Width of original image - width = None + width = attr.ib(None, converter=lambda x: None if x is None else int(x)) #: Height of original image - height = None + height = attr.ib(None, converter=lambda x: None if x is None else int(x)) #: Whether the image is animated - is_animated = None + is_animated = attr.ib(None) #: URL to a thumbnail of the image - thumbnail_url = None + thumbnail_url = attr.ib(None) #: URL to a medium preview of the image - preview_url = None + preview_url = attr.ib(None) #: Width of the medium preview image - preview_width = None + preview_width = attr.ib(None) #: Height of the medium preview image - preview_height = None + preview_height = attr.ib(None) #: URL to a large preview of the image - large_preview_url = None + large_preview_url = attr.ib(None) #: Width of the large preview image - large_preview_width = None + large_preview_width = attr.ib(None) #: Height of the large preview image - large_preview_height = None + large_preview_height = attr.ib(None) #: URL to an animated preview of the image (eg. for gifs) - animated_preview_url = None + animated_preview_url = attr.ib(None) #: Width of the animated preview image - animated_preview_width = None + animated_preview_width = attr.ib(None) #: Height of the animated preview image - animated_preview_height = None + animated_preview_height = attr.ib(None) def __init__( self, @@ -129,40 +123,41 @@ class ImageAttachment(Attachment): self.animated_preview_height = animated_preview.get("height") +@attr.s(cmp=False, init=False) class VideoAttachment(Attachment): """Represents a video that has been sent as a Facebook attachment""" #: Size of the original video in bytes - size = None + size = attr.ib(None) #: Width of original video - width = None + width = attr.ib(None) #: Height of original video - height = None + height = attr.ib(None) #: Length of video in milliseconds - duration = None + duration = attr.ib(None) #: URL to very compressed preview video - preview_url = None + preview_url = attr.ib(None) #: URL to a small preview image of the video - small_image_url = None + small_image_url = attr.ib(None) #: Width of the small preview image - small_image_width = None + small_image_width = attr.ib(None) #: Height of the small preview image - small_image_height = None + small_image_height = attr.ib(None) #: URL to a medium preview image of the video - medium_image_url = None + medium_image_url = attr.ib(None) #: Width of the medium preview image - medium_image_width = None + medium_image_width = attr.ib(None) #: Height of the medium preview image - medium_image_height = None + medium_image_height = attr.ib(None) #: URL to a large preview image of the video - large_image_url = None + large_image_url = attr.ib(None) #: Width of the large preview image - large_image_width = None + large_image_width = attr.ib(None) #: Height of the large preview image - large_image_height = None + large_image_height = attr.ib(None) def __init__( self, diff --git a/fbchat/_group.py b/fbchat/_group.py index 727504c..62cf020 100644 --- a/fbchat/_group.py +++ b/fbchat/_group.py @@ -1,28 +1,32 @@ # -*- coding: UTF-8 -*- from __future__ import unicode_literals +import attr from ._thread import ThreadType, Thread +@attr.s(cmp=False, init=False) class Group(Thread): """Represents a Facebook group. Inherits `Thread`""" #: Unique list (set) of the group thread's participant user IDs - participants = None + participants = attr.ib(factory=set, converter=lambda x: set() if x is None else x) #: A dict, containing user nicknames mapped to their IDs - nicknames = None + nicknames = attr.ib(factory=dict, converter=lambda x: {} if x is None else x) #: A :class:`ThreadColor`. The groups's message color - color = None + color = attr.ib(None) #: The groups's default emoji - emoji = None + emoji = attr.ib(None) # Set containing user IDs of thread admins - admins = None + admins = attr.ib(factory=set, converter=lambda x: set() if x is None else x) # True if users need approval to join - approval_mode = None + approval_mode = attr.ib(None) # Set containing user IDs requesting to join - approval_requests = None + approval_requests = attr.ib( + factory=set, converter=lambda x: set() if x is None else x + ) # Link for joining group - join_link = None + join_link = attr.ib(None) def __init__( self, @@ -57,11 +61,12 @@ class Group(Thread): self.join_link = join_link +@attr.s(cmp=False, init=False) class Room(Group): """Deprecated. Use :class:`Group` instead""" # True is room is not discoverable - privacy_mode = None + privacy_mode = attr.ib(None) def __init__(self, uid, privacy_mode=None, **kwargs): super(Room, self).__init__(uid, **kwargs) diff --git a/fbchat/_location.py b/fbchat/_location.py index c31de64..5361bb5 100644 --- a/fbchat/_location.py +++ b/fbchat/_location.py @@ -1,9 +1,11 @@ # -*- coding: UTF-8 -*- from __future__ import unicode_literals +import attr from ._attachment import Attachment +@attr.s(cmp=False) class LocationAttachment(Attachment): """Represents a user location @@ -11,36 +13,34 @@ class LocationAttachment(Attachment): """ #: Latitude of the location - latitude = None + latitude = attr.ib(None) #: Longitude of the location - longitude = None + longitude = attr.ib(None) #: URL of image showing the map of the location - image_url = None + image_url = attr.ib(None, init=False) #: Width of the image - image_width = None + image_width = attr.ib(None, init=False) #: Height of the image - image_height = None + image_height = attr.ib(None, init=False) #: URL to Bing maps with the location - url = None + url = attr.ib(None, init=False) # Address of the location - address = None + address = attr.ib(None) - def __init__(self, latitude=None, longitude=None, address=None, **kwargs): - super(LocationAttachment, self).__init__(**kwargs) - self.latitude = latitude - self.longitude = longitude - self.address = address + # Put here for backwards compatibility, so that the init argument order is preserved + uid = attr.ib(None) +@attr.s(cmp=False, init=False) class LiveLocationAttachment(LocationAttachment): """Represents a live user location""" #: Name of the location - name = None + name = attr.ib(None) #: Timestamp when live location expires - expiration_time = None + expiration_time = attr.ib(None) #: True if live location is expired - is_expired = None + is_expired = attr.ib(None) def __init__(self, name=None, expiration_time=None, is_expired=None, **kwargs): super(LiveLocationAttachment, self).__init__(**kwargs) diff --git a/fbchat/_message.py b/fbchat/_message.py index ec199fd..3f6f03d 100644 --- a/fbchat/_message.py +++ b/fbchat/_message.py @@ -1,6 +1,7 @@ # -*- coding: UTF-8 -*- from __future__ import unicode_literals +import attr from string import Formatter from ._core import Enum @@ -25,94 +26,48 @@ class MessageReaction(Enum): NO = "👎" +@attr.s(cmp=False) class Mention(object): """Represents a @mention""" #: The thread ID the mention is pointing at - thread_id = None + thread_id = attr.ib() #: The character where the mention starts - offset = None + offset = attr.ib(0) #: The length of the mention - length = None - - def __init__(self, thread_id, offset=0, length=10): - self.thread_id = thread_id - self.offset = offset - self.length = length - - def __repr__(self): - return self.__unicode__() - - def __unicode__(self): - return "".format( - self.thread_id, self.offset, self.length - ) + length = attr.ib(10) +@attr.s(cmp=False) class Message(object): """Represents a Facebook message""" #: The actual message - text = None + text = attr.ib(None) #: A list of :class:`Mention` objects - mentions = None + mentions = attr.ib(factory=list, converter=lambda x: [] if x is None else x) #: A :class:`EmojiSize`. Size of a sent emoji - emoji_size = None + emoji_size = attr.ib(None) #: The message ID - uid = None + uid = attr.ib(None, init=False) #: ID of the sender - author = None + author = attr.ib(None, init=False) #: Timestamp of when the message was sent - timestamp = None + timestamp = attr.ib(None, init=False) #: Whether the message is read - is_read = None + is_read = attr.ib(None, init=False) #: A list of pepole IDs who read the message, works only with :func:`fbchat.Client.fetchThreadMessages` - read_by = None + read_by = attr.ib(factory=list, init=False) #: A dict with user's IDs as keys, and their :class:`MessageReaction` as values - reactions = None - #: The actual message - text = None + reactions = attr.ib(factory=dict, init=False) #: A :class:`Sticker` - sticker = None + sticker = attr.ib(None) #: A list of attachments - attachments = None + attachments = attr.ib(factory=list, converter=lambda x: [] if x is None else x) #: A list of :class:`QuickReply` - quick_replies = None + quick_replies = attr.ib(factory=list, converter=lambda x: [] if x is None else x) #: Whether the message is unsent (deleted for everyone) - unsent = None - - def __init__( - self, - text=None, - mentions=None, - emoji_size=None, - sticker=None, - attachments=None, - quick_replies=None, - ): - 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 quick_replies is None: - quick_replies = [] - self.quick_replies = quick_replies - self.reactions = {} - self.read_by = [] - self.deleted = False - - def __repr__(self): - return self.__unicode__() - - def __unicode__(self): - return "".format( - self.uid, repr(self.text), self.mentions, self.emoji_size, self.attachments - ) + unsent = attr.ib(False, init=False) @classmethod def formatMentions(cls, text, *args, **kwargs): diff --git a/fbchat/_page.py b/fbchat/_page.py index 3942559..9c696e0 100644 --- a/fbchat/_page.py +++ b/fbchat/_page.py @@ -1,22 +1,24 @@ # -*- coding: UTF-8 -*- from __future__ import unicode_literals +import attr from ._thread import ThreadType, Thread +@attr.s(cmp=False, init=False) class Page(Thread): """Represents a Facebook page. Inherits `Thread`""" #: The page's custom url - url = None + url = attr.ib(None) #: The name of the page's location city - city = None + city = attr.ib(None) #: Amount of likes the page has - likes = None + likes = attr.ib(None) #: Some extra information about the page - sub_title = None + sub_title = attr.ib(None) #: The page's category - category = None + category = attr.ib(None) def __init__( self, diff --git a/fbchat/_plan.py b/fbchat/_plan.py index 21425be..b6f7826 100644 --- a/fbchat/_plan.py +++ b/fbchat/_plan.py @@ -1,47 +1,28 @@ # -*- coding: UTF-8 -*- from __future__ import unicode_literals +import attr + +@attr.s(cmp=False) class Plan(object): """Represents a plan""" #: ID of the plan - uid = None + uid = attr.ib(None, init=False) #: Plan time (unix time stamp), only precise down to the minute - time = None + time = attr.ib(converter=int) #: Plan title - title = None + title = attr.ib() #: Plan location name - location = None + location = attr.ib(None, converter=lambda x: x or "") #: Plan location ID - location_id = None + location_id = attr.ib(None, converter=lambda x: x or "") #: ID of the plan creator - author_id = None + author_id = attr.ib(None, init=False) #: List of the people IDs who will take part in the plan - going = None + going = attr.ib(factory=list, init=False) #: List of the people IDs who won't take part in the plan - declined = None + declined = attr.ib(factory=list, init=False) #: List of the people IDs who are invited to the plan - invited = None - - def __init__(self, time, title, location=None, location_id=None): - self.time = int(time) - self.title = title - self.location = location or "" - self.location_id = location_id or "" - self.author_id = None - self.going = [] - self.declined = [] - self.invited = [] - - def __repr__(self): - return self.__unicode__() - - def __unicode__(self): - return "".format( - self.uid, - repr(self.title), - self.time, - repr(self.location), - repr(self.location_id), - ) + invited = attr.ib(factory=list, init=False) diff --git a/fbchat/_poll.py b/fbchat/_poll.py index 3d1bf05..c2c02c0 100644 --- a/fbchat/_poll.py +++ b/fbchat/_poll.py @@ -1,54 +1,34 @@ # -*- coding: UTF-8 -*- from __future__ import unicode_literals +import attr + +@attr.s(cmp=False) class Poll(object): """Represents a poll""" #: ID of the poll - uid = None + uid = attr.ib(None, init=False) #: Title of the poll - title = None + title = attr.ib() #: List of :class:`PollOption`, can be fetched with :func:`fbchat.Client.fetchPollOptions` - options = None + options = attr.ib() #: Options count - options_count = None - - def __init__(self, title, options): - self.title = title - self.options = options - - def __repr__(self): - return self.__unicode__() - - def __unicode__(self): - return "".format( - self.uid, repr(self.title), self.options - ) + options_count = attr.ib(None, init=False) +@attr.s(cmp=False) class PollOption(object): """Represents a poll option""" #: ID of the poll option - uid = None + uid = attr.ib(None, init=False) #: Text of the poll option - text = None + text = attr.ib() #: Whether vote when creating or client voted - vote = None + vote = attr.ib(False) #: ID of the users who voted for this poll option - voters = None + voters = attr.ib(None, init=False) #: Votes count - votes_count = None - - def __init__(self, text, vote=False): - self.text = text - self.vote = vote - - def __repr__(self): - return self.__unicode__() - - def __unicode__(self): - return "".format( - self.uid, repr(self.text), self.voters - ) + votes_count = attr.ib(None, init=False) diff --git a/fbchat/_quick_reply.py b/fbchat/_quick_reply.py index 11ab094..60323bf 100644 --- a/fbchat/_quick_reply.py +++ b/fbchat/_quick_reply.py @@ -1,40 +1,32 @@ # -*- coding: UTF-8 -*- from __future__ import unicode_literals +import attr from ._attachment import Attachment +@attr.s(cmp=False) class QuickReply(object): """Represents a quick reply""" #: Payload of the quick reply - payload = None + payload = attr.ib(None) #: External payload for responses - external_payload = None + external_payload = attr.ib(None, init=False) #: Additional data - data = None + data = attr.ib(None) #: Whether it's a response for a quick reply - is_response = None - - def __init__(self, payload=None, data=None, is_response=False): - 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) + is_response = attr.ib(False) +@attr.s(cmp=False, init=False) class QuickReplyText(QuickReply): """Represents a text quick reply""" #: Title of the quick reply - title = None + title = attr.ib(None) #: URL of the quick reply image (optional) - image_url = None + image_url = attr.ib(None) #: Type of the quick reply _type = "text" @@ -44,6 +36,7 @@ class QuickReplyText(QuickReply): self.image_url = image_url +@attr.s(cmp=False, init=False) class QuickReplyLocation(QuickReply): """Represents a location quick reply (Doesn't work on mobile)""" @@ -55,11 +48,12 @@ class QuickReplyLocation(QuickReply): self.is_response = False +@attr.s(cmp=False, init=False) class QuickReplyPhoneNumber(QuickReply): """Represents a phone number quick reply (Doesn't work on mobile)""" #: URL of the quick reply image (optional) - image_url = None + image_url = attr.ib(None) #: Type of the quick reply _type = "user_phone_number" @@ -68,11 +62,12 @@ class QuickReplyPhoneNumber(QuickReply): self.image_url = image_url +@attr.s(cmp=False, init=False) class QuickReplyEmail(QuickReply): """Represents an email quick reply (Doesn't work on mobile)""" #: URL of the quick reply image (optional) - image_url = None + image_url = attr.ib(None) #: Type of the quick reply _type = "user_email" diff --git a/fbchat/_sticker.py b/fbchat/_sticker.py index fadab3d..e90655d 100644 --- a/fbchat/_sticker.py +++ b/fbchat/_sticker.py @@ -1,37 +1,39 @@ # -*- coding: UTF-8 -*- from __future__ import unicode_literals +import attr from ._attachment import Attachment +@attr.s(cmp=False, init=False) class Sticker(Attachment): """Represents a Facebook sticker that has been sent to a thread as an attachment""" #: The sticker-pack's ID - pack = None + pack = attr.ib(None) #: Whether the sticker is animated - is_animated = False + is_animated = attr.ib(False) # If the sticker is animated, the following should be present #: URL to a medium spritemap - medium_sprite_image = None + medium_sprite_image = attr.ib(None) #: URL to a large spritemap - large_sprite_image = None + large_sprite_image = attr.ib(None) #: The amount of frames present in the spritemap pr. row - frames_per_row = None + frames_per_row = attr.ib(None) #: The amount of frames present in the spritemap pr. coloumn - frames_per_col = None + frames_per_col = attr.ib(None) #: The frame rate the spritemap is intended to be played in - frame_rate = None + frame_rate = attr.ib(None) #: URL to the sticker's image - url = None + url = attr.ib(None) #: Width of the sticker - width = None + width = attr.ib(None) #: Height of the sticker - height = None + height = attr.ib(None) #: The sticker's label/name - label = None + label = attr.ib(None) - def __init__(self, *args, **kwargs): - super(Sticker, self).__init__(*args, **kwargs) + def __init__(self, uid=None): + super(Sticker, self).__init__(uid=uid) diff --git a/fbchat/_thread.py b/fbchat/_thread.py index 040493e..6b4641e 100644 --- a/fbchat/_thread.py +++ b/fbchat/_thread.py @@ -1,6 +1,7 @@ # -*- coding: UTF-8 -*- from __future__ import unicode_literals +import attr from ._core import Enum @@ -42,23 +43,24 @@ class ThreadColor(Enum): BILOBA_FLOWER = "#a695c7" +@attr.s(cmp=False, init=False) class Thread(object): """Represents a Facebook thread""" #: The unique identifier of the thread. Can be used a `thread_id`. See :ref:`intro_threads` for more info - uid = None + uid = attr.ib(converter=str) #: Specifies the type of thread. Can be used a `thread_type`. See :ref:`intro_threads` for more info - type = None + type = attr.ib() #: A url to the thread's picture - photo = None + photo = attr.ib(None) #: The name of the thread - name = None + name = attr.ib(None) #: Timestamp of last message - last_message_timestamp = None + last_message_timestamp = attr.ib(None) #: Number of messages in the thread - message_count = None + message_count = attr.ib(None) #: Set :class:`Plan` - plan = None + plan = attr.ib(None) def __init__( self, @@ -77,9 +79,3 @@ class Thread(object): self.last_message_timestamp = last_message_timestamp self.message_count = message_count self.plan = plan - - def __repr__(self): - return self.__unicode__() - - def __unicode__(self): - return "<{} {} ({})>".format(self.type.name, self.name, self.uid) diff --git a/fbchat/_user.py b/fbchat/_user.py index 0da1f3f..26e8f4d 100644 --- a/fbchat/_user.py +++ b/fbchat/_user.py @@ -1,6 +1,7 @@ # -*- coding: UTF-8 -*- from __future__ import unicode_literals +import attr from ._core import Enum from ._thread import ThreadType, Thread @@ -12,29 +13,30 @@ class TypingStatus(Enum): TYPING = 1 +@attr.s(cmp=False, init=False) class User(Thread): """Represents a Facebook user. Inherits `Thread`""" #: The profile url - url = None + url = attr.ib(None) #: The users first name - first_name = None + first_name = attr.ib(None) #: The users last name - last_name = None + last_name = attr.ib(None) #: Whether the user and the client are friends - is_friend = None + is_friend = attr.ib(None) #: The user's gender - gender = None + gender = attr.ib(None) #: From 0 to 1. How close the client is to the user - affinity = None + affinity = attr.ib(None) #: The user's nickname - nickname = None + nickname = attr.ib(None) #: The clients nickname, as seen by the user - own_nickname = None + own_nickname = attr.ib(None) #: A :class:`ThreadColor`. The message color - color = None + color = attr.ib(None) #: The default emoji - emoji = None + emoji = attr.ib(None) def __init__( self, @@ -64,23 +66,11 @@ class User(Thread): self.emoji = emoji +@attr.s(cmp=False) class ActiveStatus(object): #: Whether the user is active now - active = None + active = attr.ib(None) #: Timestamp when the user was last active - last_active = None + last_active = attr.ib(None) #: Whether the user is playing Messenger game now - in_game = None - - def __init__(self, active=None, last_active=None, in_game=None): - self.active = active - self.last_active = last_active - self.in_game = in_game - - def __repr__(self): - return self.__unicode__() - - def __unicode__(self): - return "".format( - self.active, self.last_active, self.in_game - ) + in_game = attr.ib(None)