Move ThreadLocation, ActiveStatus and Image to _models/ folder

This commit is contained in:
Mads Marquart
2020-01-23 15:15:09 +01:00
parent c71c1d37c2
commit 4015bed474
20 changed files with 146 additions and 136 deletions

View File

@@ -15,7 +15,6 @@ _logging.getLogger(__name__).addHandler(_logging.NullHandler())
# The order of these is somewhat significant, e.g. User has to be imported after Thread!
from . import _common, _util
from ._common import Image
from ._exception import (
FacebookError,
HTTPError,
@@ -28,12 +27,10 @@ from ._exception import (
)
from ._session import Session
from ._threads import (
ThreadLocation,
ThreadABC,
Thread,
User,
UserData,
ActiveStatus,
Group,
GroupData,
Page,
@@ -42,6 +39,9 @@ from ._threads import (
# Models
from ._models import (
Image,
ThreadLocation,
ActiveStatus,
Attachment,
UnsentMessage,
ShareAttachment,

View File

@@ -368,7 +368,7 @@ class Client:
def fetch_threads(
self,
limit: Optional[int],
location: _threads.ThreadLocation = _threads.ThreadLocation.INBOX,
location: _models.ThreadLocation = _models.ThreadLocation.INBOX,
) -> Iterable[_threads.ThreadABC]:
"""Fetch the client's thread list.
@@ -554,7 +554,7 @@ class Client:
)
def move_threads(
self, location: _threads.ThreadLocation, threads: Iterable[_threads.ThreadABC]
self, location: _models.ThreadLocation, threads: Iterable[_threads.ThreadABC]
):
"""Move threads to specified location.
@@ -562,10 +562,10 @@ class Client:
location: INBOX, PENDING, ARCHIVED or OTHER
threads: Threads to move
"""
if location == _threads.ThreadLocation.PENDING:
location = _threads.ThreadLocation.OTHER
if location == _models.ThreadLocation.PENDING:
location = _models.ThreadLocation.OTHER
if location == _threads.ThreadLocation.ARCHIVED:
if location == _models.ThreadLocation.ARCHIVED:
data_archive = {}
data_unpin = {}
for thread in threads:

View File

@@ -9,45 +9,3 @@ kw_only = sys.version_info[:2] > (3, 5)
#: Default attrs settings for classes
attrs_default = attr.s(frozen=True, slots=True, kw_only=kw_only)
@attrs_default
class Image:
#: URL to the image
url = attr.ib(type=str)
#: Width of the image
width = attr.ib(None, type=int)
#: Height of the image
height = attr.ib(None, type=int)
@classmethod
def _from_uri(cls, data):
return cls(
url=data["uri"],
width=int(data["width"]) if data.get("width") else None,
height=int(data["height"]) if data.get("height") else None,
)
@classmethod
def _from_url(cls, data):
return cls(
url=data["url"],
width=int(data["width"]) if data.get("width") else None,
height=int(data["height"]) if data.get("height") else None,
)
@classmethod
def _from_uri_or_none(cls, data):
if data is None:
return None
if data.get("uri") is None:
return None
return cls._from_uri(data)
@classmethod
def _from_url_or_none(cls, data):
if data is None:
return None
if data.get("url") is None:
return None
return cls._from_url(data)

View File

@@ -5,7 +5,7 @@ from ._client_payload import *
from ._delta_class import *
from ._delta_type import *
from .. import _exception, _threads
from .. import _exception, _threads, _models
from typing import Mapping
@@ -55,14 +55,14 @@ class Presence(Event):
# TODO: Document this better!
#: User ids mapped to their active status
statuses = attr.ib(type=Mapping[str, _threads.ActiveStatus])
statuses = attr.ib(type=Mapping[str, "_models.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"]): _threads.ActiveStatus._from_orca_presence(d)
str(d["u"]): _models.ActiveStatus._from_orca_presence(d)
for d in data["list"]
}
return cls(statuses=statuses, full=data["list_type"] == "full")

View File

@@ -171,12 +171,12 @@ class ThreadFolder(Event):
#: The created thread
thread = attr.ib(type="_threads.ThreadABC")
#: The folder/location
folder = attr.ib(type=_threads.ThreadLocation)
folder = attr.ib(type="_models.ThreadLocation")
@classmethod
def _parse(cls, session, data):
thread = cls._get_thread(session, data)
folder = _threads.ThreadLocation._parse(data["folder"])
folder = _models.ThreadLocation._parse(data["folder"])
return cls(thread=thread, folder=folder)
@@ -190,7 +190,7 @@ def parse_delta(session, data):
return PersonRemoved._parse(session, data)
elif class_ == "MarkFolderSeen":
# TODO: Finish this
folders = [_threads.ThreadLocation._parse(folder) for folder in data["folders"]]
folders = [_models.ThreadLocation._parse(folder) for folder in data["folders"]]
at = _util.millis_to_datetime(int(data["timestamp"]))
return None
elif class_ == "ThreadName":

View File

@@ -1,3 +1,4 @@
from ._common import *
from ._attachment import *
from ._file import *
from ._location import *

View File

@@ -1,5 +1,6 @@
import attr
from .._common import attrs_default, Image
from . import Image
from .._common import attrs_default
from .. import _util
from typing import Sequence

79
fbchat/_models/_common.py Normal file
View File

@@ -0,0 +1,79 @@
import attr
import datetime
import enum
from .._common import attrs_default
from .. import _util
class ThreadLocation(enum.Enum):
"""Used to specify where a thread is located (inbox, pending, archived, other)."""
INBOX = "INBOX"
PENDING = "PENDING"
ARCHIVED = "ARCHIVED"
OTHER = "OTHER"
@classmethod
def _parse(cls, value: str):
return cls(value.lstrip("FOLDER_"))
@attrs_default
class ActiveStatus:
#: Whether the user is active now
active = attr.ib(None, type=bool)
#: Datetime when the user was last active
last_active = attr.ib(None, type=datetime.datetime)
#: Whether the user is playing Messenger game now
in_game = attr.ib(None, type=bool)
@classmethod
def _from_orca_presence(cls, data):
# TODO: Handle `c` and `vc` keys (Probably some binary data)
return cls(
active=data["p"] in [2, 3],
last_active=_util.seconds_to_datetime(data["l"]) if "l" in data else None,
in_game=None,
)
@attrs_default
class Image:
#: URL to the image
url = attr.ib(type=str)
#: Width of the image
width = attr.ib(None, type=int)
#: Height of the image
height = attr.ib(None, type=int)
@classmethod
def _from_uri(cls, data):
return cls(
url=data["uri"],
width=int(data["width"]) if data.get("width") else None,
height=int(data["height"]) if data.get("height") else None,
)
@classmethod
def _from_url(cls, data):
return cls(
url=data["url"],
width=int(data["width"]) if data.get("width") else None,
height=int(data["height"]) if data.get("height") else None,
)
@classmethod
def _from_uri_or_none(cls, data):
if data is None:
return None
if data.get("uri") is None:
return None
return cls._from_uri(data)
@classmethod
def _from_url_or_none(cls, data):
if data is None:
return None
if data.get("url") is None:
return None
return cls._from_url(data)

View File

@@ -1,7 +1,7 @@
import attr
import datetime
from ._attachment import Attachment
from .._common import attrs_default, Image
from . import Image, Attachment
from .._common import attrs_default
from .. import _util
from typing import Set

View File

@@ -1,6 +1,6 @@
import attr
from ._attachment import Attachment
from .._common import attrs_default, Image
from . import Image, Attachment
from .._common import attrs_default
from .. import _util

View File

@@ -1,5 +1,5 @@
import attr
from ._attachment import Attachment
from . import Attachment
from .._common import attrs_default
from typing import Any

View File

@@ -1,6 +1,6 @@
import attr
from ._attachment import Attachment
from .._common import attrs_default, Image
from . import Image, Attachment
from .._common import attrs_default
@attrs_default

View File

@@ -2,25 +2,11 @@ import abc
import attr
import collections
import datetime
import enum
from .._common import log, attrs_default, Image
from .._common import log, attrs_default
from .. import _util, _exception, _session, _graphql, _models
from typing import MutableMapping, Mapping, Any, Iterable, Tuple, Optional
class ThreadLocation(enum.Enum):
"""Used to specify where a thread is located (inbox, pending, archived, other)."""
INBOX = "INBOX"
PENDING = "PENDING"
ARCHIVED = "ARCHIVED"
OTHER = "OTHER"
@classmethod
def _parse(cls, value: str):
return cls(value.lstrip("FOLDER_"))
DEFAULT_COLOR = "#0084ff"
SETABLE_COLORS = (
DEFAULT_COLOR,

View File

@@ -2,7 +2,7 @@ import attr
import datetime
from ._abc import ThreadABC
from . import _user
from .._common import attrs_default, Image
from .._common import attrs_default
from .. import _util, _session, _graphql, _models
from typing import Sequence, Iterable, Set, Mapping
@@ -176,7 +176,7 @@ class GroupData(Group):
"""
#: The group's picture
photo = attr.ib(None, type=Image)
photo = attr.ib(None, type="_models.Image")
#: The name of the group
name = attr.ib(None, type=str)
#: When the group was last active / when the last message was sent
@@ -238,7 +238,7 @@ class GroupData(Group):
if data.get("group_approval_queue")
else None,
join_link=data["joinable_mode"].get("link"),
photo=Image._from_uri_or_none(data["image"]),
photo=_models.Image._from_uri_or_none(data["image"]),
name=data.get("name"),
message_count=data.get("messages_count"),
last_active=last_active,

View File

@@ -1,7 +1,7 @@
import attr
import datetime
from ._abc import ThreadABC
from .._common import attrs_default, Image
from .._common import attrs_default
from .. import _session, _models
@@ -32,7 +32,7 @@ class PageData(Page):
"""
#: The page's picture
photo = attr.ib(type=Image)
photo = attr.ib(type="_models.Image")
#: The name of the page
name = attr.ib(type=str)
#: When the thread was last active / when the last message was sent
@@ -70,7 +70,7 @@ class PageData(Page):
url=data.get("url"),
city=data.get("city").get("name"),
category=data.get("category_type"),
photo=Image._from_uri(data["profile_picture"]),
photo=_models.Image._from_uri(data["profile_picture"]),
name=data["name"],
message_count=data.get("messages_count"),
plan=plan,

View File

@@ -1,7 +1,7 @@
import attr
import datetime
from ._abc import ThreadABC
from .._common import log, attrs_default, Image
from .._common import log, attrs_default
from .. import _util, _session, _models
@@ -100,7 +100,7 @@ class UserData(User):
"""
#: The user's picture
photo = attr.ib(type=Image)
photo = attr.ib(type="_models.Image")
#: The name of the user
name = attr.ib(type=str)
#: Whether the user and the client are friends
@@ -162,7 +162,7 @@ class UserData(User):
color=c_info["color"],
emoji=c_info["emoji"],
own_nickname=c_info.get("own_nickname"),
photo=Image._from_uri(data["profile_picture"]),
photo=_models.Image._from_uri(data["profile_picture"]),
name=data["name"],
message_count=data.get("messages_count"),
plan=plan,
@@ -196,7 +196,7 @@ class UserData(User):
color=c_info["color"],
emoji=c_info["emoji"],
own_nickname=c_info.get("own_nickname"),
photo=Image._from_uri(user["big_image_src"]),
photo=_models.Image._from_uri(user["big_image_src"]),
message_count=data["messages_count"],
last_active=_util.millis_to_datetime(int(data["updated_time_precise"])),
plan=plan,
@@ -209,27 +209,8 @@ class UserData(User):
id=data["id"],
first_name=data["firstName"],
url=data["uri"],
photo=Image(url=data["thumbSrc"]),
photo=_models.Image(url=data["thumbSrc"]),
name=data["name"],
is_friend=data["is_friend"],
gender=GENDERS.get(data["gender"]),
)
@attrs_default
class ActiveStatus:
#: Whether the user is active now
active = attr.ib(None, type=bool)
#: Datetime when the user was last active
last_active = attr.ib(None, type=datetime.datetime)
#: Whether the user is playing Messenger game now
in_game = attr.ib(None, type=bool)
@classmethod
def _from_orca_presence(cls, data):
# TODO: Handle `c` and `vc` keys (Probably some binary data)
return cls(
active=data["p"] in [2, 3],
last_active=_util.seconds_to_datetime(data["l"]) if "l" in data else None,
in_game=None,
)

View File

@@ -1,7 +1,7 @@
import pytest
import datetime
import fbchat
from fbchat import UnsentMessage, ShareAttachment
from fbchat import Image, UnsentMessage, ShareAttachment
from fbchat._models._message import graphql_to_extensible_attachment
@@ -122,7 +122,7 @@ def test_share_from_graphql_link_with_image():
" Share photos and videos, send messages and get updates."
),
source=None,
image=fbchat.Image(
image=Image(
url="https://www.facebook.com/rsrc.php/v3/x.png", width=325, height=325
),
original_image_url="https://www.facebook.com/rsrc.php/v3/x.png",
@@ -184,7 +184,7 @@ def test_share_from_graphql_video():
" Subscribe to the official Rick As..."
),
source="youtube.com",
image=fbchat.Image(
image=Image(
url="https://external-arn2-1.xx.fbcdn.net/safe_image.php?d=xyz123"
"&w=960&h=540&url=https%3A%2F%2Fi.ytimg.com%2Fvi%2FdQw4w9WgXcQ"
"%2Fmaxresdefault.jpg&sx=0&sy=0&sw=1280&sh=720&_nc_hash=abc123",
@@ -307,7 +307,7 @@ def test_share_with_image_subattachment():
title="",
description="Abc",
source="Def",
image=fbchat.Image(
image=Image(
url="https://scontent-arn2-1.xx.fbcdn.net/v/t1.0-9/1.jpg",
width=720,
height=960,
@@ -435,7 +435,7 @@ def test_share_with_video_subattachment():
title="",
description="Abc",
source="Def",
image=fbchat.Image(
image=Image(
url="https://scontent-arn2-1.xx.fbcdn.net/v/t15.5256-10/p180x540/1.jpg",
width=960,
height=540,
@@ -447,7 +447,7 @@ def test_share_with_video_subattachment():
duration=datetime.timedelta(seconds=24, microseconds=469000),
preview_url="https://video-arn2-1.xx.fbcdn.net/v/t42.9040-2/vid.mp4",
previews={
fbchat.Image(
Image(
url="https://scontent-arn2-1.xx.fbcdn.net/v/t15.5256-10/p180x540/1.jpg",
width=960,
height=540,

View File

@@ -1,6 +1,12 @@
import datetime
import fbchat
from fbchat import FileAttachment, AudioAttachment, ImageAttachment, VideoAttachment
from fbchat import (
Image,
FileAttachment,
AudioAttachment,
ImageAttachment,
VideoAttachment,
)
from fbchat._models._file import graphql_to_attachment, graphql_to_subattachment
@@ -28,13 +34,13 @@ def test_imageattachment_from_list():
width=2833,
height=1367,
previews={
fbchat.Image(url="https://scontent-arn2-1.xx.fbcdn.net/v/s261x260/1.jpg"),
fbchat.Image(
Image(url="https://scontent-arn2-1.xx.fbcdn.net/v/s261x260/1.jpg"),
Image(
url="https://scontent-arn2-1.xx.fbcdn.net/v/2.jpg",
width=960,
height=463,
),
fbchat.Image(
Image(
url="https://scontent-arn2-1.xx.fbcdn.net/v/s2048x2048/3.jpg",
width=2048,
height=988,
@@ -68,15 +74,15 @@ def test_videoattachment_from_list():
width=640,
height=368,
previews={
fbchat.Image(
Image(
url="https://scontent-arn2-1.xx.fbcdn.net/v/t15.3394-10/p261x260/1.jpg"
),
fbchat.Image(
Image(
url="https://scontent-arn2-1.xx.fbcdn.net/v/t15.3394-10/2.jpg",
width=640,
height=368,
),
fbchat.Image(
Image(
url="https://scontent-arn2-1.xx.fbcdn.net/v/t15.3394-10/3.jpg",
width=640,
height=368,
@@ -169,8 +175,8 @@ def test_graphql_to_attachment_image1():
height=None,
is_animated=False,
previews={
fbchat.Image(url="https://scontent-arn2-1.xx.fbcdn.net/v/p50x50/2.png"),
fbchat.Image(
Image(url="https://scontent-arn2-1.xx.fbcdn.net/v/p50x50/2.png"),
Image(
url="https://scontent-arn2-1.xx.fbcdn.net/v/1.png",
width=128,
height=128,
@@ -204,9 +210,7 @@ def test_graphql_to_attachment_image2():
width=None,
height=None,
is_animated=True,
previews={
fbchat.Image(url="https://cdn.fbsbx.com/v/1.gif", width=128, height=128)
},
previews={Image(url="https://cdn.fbsbx.com/v/1.gif", width=128, height=128)},
) == graphql_to_attachment(data)
@@ -244,17 +248,17 @@ def test_graphql_to_attachment_video():
duration=datetime.timedelta(seconds=6),
preview_url="https://video-arn2-1.xx.fbcdn.net/v/video-4321.mp4",
previews={
fbchat.Image(
Image(
url="https://scontent-arn2-1.xx.fbcdn.net/v/s168x128/1.jpg",
width=168,
height=96,
),
fbchat.Image(
Image(
url="https://scontent-arn2-1.xx.fbcdn.net/v/p261x260/3.jpg",
width=452,
height=260,
),
fbchat.Image(
Image(
url="https://scontent-arn2-1.xx.fbcdn.net/v/2.jpg",
width=640,
height=368,
@@ -345,7 +349,7 @@ def test_graphql_to_subattachment_video():
duration=datetime.timedelta(seconds=24, microseconds=469000),
preview_url="https://video-arn2-1.xx.fbcdn.net/v/t42.9040-2/vid.mp4",
previews={
fbchat.Image(
Image(
url="https://scontent-arn2-1.xx.fbcdn.net/v/t15.5256-10/p180x540/1.jpg",
width=960,
height=540,

View File

@@ -1,7 +1,7 @@
import pytest
import datetime
import fbchat
from fbchat import LocationAttachment, LiveLocationAttachment
from fbchat import Image, LocationAttachment, LiveLocationAttachment
def test_location_attachment_from_graphql():
@@ -38,7 +38,7 @@ def test_location_attachment_from_graphql():
id=400828513928715,
latitude=55.4,
longitude=12.4322,
image=fbchat.Image(
image=Image(
url="https://external-arn2-1.xx.fbcdn.net/static_map.php?v=1020&osm_provider=2&size=545x280&zoom=15&markers=55.40000000%2C12.43220000&language=en",
width=545,
height=280,

View File

@@ -1,6 +1,6 @@
import pytest
import fbchat
from fbchat import Sticker
from fbchat import Image, Sticker
def test_from_graphql_none():
@@ -20,7 +20,7 @@ def test_from_graphql_normal():
frames_per_col=1,
frame_count=1,
frame_rate=83,
image=fbchat.Image(
image=Image(
url="https://scontent-arn2-1.xx.fbcdn.net/v/redacted.png",
width=274,
height=274,
@@ -57,7 +57,7 @@ def test_from_graphql_animated():
frames_per_col=2,
frame_count=4,
frame_rate=142,
image=fbchat.Image(
image=Image(
url="https://scontent-arn2-1.fbcdn.net/v/redacted1.png",
width=240,
height=293,