Refactor threads file structure

This commit is contained in:
Mads Marquart
2020-01-23 14:06:00 +01:00
parent c83836ceed
commit 2aea401c79
17 changed files with 138 additions and 116 deletions

View File

@@ -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

View File

@@ -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]):

View File

@@ -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")

View File

@@ -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(

View File

@@ -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

View File

@@ -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":

View File

@@ -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)

View File

@@ -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)

View File

@@ -0,0 +1,4 @@
from ._abc import *
from ._group import *
from ._user import *
from ._page import *

View File

@@ -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

View File

@@ -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):

View File

@@ -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:

View File

@@ -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:

View File

@@ -1,5 +1,5 @@
import fbchat
from fbchat._page import PageData
from fbchat import PageData
def test_page_from_graphql(session):

View File

@@ -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):