Move ClientPayload parsing to separate file and add tests
This commit is contained in:
@@ -43,6 +43,13 @@ from ._poll import Poll, PollOption
|
|||||||
from ._plan import GuestStatus, Plan, PlanData
|
from ._plan import GuestStatus, Plan, PlanData
|
||||||
|
|
||||||
from ._event_common import Event, UnknownEvent, ThreadEvent
|
from ._event_common import Event, UnknownEvent, ThreadEvent
|
||||||
|
from ._client_payload import (
|
||||||
|
ReactionEvent,
|
||||||
|
UserStatusEvent,
|
||||||
|
LiveLocationEvent,
|
||||||
|
UnsendEvent,
|
||||||
|
MessageReplyEvent,
|
||||||
|
)
|
||||||
|
|
||||||
from ._client import Client
|
from ._client import Client
|
||||||
|
|
||||||
|
@@ -14,6 +14,7 @@ from . import (
|
|||||||
_thread,
|
_thread,
|
||||||
_message,
|
_message,
|
||||||
_event_common,
|
_event_common,
|
||||||
|
_client_payload,
|
||||||
)
|
)
|
||||||
|
|
||||||
from ._thread import ThreadLocation
|
from ._thread import ThreadLocation
|
||||||
@@ -964,92 +965,8 @@ class Client:
|
|||||||
|
|
||||||
# Client payload (that weird numbers)
|
# Client payload (that weird numbers)
|
||||||
elif delta_class == "ClientPayload":
|
elif delta_class == "ClientPayload":
|
||||||
payload = _util.parse_json("".join(chr(z) for z in delta["payload"]))
|
for event in _client_payload.parse_client_payloads(self.session, delta):
|
||||||
# Hack
|
self.on_event(event)
|
||||||
at = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc)
|
|
||||||
for d in payload.get("deltas", []):
|
|
||||||
|
|
||||||
# Message reaction
|
|
||||||
if d.get("deltaMessageReaction"):
|
|
||||||
i = d["deltaMessageReaction"]
|
|
||||||
mid = i["messageId"]
|
|
||||||
author_id = str(i["userId"])
|
|
||||||
add_reaction = not bool(i["action"])
|
|
||||||
if add_reaction:
|
|
||||||
self.on_reaction_added(
|
|
||||||
mid=mid,
|
|
||||||
reaction=i.get("reaction"),
|
|
||||||
author_id=author_id,
|
|
||||||
thread=get_thread(i),
|
|
||||||
at=at,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.on_reaction_removed(
|
|
||||||
mid=mid, author_id=author_id, thread=get_thread(i), at=at,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Viewer status change
|
|
||||||
elif d.get("deltaChangeViewerStatus"):
|
|
||||||
i = d["deltaChangeViewerStatus"]
|
|
||||||
author_id = str(i["actorFbid"])
|
|
||||||
reason = i["reason"]
|
|
||||||
can_reply = i["canViewerReply"]
|
|
||||||
if reason == 2:
|
|
||||||
if can_reply:
|
|
||||||
self.on_unblock(
|
|
||||||
author_id=author_id, thread=get_thread(i), at=at
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.on_block(
|
|
||||||
author_id=author_id, thread=get_thread(i), at=at
|
|
||||||
)
|
|
||||||
|
|
||||||
# Live location info
|
|
||||||
elif d.get("liveLocationData"):
|
|
||||||
i = d["liveLocationData"]
|
|
||||||
for l in i["messageLiveLocations"]:
|
|
||||||
mid = l["messageId"]
|
|
||||||
author_id = str(l["senderId"])
|
|
||||||
location = LiveLocationAttachment._from_pull(l)
|
|
||||||
self.on_live_location(
|
|
||||||
mid=mid,
|
|
||||||
location=location,
|
|
||||||
author_id=author_id,
|
|
||||||
thread=get_thread(i),
|
|
||||||
at=at,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Message deletion
|
|
||||||
elif d.get("deltaRecallMessageData"):
|
|
||||||
i = d["deltaRecallMessageData"]
|
|
||||||
mid = i["messageID"]
|
|
||||||
at = _util.millis_to_datetime(i["deletionTimestamp"])
|
|
||||||
author_id = str(i["senderID"])
|
|
||||||
self.on_message_unsent(
|
|
||||||
mid=mid, author_id=author_id, thread=get_thread(i), at=at
|
|
||||||
)
|
|
||||||
|
|
||||||
elif d.get("deltaMessageReply"):
|
|
||||||
i = d["deltaMessageReply"]
|
|
||||||
metadata = i["message"]["messageMetadata"]
|
|
||||||
thread = get_thread(metadata)
|
|
||||||
replied_to = _message.MessageData._from_reply(
|
|
||||||
thread, i["repliedToMessage"]
|
|
||||||
)
|
|
||||||
message = _message.MessageData._from_reply(
|
|
||||||
thread, i["message"], replied_to
|
|
||||||
)
|
|
||||||
self.on_message(
|
|
||||||
mid=message.id,
|
|
||||||
author_id=message.author,
|
|
||||||
message_object=message,
|
|
||||||
thread=thread,
|
|
||||||
at=message.created_at,
|
|
||||||
metadata=metadata,
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
|
||||||
self.on_unknown_messsage_type(msg=d)
|
|
||||||
|
|
||||||
# New message
|
# New message
|
||||||
elif delta.get("class") == "NewMessage":
|
elif delta.get("class") == "NewMessage":
|
||||||
@@ -1413,21 +1330,6 @@ class Client:
|
|||||||
"""
|
"""
|
||||||
log.info("Marked messages as seen in threads {} at {}".format(threads, seen_at))
|
log.info("Marked messages as seen in threads {} at {}".format(threads, seen_at))
|
||||||
|
|
||||||
def on_message_unsent(self, mid=None, author_id=None, thread=None, at=None):
|
|
||||||
"""Called when the client is listening, and someone unsends (deletes for everyone) a message.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
mid: ID of the unsent message
|
|
||||||
author_id: The ID of the person who unsent the message
|
|
||||||
thread: Thread that the action was sent to. See :ref:`intro_threads`
|
|
||||||
at (datetime.datetime): When the action was executed
|
|
||||||
"""
|
|
||||||
log.info(
|
|
||||||
"{} unsent the message {} in {} at {}".format(
|
|
||||||
author_id, repr(mid), thread, at
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def on_people_added(
|
def on_people_added(
|
||||||
self, mid=None, added_ids=None, author_id=None, group=None, at=None
|
self, mid=None, added_ids=None, author_id=None, group=None, at=None
|
||||||
):
|
):
|
||||||
@@ -1523,76 +1425,6 @@ class Client:
|
|||||||
"""
|
"""
|
||||||
log.info('{} played "{}" in {}'.format(author_id, game_name, thread))
|
log.info('{} played "{}" in {}'.format(author_id, game_name, thread))
|
||||||
|
|
||||||
def on_reaction_added(
|
|
||||||
self, mid=None, reaction=None, author_id=None, thread=None, at=None
|
|
||||||
):
|
|
||||||
"""Called when the client is listening, and somebody reacts to a message.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
mid: Message ID, that user reacted to
|
|
||||||
reaction: The added reaction. Not limited to the ones in `Message.react`
|
|
||||||
add_reaction: Whether user added or removed reaction
|
|
||||||
author_id: The ID of the person who reacted to the message
|
|
||||||
thread: Thread that the action was sent to. See :ref:`intro_threads`
|
|
||||||
at (datetime.datetime): When the action was executed
|
|
||||||
"""
|
|
||||||
log.info(
|
|
||||||
"{} reacted to message {} with {} in {}".format(
|
|
||||||
author_id, mid, reaction, thread
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def on_reaction_removed(self, mid=None, author_id=None, thread=None, at=None):
|
|
||||||
"""Called when the client is listening, and somebody removes reaction from a message.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
mid: Message ID, that user reacted to
|
|
||||||
author_id: The ID of the person who removed reaction
|
|
||||||
thread: Thread that the action was sent to. See :ref:`intro_threads`
|
|
||||||
at (datetime.datetime): When the action was executed
|
|
||||||
"""
|
|
||||||
log.info(
|
|
||||||
"{} removed reaction from {} message in {}".format(author_id, mid, thread)
|
|
||||||
)
|
|
||||||
|
|
||||||
def on_block(self, author_id=None, thread=None, at=None):
|
|
||||||
"""Called when the client is listening, and somebody blocks client.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
author_id: The ID of the person who blocked
|
|
||||||
thread: Thread that the action was sent to. See :ref:`intro_threads`
|
|
||||||
at (datetime.datetime): When the action was executed
|
|
||||||
"""
|
|
||||||
log.info("{} blocked {}".format(author_id, thread))
|
|
||||||
|
|
||||||
def on_unblock(self, author_id=None, thread=None, at=None):
|
|
||||||
"""Called when the client is listening, and somebody blocks client.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
author_id: The ID of the person who unblocked
|
|
||||||
thread: Thread that the action was sent to. See :ref:`intro_threads`
|
|
||||||
at (datetime.datetime): When the action was executed
|
|
||||||
"""
|
|
||||||
log.info("{} unblocked {}".format(author_id, thread))
|
|
||||||
|
|
||||||
def on_live_location(
|
|
||||||
self, mid=None, location=None, author_id=None, thread=None, at=None
|
|
||||||
):
|
|
||||||
"""Called when the client is listening and somebody sends live location info.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
mid: The action ID
|
|
||||||
location (LiveLocationAttachment): Sent location info
|
|
||||||
author_id: The ID of the person who sent location info
|
|
||||||
thread: Thread that the action was sent to. See :ref:`intro_threads`
|
|
||||||
at (datetime.datetime): When the action was executed
|
|
||||||
"""
|
|
||||||
log.info(
|
|
||||||
"{} sent live location info in {} with latitude {} and longitude {}".format(
|
|
||||||
author_id, thread, location.latitude, location.longitude
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def on_call_started(
|
def on_call_started(
|
||||||
self,
|
self,
|
||||||
mid=None,
|
mid=None,
|
||||||
|
136
fbchat/_client_payload.py
Normal file
136
fbchat/_client_payload.py
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
import attr
|
||||||
|
import datetime
|
||||||
|
from ._event_common import attrs_event, UnknownEvent, ThreadEvent
|
||||||
|
from . import _exception, _util, _user, _message
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
@attrs_event
|
||||||
|
class ReactionEvent(ThreadEvent):
|
||||||
|
"""Somebody reacted to a message."""
|
||||||
|
|
||||||
|
#: Message that the user reacted to
|
||||||
|
message = attr.ib(type=_message.Message)
|
||||||
|
|
||||||
|
reaction = attr.ib(type=Optional[str])
|
||||||
|
"""The reaction.
|
||||||
|
|
||||||
|
Not limited to the ones in `Message.react`.
|
||||||
|
|
||||||
|
If ``None``, the reaction was removed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _parse(cls, session, data):
|
||||||
|
thread = cls._get_thread(session, data)
|
||||||
|
return cls(
|
||||||
|
author=_user.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,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@attrs_event
|
||||||
|
class UserStatusEvent(ThreadEvent):
|
||||||
|
#: Whether the user was blocked or unblocked
|
||||||
|
blocked = attr.ib(type=bool)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _parse(cls, session, data):
|
||||||
|
return cls(
|
||||||
|
author=_user.User(session=session, id=str(data["actorFbid"])),
|
||||||
|
thread=cls._get_thread(session, data),
|
||||||
|
blocked=not data["canViewerReply"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@attrs_event
|
||||||
|
class LiveLocationEvent(ThreadEvent):
|
||||||
|
"""Somebody sent live location info."""
|
||||||
|
|
||||||
|
# TODO: This!
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _parse(cls, session, data):
|
||||||
|
from . import _location
|
||||||
|
|
||||||
|
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"]))
|
||||||
|
location = _location.LiveLocationAttachment._from_pull(location_data)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@attrs_event
|
||||||
|
class UnsendEvent(ThreadEvent):
|
||||||
|
"""Somebody unsent a message (which deletes it for everyone)."""
|
||||||
|
|
||||||
|
#: The unsent message
|
||||||
|
message = attr.ib(type=_message.Message)
|
||||||
|
#: When the message was unsent
|
||||||
|
at = attr.ib(type=datetime.datetime)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _parse(cls, session, data):
|
||||||
|
thread = cls._get_thread(session, data)
|
||||||
|
return cls(
|
||||||
|
author=_user.User(session=session, id=str(data["senderID"])),
|
||||||
|
thread=thread,
|
||||||
|
message=_message.Message(thread=thread, id=data["messageID"]),
|
||||||
|
at=_util.millis_to_datetime(data["deletionTimestamp"]),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@attrs_event
|
||||||
|
class MessageReplyEvent(ThreadEvent):
|
||||||
|
"""Somebody replied to a message."""
|
||||||
|
|
||||||
|
#: The sent message
|
||||||
|
message = attr.ib(type=_message.MessageData)
|
||||||
|
#: The message that was replied to
|
||||||
|
replied_to = attr.ib(type=_message.MessageData)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _parse(cls, session, data):
|
||||||
|
metadata = data["message"]["messageMetadata"]
|
||||||
|
thread = cls._get_thread(session, metadata)
|
||||||
|
return cls(
|
||||||
|
author=_user.User(session=session, id=str(metadata["actorFbId"])),
|
||||||
|
thread=thread,
|
||||||
|
message=_message.MessageData._from_reply(thread, data["message"]),
|
||||||
|
replied_to=_message.MessageData._from_reply(
|
||||||
|
thread, data["repliedToMessage"]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_client_delta(session, data):
|
||||||
|
if "deltaMessageReaction" in data:
|
||||||
|
return ReactionEvent._parse(session, data["deltaMessageReaction"])
|
||||||
|
elif "deltaChangeViewerStatus" in data:
|
||||||
|
# TODO: Parse all `reason`
|
||||||
|
if data["deltaChangeViewerStatus"]["reason"] == 2:
|
||||||
|
return UserStatusEvent._parse(session, data["deltaChangeViewerStatus"])
|
||||||
|
elif "liveLocationData" in data:
|
||||||
|
return LiveLocationEvent._parse(session, data["liveLocationData"])
|
||||||
|
elif "deltaRecallMessageData" in data:
|
||||||
|
return UnsendEvent._parse(session, data["deltaRecallMessageData"])
|
||||||
|
elif "deltaMessageReply" in data:
|
||||||
|
return MessageReplyEvent._parse(session, data["deltaMessageReply"])
|
||||||
|
return UnknownEvent(data=data)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_client_payloads(session, data):
|
||||||
|
payload = _util.parse_json("".join(chr(z) for z in data["payload"]))
|
||||||
|
|
||||||
|
try:
|
||||||
|
for delta in payload["deltas"]:
|
||||||
|
yield parse_client_delta(session, delta)
|
||||||
|
except _exception.ParseError:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
raise _exception.ParseError("Error parsing ClientPayload", data=payload) from e
|
@@ -319,7 +319,7 @@ class MessageData(Message):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _from_reply(cls, thread, data, replied_to=None):
|
def _from_reply(cls, thread, data):
|
||||||
tags = data["messageMetadata"].get("tags")
|
tags = data["messageMetadata"].get("tags")
|
||||||
metadata = data.get("messageMetadata", {})
|
metadata = data.get("messageMetadata", {})
|
||||||
|
|
||||||
@@ -360,8 +360,9 @@ class MessageData(Message):
|
|||||||
attachments=attachments,
|
attachments=attachments,
|
||||||
quick_replies=cls._parse_quick_replies(data.get("platform_xmd_encoded")),
|
quick_replies=cls._parse_quick_replies(data.get("platform_xmd_encoded")),
|
||||||
unsent=unsent,
|
unsent=unsent,
|
||||||
reply_to_id=replied_to.id if replied_to else None,
|
reply_to_id=data["messageReply"]["replyToMessageId"]["id"]
|
||||||
replied_to=replied_to,
|
if "messageReply" in data
|
||||||
|
else None,
|
||||||
forwarded=cls._get_forwarded_from_tags(tags),
|
forwarded=cls._get_forwarded_from_tags(tags),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
172
tests/test_client_payload.py
Normal file
172
tests/test_client_payload.py
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
import datetime
|
||||||
|
import pytest
|
||||||
|
from fbchat import (
|
||||||
|
ParseError,
|
||||||
|
User,
|
||||||
|
Group,
|
||||||
|
UnknownEvent,
|
||||||
|
ReactionEvent,
|
||||||
|
UserStatusEvent,
|
||||||
|
LiveLocationEvent,
|
||||||
|
UnsendEvent,
|
||||||
|
MessageReplyEvent,
|
||||||
|
)
|
||||||
|
from fbchat._message import Message, MessageData
|
||||||
|
from fbchat._client_payload import parse_client_delta, parse_client_payloads
|
||||||
|
|
||||||
|
|
||||||
|
def test_reaction_event_added(session):
|
||||||
|
data = {
|
||||||
|
"threadKey": {"otherUserFbId": 1234},
|
||||||
|
"messageId": "mid.$XYZ",
|
||||||
|
"action": 0,
|
||||||
|
"userId": 4321,
|
||||||
|
"reaction": "😍",
|
||||||
|
"senderId": 4321,
|
||||||
|
"offlineThreadingId": "6623596674408921967",
|
||||||
|
}
|
||||||
|
thread = User(session=session, id="1234")
|
||||||
|
assert ReactionEvent(
|
||||||
|
author=User(session=session, id="4321"),
|
||||||
|
thread=thread,
|
||||||
|
message=Message(thread=thread, id="mid.$XYZ"),
|
||||||
|
reaction="😍",
|
||||||
|
) == parse_client_delta(session, {"deltaMessageReaction": data})
|
||||||
|
|
||||||
|
|
||||||
|
def test_reaction_event_removed(session):
|
||||||
|
data = {
|
||||||
|
"threadKey": {"threadFbId": 1234},
|
||||||
|
"messageId": "mid.$XYZ",
|
||||||
|
"action": 1,
|
||||||
|
"userId": 4321,
|
||||||
|
"senderId": 4321,
|
||||||
|
"offlineThreadingId": "6623586106713014836",
|
||||||
|
}
|
||||||
|
thread = Group(session=session, id="1234")
|
||||||
|
assert ReactionEvent(
|
||||||
|
author=User(session=session, id="4321"),
|
||||||
|
thread=thread,
|
||||||
|
message=Message(thread=thread, id="mid.$XYZ"),
|
||||||
|
reaction=None,
|
||||||
|
) == parse_client_delta(session, {"deltaMessageReaction": data})
|
||||||
|
|
||||||
|
|
||||||
|
def test_user_status_blocked(session):
|
||||||
|
data = {
|
||||||
|
"threadKey": {"otherUserFbId": 1234},
|
||||||
|
"canViewerReply": False,
|
||||||
|
"reason": 2,
|
||||||
|
"actorFbid": 4321,
|
||||||
|
}
|
||||||
|
assert UserStatusEvent(
|
||||||
|
author=User(session=session, id="4321"),
|
||||||
|
thread=User(session=session, id="1234"),
|
||||||
|
blocked=True,
|
||||||
|
) == parse_client_delta(session, {"deltaChangeViewerStatus": data})
|
||||||
|
|
||||||
|
|
||||||
|
def test_user_status_unblocked(session):
|
||||||
|
data = {
|
||||||
|
"threadKey": {"otherUserFbId": 1234},
|
||||||
|
"canViewerReply": True,
|
||||||
|
"reason": 2,
|
||||||
|
"actorFbid": 1234,
|
||||||
|
}
|
||||||
|
assert UserStatusEvent(
|
||||||
|
author=User(session=session, id="1234"),
|
||||||
|
thread=User(session=session, id="1234"),
|
||||||
|
blocked=False,
|
||||||
|
) == parse_client_delta(session, {"deltaChangeViewerStatus": data})
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip(reason="need to gather test data")
|
||||||
|
def test_live_location(session):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test_message_reply(session):
|
||||||
|
message = {
|
||||||
|
"messageMetadata": {
|
||||||
|
"threadKey": {"otherUserFbId": 1234},
|
||||||
|
"messageId": "mid.$XYZ",
|
||||||
|
"offlineThreadingId": "112233445566",
|
||||||
|
"actorFbId": 1234,
|
||||||
|
"timestamp": 1500000000000,
|
||||||
|
"tags": ["source:messenger:web", "cg-enabled", "sent", "inbox"],
|
||||||
|
"threadReadStateEffect": 3,
|
||||||
|
"skipBumpThread": False,
|
||||||
|
"skipSnippetUpdate": False,
|
||||||
|
"unsendType": "can_unsend",
|
||||||
|
"folderId": {"systemFolderId": 0},
|
||||||
|
},
|
||||||
|
"body": "xyz",
|
||||||
|
"attachments": [],
|
||||||
|
"irisSeqId": 1111111,
|
||||||
|
"messageReply": {"replyToMessageId": {"id": "mid.$ABC"}, "status": 0,},
|
||||||
|
"requestContext": {"apiArgs": "..."},
|
||||||
|
"irisTags": ["DeltaNewMessage"],
|
||||||
|
}
|
||||||
|
reply = {
|
||||||
|
"messageMetadata": {
|
||||||
|
"threadKey": {"otherUserFbId": 1234},
|
||||||
|
"messageId": "mid.$ABC",
|
||||||
|
"offlineThreadingId": "665544332211",
|
||||||
|
"actorFbId": 4321,
|
||||||
|
"timestamp": 1600000000000,
|
||||||
|
"tags": ["inbox", "sent", "source:messenger:web"],
|
||||||
|
},
|
||||||
|
"body": "abc",
|
||||||
|
"attachments": [],
|
||||||
|
"requestContext": {"apiArgs": "..."},
|
||||||
|
"irisTags": [],
|
||||||
|
}
|
||||||
|
data = {
|
||||||
|
"message": message,
|
||||||
|
"repliedToMessage": reply,
|
||||||
|
"status": 0,
|
||||||
|
}
|
||||||
|
thread = User(session=session, id="1234")
|
||||||
|
assert MessageReplyEvent(
|
||||||
|
author=User(session=session, id="1234"),
|
||||||
|
thread=thread,
|
||||||
|
message=MessageData(
|
||||||
|
thread=thread,
|
||||||
|
id="mid.$XYZ",
|
||||||
|
author="1234",
|
||||||
|
created_at=datetime.datetime(
|
||||||
|
2017, 7, 14, 2, 40, tzinfo=datetime.timezone.utc
|
||||||
|
),
|
||||||
|
text="xyz",
|
||||||
|
reply_to_id="mid.$ABC",
|
||||||
|
),
|
||||||
|
replied_to=MessageData(
|
||||||
|
thread=thread,
|
||||||
|
id="mid.$ABC",
|
||||||
|
author="4321",
|
||||||
|
created_at=datetime.datetime(
|
||||||
|
2020, 9, 13, 12, 26, 40, tzinfo=datetime.timezone.utc
|
||||||
|
),
|
||||||
|
text="abc",
|
||||||
|
),
|
||||||
|
) == parse_client_delta(session, {"deltaMessageReply": data})
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_client_delta_unknown(session):
|
||||||
|
assert UnknownEvent(data={"abc": 10}) == parse_client_delta(session, {"abc": 10})
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_client_payloads_empty(session):
|
||||||
|
# This is never something that happens, it's just so that we can test the parsing
|
||||||
|
# payload = '{"deltas":[]}'
|
||||||
|
payload = [123, 34, 100, 101, 108, 116, 97, 115, 34, 58, 91, 93, 125]
|
||||||
|
data = {"payload": payload, "class": "ClientPayload"}
|
||||||
|
assert [] == list(parse_client_payloads(session, data))
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_client_payloads_invalid(session):
|
||||||
|
# payload = '{"invalid":"data"}'
|
||||||
|
payload = [123, 34, 105, 110, 118, 97, 108, 105, 100, 34, 58, 34, 97, 34, 125]
|
||||||
|
data = {"payload": payload, "class": "ClientPayload"}
|
||||||
|
with pytest.raises(ParseError, match="Error parsing ClientPayload"):
|
||||||
|
list(parse_client_payloads(session, data))
|
Reference in New Issue
Block a user