From 2aea401c79cd026a4ee29bd7cd744ba59cf01e0b Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Thu, 23 Jan 2020 14:06:00 +0100 Subject: [PATCH] Refactor threads file structure --- fbchat/__init__.py | 16 +++-- fbchat/_client.py | 96 ++++++++++++------------- fbchat/_events/__init__.py | 17 ++--- fbchat/_events/_client_payload.py | 12 ++-- fbchat/_events/_common.py | 12 ++-- fbchat/_events/_delta_class.py | 34 ++++----- fbchat/_events/_delta_type.py | 16 +++-- fbchat/_message.py | 4 +- fbchat/_threads/__init__.py | 4 ++ fbchat/{_thread.py => _threads/_abc.py} | 13 +++- fbchat/{ => _threads}/_group.py | 12 ++-- fbchat/{ => _threads}/_page.py | 7 +- fbchat/{ => _threads}/_user.py | 7 +- tests/{ => threads}/test_group.py | 0 tests/{ => threads}/test_page.py | 2 +- tests/{ => threads}/test_thread.py | 0 tests/{ => threads}/test_user.py | 2 +- 17 files changed, 138 insertions(+), 116 deletions(-) create mode 100644 fbchat/_threads/__init__.py rename fbchat/{_thread.py => _threads/_abc.py} (99%) rename fbchat/{ => _threads}/_group.py (97%) rename fbchat/{ => _threads}/_page.py (94%) rename fbchat/{ => _threads}/_user.py (98%) rename tests/{ => threads}/test_group.py (100%) rename tests/{ => threads}/test_page.py (94%) rename tests/{ => threads}/test_thread.py (100%) rename tests/{ => threads}/test_user.py (99%) diff --git a/fbchat/__init__.py b/fbchat/__init__.py index 0ea3bad..c701749 100644 --- a/fbchat/__init__.py +++ b/fbchat/__init__.py @@ -27,10 +27,18 @@ from ._exception import ( PleaseRefresh, ) from ._session import Session -from ._thread import ThreadLocation, ThreadABC, Thread -from ._user import User, UserData, ActiveStatus -from ._group import Group, GroupData -from ._page import Page, PageData +from ._threads import ( + ThreadLocation, + ThreadABC, + Thread, + User, + UserData, + ActiveStatus, + Group, + GroupData, + Page, + PageData, +) from ._message import EmojiSize, Mention, Message, MessageData from ._attachment import Attachment, UnsentMessage, ShareAttachment from ._sticker import Sticker diff --git a/fbchat/_client.py b/fbchat/_client.py index c6dd7f0..698caff 100644 --- a/fbchat/_client.py +++ b/fbchat/_client.py @@ -2,17 +2,7 @@ import attr import datetime from ._common import log, attrs_default -from . import ( - _exception, - _util, - _graphql, - _session, - _user, - _page, - _group, - _thread, - _message, -) +from . import _exception, _util, _graphql, _session, _threads, _message from typing import Sequence, Iterable, Tuple, Optional, Set @@ -32,7 +22,7 @@ class Client: #: The session to use when making requests. session = attr.ib(type=_session.Session) - def fetch_users(self) -> Sequence[_user.UserData]: + def fetch_users(self) -> Sequence[_threads.UserData]: """Fetch users the client is currently chatting with. This is very close to your friend list, with the follow differences: @@ -60,10 +50,10 @@ class Client: if data["type"] not in ["user", "friend"] or data["id"] in ["0", 0]: log.warning("Invalid user data %s", data) continue # Skip invalid users - users.append(_user.UserData._from_all_fetch(self.session, data)) + users.append(_threads.UserData._from_all_fetch(self.session, data)) return users - def search_for_users(self, name: str, limit: int) -> Iterable[_user.UserData]: + def search_for_users(self, name: str, limit: int) -> Iterable[_threads.UserData]: """Find and get users by their name. The returned users are ordered by relevance. @@ -85,11 +75,11 @@ class Client: ) return ( - _user.UserData._from_graphql(self.session, node) + _threads.UserData._from_graphql(self.session, node) for node in j[name]["users"]["nodes"] ) - def search_for_pages(self, name: str, limit: int) -> Iterable[_page.PageData]: + def search_for_pages(self, name: str, limit: int) -> Iterable[_threads.PageData]: """Find and get pages by their name. The returned pages are ordered by relevance. @@ -111,11 +101,11 @@ class Client: ) return ( - _page.PageData._from_graphql(self.session, node) + _threads.PageData._from_graphql(self.session, node) for node in j[name]["pages"]["nodes"] ) - def search_for_groups(self, name: str, limit: int) -> Iterable[_group.GroupData]: + def search_for_groups(self, name: str, limit: int) -> Iterable[_threads.GroupData]: """Find and get group threads by their name. The returned groups are ordered by relevance. @@ -137,11 +127,11 @@ class Client: ) return ( - _group.GroupData._from_graphql(self.session, node) + _threads.GroupData._from_graphql(self.session, node) for node in j["viewer"]["groups"]["nodes"] ) - def search_for_threads(self, name: str, limit: int) -> Iterable[_thread.ThreadABC]: + def search_for_threads(self, name: str, limit: int) -> Iterable[_threads.ThreadABC]: """Find and get threads by their name. The returned threads are ordered by relevance. @@ -165,12 +155,12 @@ class Client: for node in j[name]["threads"]["nodes"]: if node["__typename"] == "User": - yield _user.UserData._from_graphql(self.session, node) + yield _threads.UserData._from_graphql(self.session, node) elif node["__typename"] == "MessageThread": # MessageThread => Group thread - yield _group.GroupData._from_graphql(self.session, node) + yield _threads.GroupData._from_graphql(self.session, node) elif node["__typename"] == "Page": - yield _page.PageData._from_graphql(self.session, node) + yield _threads.PageData._from_graphql(self.session, node) elif node["__typename"] == "Group": # We don't handle Facebook "Groups" pass @@ -189,17 +179,17 @@ class Client: for node in j["graphql_payload"]["message_threads"]: type_ = node["thread_type"] if type_ == "GROUP": - thread = _group.Group( + thread = _threads.Group( session=self.session, id=node["thread_key"]["thread_fbid"] ) elif type_ == "ONE_TO_ONE": - thread = _thread.Thread( + thread = _threads.Thread( session=self.session, id=node["thread_key"]["other_user_id"] ) # if True: # TODO: This check! - # thread = _user.UserData._from_graphql(self.session, node) + # thread = _threads.UserData._from_graphql(self.session, node) # else: - # thread = _page.PageData._from_graphql(self.session, node) + # thread = _threads.PageData._from_graphql(self.session, node) else: thread = None log.warning("Unknown thread type %s, data: %s", type_, node) @@ -213,7 +203,7 @@ class Client: def search_messages( self, query: str, limit: Optional[int] - ) -> Iterable[Tuple[_thread.ThreadABC, int]]: + ) -> Iterable[Tuple[_threads.ThreadABC, int]]: """Search for messages in all threads. Intended to be used alongside `ThreadABC.search_messages` @@ -285,7 +275,7 @@ class Client: log.debug(entries) return entries - def fetch_thread_info(self, ids: Iterable[str]) -> Iterable[_thread.ThreadABC]: + def fetch_thread_info(self, ids: Iterable[str]) -> Iterable[_threads.ThreadABC]: """Fetch threads' info from IDs, unordered. Warning: @@ -336,7 +326,7 @@ class Client: entry = entry["message_thread"] if entry.get("thread_type") == "GROUP": _id = entry["thread_key"]["thread_fbid"] - yield _group.GroupData._from_graphql(self.session, entry) + yield _threads.GroupData._from_graphql(self.session, entry) elif entry.get("thread_type") == "ONE_TO_ONE": _id = entry["thread_key"]["other_user_id"] if pages_and_users.get(_id) is None: @@ -345,9 +335,9 @@ class Client: ) entry.update(pages_and_users[_id]) if "first_name" in entry: - yield _user.UserData._from_graphql(self.session, entry) + yield _threads.UserData._from_graphql(self.session, entry) else: - yield _page.PageData._from_graphql(self.session, entry) + yield _threads.PageData._from_graphql(self.session, entry) else: raise _exception.ParseError("Unknown thread type", data=entry) @@ -367,9 +357,9 @@ class Client: for node in j["viewer"]["message_threads"]["nodes"]: _type = node.get("thread_type") if _type == "GROUP": - rtn.append(_group.GroupData._from_graphql(self.session, node)) + rtn.append(_threads.GroupData._from_graphql(self.session, node)) elif _type == "ONE_TO_ONE": - rtn.append(_user.UserData._from_thread_fetch(self.session, node)) + rtn.append(_threads.UserData._from_thread_fetch(self.session, node)) else: rtn.append(None) log.warning("Unknown thread type: %s, data: %s", _type, node) @@ -378,8 +368,8 @@ class Client: def fetch_threads( self, limit: Optional[int], - location: _thread.ThreadLocation = _thread.ThreadLocation.INBOX, - ) -> Iterable[_thread.ThreadABC]: + location: _threads.ThreadLocation = _threads.ThreadLocation.INBOX, + ) -> Iterable[_threads.ThreadABC]: """Fetch the client's thread list. Args: @@ -422,7 +412,7 @@ class Client: if not before: raise ValueError("Too many unknown threads.") - def fetch_unread(self) -> Sequence[_thread.ThreadABC]: + def fetch_unread(self) -> Sequence[_threads.ThreadABC]: """Fetch unread threads. Warning: @@ -439,13 +429,14 @@ class Client: result = j["unread_thread_fbids"][0] # TODO: Parse Pages? return [ - _group.Group(session=self.session, id=id_) for id_ in result["thread_fbids"] + _threads.Group(session=self.session, id=id_) + for id_ in result["thread_fbids"] ] + [ - _user.User(session=self.session, id=id_) + _threads.User(session=self.session, id=id_) for id_ in result["other_user_fbids"] ] - def fetch_unseen(self) -> Sequence[_thread.ThreadABC]: + def fetch_unseen(self) -> Sequence[_threads.ThreadABC]: """Fetch unseen / new threads. Warning: @@ -456,9 +447,10 @@ class Client: result = j["unseen_thread_fbids"][0] # TODO: Parse Pages? return [ - _group.Group(session=self.session, id=id_) for id_ in result["thread_fbids"] + _threads.Group(session=self.session, id=id_) + for id_ in result["thread_fbids"] ] + [ - _user.User(session=self.session, id=id_) + _threads.User(session=self.session, id=id_) for id_ in result["other_user_fbids"] ] @@ -529,7 +521,9 @@ class Client: j = self.session._payload_post("/ajax/mercury/change_read_status.php", data) - def mark_as_read(self, threads: Iterable[_thread.ThreadABC], at: datetime.datetime): + def mark_as_read( + self, threads: Iterable[_threads.ThreadABC], at: datetime.datetime + ): """Mark threads as read. All messages inside the specified threads will be marked as read. @@ -541,7 +535,7 @@ class Client: return self._read_status(True, threads, at) def mark_as_unread( - self, threads: Iterable[_thread.ThreadABC], at: datetime.datetime + self, threads: Iterable[_threads.ThreadABC], at: datetime.datetime ): """Mark threads as unread. @@ -560,7 +554,7 @@ class Client: ) def move_threads( - self, location: _thread.ThreadLocation, threads: Iterable[_thread.ThreadABC] + self, location: _threads.ThreadLocation, threads: Iterable[_threads.ThreadABC] ): """Move threads to specified location. @@ -568,10 +562,10 @@ class Client: location: INBOX, PENDING, ARCHIVED or OTHER threads: Threads to move """ - if location == _thread.ThreadLocation.PENDING: - location = _thread.ThreadLocation.OTHER + if location == _threads.ThreadLocation.PENDING: + location = _threads.ThreadLocation.OTHER - if location == _thread.ThreadLocation.ARCHIVED: + if location == _threads.ThreadLocation.ARCHIVED: data_archive = {} data_unpin = {} for thread in threads: @@ -587,9 +581,9 @@ class Client: data = {} for i, thread in enumerate(threads): data["{}[{}]".format(location.name.lower(), i)] = thread.id - j = self.session._payload_post("/ajax/mercury/move_thread.php", data) + j = self.session._payload_post("/ajax/mercury/move_threads.php", data) - def delete_threads(self, threads: Iterable[_thread.ThreadABC]): + def delete_threads(self, threads: Iterable[_threads.ThreadABC]): """Bulk delete threads. Args: @@ -608,7 +602,7 @@ class Client: "/ajax/mercury/change_pinned_status.php?dpr=1", data_unpin ) j_delete = self.session._payload_post( - "/ajax/mercury/delete_thread.php?dpr=1", data_delete + "/ajax/mercury/delete_threads.php?dpr=1", data_delete ) def delete_messages(self, messages: Iterable[_message.Message]): diff --git a/fbchat/_events/__init__.py b/fbchat/_events/__init__.py index fcfc5b3..9806ccb 100644 --- a/fbchat/_events/__init__.py +++ b/fbchat/_events/__init__.py @@ -6,7 +6,7 @@ from ._client_payload import * from ._delta_class import * from ._delta_type import * -from .. import _exception, _util, _user, _group, _thread +from .. import _exception, _util, _threads from typing import Mapping @@ -20,15 +20,15 @@ class Typing(ThreadEvent): @classmethod def _parse_orca(cls, session, data): - author = _user.User(session=session, id=str(data["sender_fbid"])) + author = _threads.User(session=session, id=str(data["sender_fbid"])) status = data["state"] == 1 return cls(author=author, thread=author, status=status) @classmethod def _parse(cls, session, data): # TODO: Rename this method - author = _user.User(session=session, id=str(data["sender_fbid"])) - thread = _group.Group(session=session, id=str(data["thread"])) + author = _threads.User(session=session, id=str(data["sender_fbid"])) + thread = _threads.Group(session=session, id=str(data["thread"])) status = data["state"] == 1 return cls(author=author, thread=thread, status=status) @@ -38,11 +38,11 @@ class FriendRequest(Event): """Somebody sent a friend request.""" #: The user that sent the request - author = attr.ib(type=_user.User) + author = attr.ib(type="_threads.User") @classmethod def _parse(cls, session, data): - author = _user.User(session=session, id=str(data["from"])) + author = _threads.User(session=session, id=str(data["from"])) return cls(author=author) @@ -56,14 +56,15 @@ class Presence(Event): # TODO: Document this better! #: User ids mapped to their active status - statuses = attr.ib(type=Mapping[str, _user.ActiveStatus]) + statuses = attr.ib(type=Mapping[str, _threads.ActiveStatus]) #: ``True`` if the list is fully updated and ``False`` if it's partially updated full = attr.ib(type=bool) @classmethod def _parse(cls, session, data): statuses = { - str(d["u"]): _user.ActiveStatus._from_orca_presence(d) for d in data["list"] + str(d["u"]): _threads.ActiveStatus._from_orca_presence(d) + for d in data["list"] } return cls(statuses=statuses, full=data["list_type"] == "full") diff --git a/fbchat/_events/_client_payload.py b/fbchat/_events/_client_payload.py index 5486483..6a12e1d 100644 --- a/fbchat/_events/_client_payload.py +++ b/fbchat/_events/_client_payload.py @@ -1,7 +1,7 @@ import attr import datetime from ._common import attrs_event, UnknownEvent, ThreadEvent -from .. import _exception, _util, _user, _message +from .. import _exception, _util, _threads, _message from typing import Optional @@ -25,7 +25,7 @@ class ReactionEvent(ThreadEvent): def _parse(cls, session, data): thread = cls._get_thread(session, data) return cls( - author=_user.User(session=session, id=str(data["userId"])), + author=_threads.User(session=session, id=str(data["userId"])), thread=thread, message=_message.Message(thread=thread, id=data["messageId"]), reaction=data["reaction"] if data["action"] == 0 else None, @@ -40,7 +40,7 @@ class UserStatusEvent(ThreadEvent): @classmethod def _parse(cls, session, data): return cls( - author=_user.User(session=session, id=str(data["actorFbid"])), + author=_threads.User(session=session, id=str(data["actorFbid"])), thread=cls._get_thread(session, data), blocked=not data["canViewerReply"], ) @@ -59,7 +59,7 @@ class LiveLocationEvent(ThreadEvent): thread = cls._get_thread(session, data) for location_data in data["messageLiveLocations"]: message = _message.Message(thread=thread, id=data["messageId"]) - author = _user.User(session=session, id=str(location_data["senderId"])) + author = _threads.User(session=session, id=str(location_data["senderId"])) location = _location.LiveLocationAttachment._from_pull(location_data) return None @@ -78,7 +78,7 @@ class UnsendEvent(ThreadEvent): def _parse(cls, session, data): thread = cls._get_thread(session, data) return cls( - author=_user.User(session=session, id=str(data["senderID"])), + author=_threads.User(session=session, id=str(data["senderID"])), thread=thread, message=_message.Message(thread=thread, id=data["messageID"]), at=_util.millis_to_datetime(data["deletionTimestamp"]), @@ -99,7 +99,7 @@ class MessageReplyEvent(ThreadEvent): metadata = data["message"]["messageMetadata"] thread = cls._get_thread(session, metadata) return cls( - author=_user.User(session=session, id=str(metadata["actorFbId"])), + author=_threads.User(session=session, id=str(metadata["actorFbId"])), thread=thread, message=_message.MessageData._from_reply(thread, data["message"]), replied_to=_message.MessageData._from_reply( diff --git a/fbchat/_events/_common.py b/fbchat/_events/_common.py index 913bb43..46de65a 100644 --- a/fbchat/_events/_common.py +++ b/fbchat/_events/_common.py @@ -1,7 +1,7 @@ import attr import abc from .._common import kw_only -from .. import _exception, _util, _thread, _group, _user, _message +from .. import _exception, _util, _threads, _message from typing import Any @@ -38,9 +38,9 @@ class ThreadEvent(Event): """Represent an event that was done by a user/page in a thread.""" #: The person who did the action - author = attr.ib(type=_user.User) # Or Union[User, Page]? + author = attr.ib(type="_threads.User") # Or Union[User, Page]? #: Thread that the action was done in - thread = attr.ib(type=_thread.ThreadABC) + thread = attr.ib(type="_threads.ThreadABC") @staticmethod def _get_thread(session, data): @@ -48,15 +48,15 @@ class ThreadEvent(Event): key = data["threadKey"] if "threadFbId" in key: - return _group.Group(session=session, id=str(key["threadFbId"])) + return _threads.Group(session=session, id=str(key["threadFbId"])) elif "otherUserFbId" in key: - return _user.User(session=session, id=str(key["otherUserFbId"])) + return _threads.User(session=session, id=str(key["otherUserFbId"])) raise _exception.ParseError("Could not find thread data", data=data) @staticmethod def _parse_metadata(session, data): metadata = data["messageMetadata"] - author = _user.User(session=session, id=metadata["actorFbId"]) + author = _threads.User(session=session, id=metadata["actorFbId"]) thread = ThreadEvent._get_thread(session, metadata) at = _util.millis_to_datetime(int(metadata["timestamp"])) return author, thread, at diff --git a/fbchat/_events/_delta_class.py b/fbchat/_events/_delta_class.py index 6e0ca77..7e496a0 100644 --- a/fbchat/_events/_delta_class.py +++ b/fbchat/_events/_delta_class.py @@ -1,7 +1,7 @@ import attr import datetime from ._common import attrs_event, Event, UnknownEvent, ThreadEvent -from .. import _util, _user, _group, _thread, _message +from .. import _util, _threads, _message from typing import Sequence, Optional @@ -12,9 +12,9 @@ class PeopleAdded(ThreadEvent): # TODO: Add message id - thread = attr.ib(type=_group.Group) # Set the correct type + thread = attr.ib(type="_threads.Group") # Set the correct type #: The people who got added - added = attr.ib(type=Sequence[_user.User]) + added = attr.ib(type=Sequence["_threads.User"]) #: When the people were added at = attr.ib(type=datetime.datetime) @@ -23,7 +23,7 @@ class PeopleAdded(ThreadEvent): author, thread, at = cls._parse_metadata(session, data) added = [ # TODO: Parse user name - _user.User(session=session, id=x["userFbId"]) + _threads.User(session=session, id=x["userFbId"]) for x in data["addedParticipants"] ] return cls(author=author, thread=thread, added=added, at=at) @@ -35,7 +35,7 @@ class PersonRemoved(ThreadEvent): # TODO: Add message id - thread = attr.ib(type=_group.Group) # Set the correct type + thread = attr.ib(type="_threads.Group") # Set the correct type #: Person who got removed removed = attr.ib(type=_message.Message) #: When the person were removed @@ -44,7 +44,7 @@ class PersonRemoved(ThreadEvent): @classmethod def _parse(cls, session, data): author, thread, at = cls._parse_metadata(session, data) - removed = _user.User(session=session, id=data["leftParticipantFbId"]) + removed = _threads.User(session=session, id=data["leftParticipantFbId"]) return cls(author=author, thread=thread, removed=removed, at=at) @@ -52,7 +52,7 @@ class PersonRemoved(ThreadEvent): class TitleSet(ThreadEvent): """Somebody changed a group's title.""" - thread = attr.ib(type=_group.Group) # Set the correct type + thread = attr.ib(type="_threads.Group") # Set the correct type #: The new title title = attr.ib(type=str) #: When the title was set @@ -77,7 +77,7 @@ class UnfetchedThreadEvent(Event): # TODO: Present this in a way that users can fetch the changed group photo easily #: The thread the message was sent to - thread = attr.ib(type=_thread.ThreadABC) + thread = attr.ib(type="_threads.ThreadABC") #: The message message = attr.ib(type=Optional[_message.Message]) @@ -103,7 +103,7 @@ class MessagesDelivered(ThreadEvent): def _parse(cls, session, data): thread = cls._get_thread(session, data) if "actorFbId" in data: - author = _user.User(session=session, id=data["actorFbId"]) + author = _threads.User(session=session, id=data["actorFbId"]) else: author = thread messages = [_message.Message(thread=thread, id=x) for x in data["messageIds"]] @@ -116,22 +116,22 @@ class ThreadsRead(Event): """Somebody marked threads as read/seen.""" #: The person who marked the threads as read - author = attr.ib(type=_thread.ThreadABC) + author = attr.ib(type="_threads.ThreadABC") #: The threads that were marked as read - threads = attr.ib(type=Sequence[_thread.ThreadABC]) + threads = attr.ib(type=Sequence["_threads.ThreadABC"]) #: When the threads were read at = attr.ib(type=datetime.datetime) @classmethod def _parse_read_receipt(cls, session, data): - author = _user.User(session=session, id=data["actorFbId"]) + author = _threads.User(session=session, id=data["actorFbId"]) thread = ThreadEvent._get_thread(session, data) at = _util.millis_to_datetime(int(data["actionTimestampMs"])) return cls(author=author, threads=[thread], at=at) @classmethod def _parse(cls, session, data): - author = _user.User(session=session, id=session.user_id) + author = _threads.User(session=session, id=session.user_id) threads = [ ThreadEvent._get_thread(session, {"threadKey": x}) for x in data["threadKeys"] @@ -169,14 +169,14 @@ class ThreadFolder(Event): # TODO: Finish this #: The created thread - thread = attr.ib(type=_thread.ThreadABC) + thread = attr.ib(type="_threads.ThreadABC") #: The folder/location - folder = attr.ib(type=_thread.ThreadLocation) + folder = attr.ib(type=_threads.ThreadLocation) @classmethod def _parse(cls, session, data): thread = ThreadEvent._get_thread(session, data) - folder = _thread.ThreadLocation._parse(data["folder"]) + folder = _threads.ThreadLocation._parse(data["folder"]) return cls(thread=thread, folder=folder) @@ -188,7 +188,7 @@ def parse_delta(session, data): return PersonRemoved._parse(session, data) elif class_ == "MarkFolderSeen": # TODO: Finish this - folders = [_thread.ThreadLocation._parse(folder) for folder in data["folders"]] + folders = [_threads.ThreadLocation._parse(folder) for folder in data["folders"]] at = _util.millis_to_datetime(int(data["timestamp"])) return None elif class_ == "ThreadName": diff --git a/fbchat/_events/_delta_type.py b/fbchat/_events/_delta_type.py index e59a2ed..2376098 100644 --- a/fbchat/_events/_delta_type.py +++ b/fbchat/_events/_delta_type.py @@ -1,7 +1,7 @@ import attr import datetime from ._common import attrs_event, Event, UnknownEvent, ThreadEvent -from .. import _util, _user, _thread, _poll, _plan +from .. import _util, _threads, _poll, _plan from typing import Sequence, Optional @@ -18,7 +18,7 @@ class ColorSet(ThreadEvent): @classmethod def _parse(cls, session, data): author, thread, at = cls._parse_metadata(session, data) - color = _thread.ThreadABC._parse_color(data["untypedData"]["theme_color"]) + color = _threads.ThreadABC._parse_color(data["untypedData"]["theme_color"]) return cls(author=author, thread=thread, color=color, at=at) @@ -52,7 +52,9 @@ class NicknameSet(ThreadEvent): @classmethod def _parse(cls, session, data): author, thread, at = cls._parse_metadata(session, data) - subject = _user.User(session=session, id=data["untypedData"]["participant_id"]) + subject = _threads.User( + session=session, id=data["untypedData"]["participant_id"] + ) nickname = data["untypedData"]["nickname"] or None # None if "" return cls( author=author, thread=thread, subject=subject, nickname=nickname, at=at @@ -64,14 +66,14 @@ class AdminsAdded(ThreadEvent): """Somebody added admins to a group.""" #: The people that were set as admins - added = attr.ib(type=Sequence[_user.User]) + added = attr.ib(type=Sequence["_threads.User"]) #: When the admins were added at = attr.ib(type=datetime.datetime) @classmethod def _parse(cls, session, data): author, thread, at = cls._parse_metadata(session, data) - subject = _user.User(session=session, id=data["untypedData"]["TARGET_ID"]) + subject = _threads.User(session=session, id=data["untypedData"]["TARGET_ID"]) return cls(author=author, thread=thread, added=[subject], at=at) @@ -80,14 +82,14 @@ class AdminsRemoved(ThreadEvent): """Somebody removed admins from a group.""" #: The people that were removed as admins - removed = attr.ib(type=Sequence[_user.User]) + removed = attr.ib(type=Sequence["_threads.User"]) #: When the admins were removed at = attr.ib(type=datetime.datetime) @classmethod def _parse(cls, session, data): author, thread, at = cls._parse_metadata(session, data) - subject = _user.User(session=session, id=data["untypedData"]["TARGET_ID"]) + subject = _threads.User(session=session, id=data["untypedData"]["TARGET_ID"]) return cls(author=author, thread=thread, removed=[subject], at=at) diff --git a/fbchat/_message.py b/fbchat/_message.py index 3e9edef..6d70d3e 100644 --- a/fbchat/_message.py +++ b/fbchat/_message.py @@ -12,7 +12,7 @@ from . import ( _file, _quick_reply, _sticker, - _thread, + _threads, ) from typing import Optional, Mapping, Sequence @@ -94,7 +94,7 @@ class Message: """ #: The thread that this message belongs to. - thread = attr.ib(type="_thread.ThreadABC") + thread = attr.ib(type="_threads.ThreadABC") #: The message ID. id = attr.ib(converter=str, type=str) diff --git a/fbchat/_threads/__init__.py b/fbchat/_threads/__init__.py new file mode 100644 index 0000000..f819ffb --- /dev/null +++ b/fbchat/_threads/__init__.py @@ -0,0 +1,4 @@ +from ._abc import * +from ._group import * +from ._user import * +from ._page import * diff --git a/fbchat/_thread.py b/fbchat/_threads/_abc.py similarity index 99% rename from fbchat/_thread.py rename to fbchat/_threads/_abc.py index b6aab4e..f818b90 100644 --- a/fbchat/_thread.py +++ b/fbchat/_threads/_abc.py @@ -3,8 +3,17 @@ import attr import collections import datetime import enum -from ._common import log, attrs_default, Image -from . import _util, _exception, _session, _graphql, _attachment, _file, _plan, _message +from .._common import log, attrs_default, Image +from .. import ( + _util, + _exception, + _session, + _graphql, + _attachment, + _file, + _plan, + _message, +) from typing import MutableMapping, Mapping, Any, Iterable, Tuple, Optional diff --git a/fbchat/_group.py b/fbchat/_threads/_group.py similarity index 97% rename from fbchat/_group.py rename to fbchat/_threads/_group.py index 582ef6e..d99592a 100644 --- a/fbchat/_group.py +++ b/fbchat/_threads/_group.py @@ -1,12 +1,14 @@ import attr import datetime -from ._common import attrs_default, Image -from . import _util, _session, _graphql, _plan, _thread, _user +from ._abc import ThreadABC +from . import _user +from .._common import attrs_default, Image +from .. import _util, _session, _graphql, _plan from typing import Sequence, Iterable, Set, Mapping @attrs_default -class Group(_thread.ThreadABC): +class Group(ThreadABC): """Represents a Facebook group. Implements `ThreadABC`. Example: @@ -245,7 +247,7 @@ class GroupData(Group): @attrs_default -class NewGroup(_thread.ThreadABC): +class NewGroup(ThreadABC): """Helper class to create new groups. TODO: Complete this! @@ -256,7 +258,7 @@ class NewGroup(_thread.ThreadABC): #: The session to use when making requests. session = attr.ib(type=_session.Session) #: The users that should be added to the group. - _users = attr.ib(type=Sequence[_user.User]) + _users = attr.ib(type=Sequence["_user.User"]) @property def id(self): diff --git a/fbchat/_page.py b/fbchat/_threads/_page.py similarity index 94% rename from fbchat/_page.py rename to fbchat/_threads/_page.py index 41845b0..67db734 100644 --- a/fbchat/_page.py +++ b/fbchat/_threads/_page.py @@ -1,11 +1,12 @@ import attr import datetime -from ._common import attrs_default, Image -from . import _session, _plan, _thread +from ._abc import ThreadABC +from .._common import attrs_default, Image +from .. import _session, _plan @attrs_default -class Page(_thread.ThreadABC): +class Page(ThreadABC): """Represents a Facebook page. Implements `ThreadABC`. Example: diff --git a/fbchat/_user.py b/fbchat/_threads/_user.py similarity index 98% rename from fbchat/_user.py rename to fbchat/_threads/_user.py index 4090052..d910851 100644 --- a/fbchat/_user.py +++ b/fbchat/_threads/_user.py @@ -1,7 +1,8 @@ import attr import datetime -from ._common import log, attrs_default, Image -from . import _util, _session, _plan, _thread +from ._abc import ThreadABC +from .._common import log, attrs_default, Image +from .. import _util, _session, _plan GENDERS = { @@ -35,7 +36,7 @@ GENDERS = { @attrs_default -class User(_thread.ThreadABC): +class User(ThreadABC): """Represents a Facebook user. Implements `ThreadABC`. Example: diff --git a/tests/test_group.py b/tests/threads/test_group.py similarity index 100% rename from tests/test_group.py rename to tests/threads/test_group.py diff --git a/tests/test_page.py b/tests/threads/test_page.py similarity index 94% rename from tests/test_page.py rename to tests/threads/test_page.py index 4cba5b8..3cbc057 100644 --- a/tests/test_page.py +++ b/tests/threads/test_page.py @@ -1,5 +1,5 @@ import fbchat -from fbchat._page import PageData +from fbchat import PageData def test_page_from_graphql(session): diff --git a/tests/test_thread.py b/tests/threads/test_thread.py similarity index 100% rename from tests/test_thread.py rename to tests/threads/test_thread.py diff --git a/tests/test_user.py b/tests/threads/test_user.py similarity index 99% rename from tests/test_user.py rename to tests/threads/test_user.py index c1222fe..2ae2a3a 100644 --- a/tests/test_user.py +++ b/tests/threads/test_user.py @@ -1,7 +1,7 @@ import pytest import datetime import fbchat -from fbchat._user import UserData, ActiveStatus +from fbchat import UserData, ActiveStatus def test_user_from_graphql(session):