Move /t_ms delta admin text type parsing to separate file and add tests

This commit is contained in:
Mads Marquart
2020-01-20 16:56:35 +01:00
parent 4abe5659ae
commit 0a6bf221e6
4 changed files with 1306 additions and 574 deletions

View File

@@ -59,6 +59,24 @@ from ._delta_class import (
ThreadsRead,
MessageEvent,
)
from ._delta_type import (
ColorSet,
EmojiSet,
NicknameSet,
AdminsAdded,
AdminsRemoved,
ApprovalModeSet,
CallStarted,
CallEnded,
CallJoined,
PollCreated,
PollVoted,
PlanCreated,
PlanEnded,
PlanEdited,
PlanDeleted,
PlanResponded,
)
from ._client import Client

View File

@@ -16,6 +16,7 @@ from . import (
_event_common,
_client_payload,
_delta_class,
_delta_type,
)
from ._thread import ThreadLocation
@@ -609,244 +610,8 @@ class Client:
"""
def _parse_delta(self, delta):
def get_thread(data):
if "threadFbId" in data["threadKey"]:
group_id = str(data["threadKey"]["threadFbId"])
return Group(session=self.session, id=group_id)
elif "otherUserFbId" in data["threadKey"]:
user_id = str(data["threadKey"]["otherUserFbId"])
return User(session=self.session, id=user_id)
return None
delta_type = delta.get("type")
metadata = delta.get("messageMetadata")
if metadata:
mid = metadata["messageId"]
author_id = str(metadata["actorFbId"])
at = _util.millis_to_datetime(int(metadata.get("timestamp")))
# Color change
if delta_type == "change_thread_theme":
thread = get_thread(metadata)
self.on_color_change(
mid=mid,
author_id=author_id,
new_color=_thread.ThreadABC._parse_color(
delta["untypedData"]["theme_color"]
),
thread=get_thread(metadata),
at=at,
metadata=metadata,
)
# Emoji change
elif delta_type == "change_thread_icon":
new_emoji = delta["untypedData"]["thread_icon"]
self.on_emoji_change(
mid=mid,
author_id=author_id,
new_emoji=new_emoji,
thread=get_thread(metadata),
at=at,
metadata=metadata,
)
# Nickname change
elif delta_type == "change_thread_nickname":
changed_for = str(delta["untypedData"]["participant_id"])
new_nickname = delta["untypedData"]["nickname"]
self.on_nickname_change(
mid=mid,
author_id=author_id,
changed_for=changed_for,
new_nickname=new_nickname,
thread=get_thread(metadata),
at=at,
metadata=metadata,
)
# Admin added or removed in a group thread
elif delta_type == "change_thread_admins":
target_id = delta["untypedData"]["TARGET_ID"]
admin_event = delta["untypedData"]["ADMIN_EVENT"]
if admin_event == "add_admin":
self.on_admin_added(
mid=mid,
added_id=target_id,
author_id=author_id,
group=get_thread(metadata),
at=at,
)
elif admin_event == "remove_admin":
self.on_admin_removed(
mid=mid,
removed_id=target_id,
author_id=author_id,
group=get_thread(metadata),
at=at,
)
# Group approval mode change
elif delta_type == "change_thread_approval_mode":
approval_mode = bool(int(delta["untypedData"]["APPROVAL_MODE"]))
self.on_approval_mode_change(
mid=mid,
approval_mode=approval_mode,
author_id=author_id,
group=get_thread(metadata),
at=at,
)
# Game played
elif delta_type == "instant_game_update":
game_id = delta["untypedData"]["game_id"]
game_name = delta["untypedData"]["game_name"]
score = delta["untypedData"].get("score")
if score is not None:
score = int(score)
leaderboard = delta["untypedData"].get("leaderboard")
if leaderboard is not None:
leaderboard = _util.parse_json(leaderboard)["scores"]
self.on_game_played(
mid=mid,
author_id=author_id,
game_id=game_id,
game_name=game_name,
score=score,
leaderboard=leaderboard,
thread=get_thread(metadata),
at=at,
metadata=metadata,
)
# Group call started/ended
elif delta_type == "rtc_call_log":
call_status = delta["untypedData"]["event"]
call_duration = _util.seconds_to_timedelta(
int(delta["untypedData"]["call_duration"])
)
is_video_call = bool(int(delta["untypedData"]["is_video_call"]))
if call_status == "call_started":
self.on_call_started(
mid=mid,
caller_id=author_id,
is_video_call=is_video_call,
thread=get_thread(metadata),
at=at,
metadata=metadata,
)
elif call_status == "call_ended":
self.on_call_ended(
mid=mid,
caller_id=author_id,
is_video_call=is_video_call,
call_duration=call_duration,
thread=get_thread(metadata),
at=at,
metadata=metadata,
)
# User joined to group call
elif delta_type == "participant_joined_group_call":
is_video_call = bool(int(delta["untypedData"]["group_call_type"]))
self.on_user_joined_call(
mid=mid,
joined_id=author_id,
is_video_call=is_video_call,
thread=get_thread(metadata),
at=at,
metadata=metadata,
)
# Group poll event
elif delta_type == "group_poll":
event_type = delta["untypedData"]["event_type"]
poll_json = _util.parse_json(delta["untypedData"]["question_json"])
poll = _poll.Poll._from_graphql(self.session, poll_json)
if event_type == "question_creation":
# User created group poll
self.on_poll_created(
mid=mid,
poll=poll,
author_id=author_id,
thread=get_thread(metadata),
at=at,
metadata=metadata,
)
elif event_type == "update_vote":
# User voted on group poll
added = _util.parse_json(delta["untypedData"]["added_option_ids"])
removed = _util.parse_json(delta["untypedData"]["removed_option_ids"])
self.on_poll_voted(
mid=mid,
poll=poll,
added_options=added,
removed_options=removed,
author_id=author_id,
thread=get_thread(metadata),
at=at,
metadata=metadata,
)
# Plan created
elif delta_type == "lightweight_event_create":
self.on_plan_created(
mid=mid,
plan=PlanData._from_pull(self.session, delta["untypedData"]),
author_id=author_id,
thread=get_thread(metadata),
at=at,
metadata=metadata,
)
# Plan ended
elif delta_type == "lightweight_event_notify":
self.on_plan_ended(
mid=mid,
plan=PlanData._from_pull(self.session, delta["untypedData"]),
thread=get_thread(metadata),
at=at,
metadata=metadata,
)
# Plan edited
elif delta_type == "lightweight_event_update":
self.on_plan_edited(
mid=mid,
plan=PlanData._from_pull(self.session, delta["untypedData"]),
author_id=author_id,
thread=get_thread(metadata),
at=at,
metadata=metadata,
)
# Plan deleted
elif delta_type == "lightweight_event_delete":
self.on_plan_deleted(
mid=mid,
plan=PlanData._from_pull(self.session, delta["untypedData"]),
author_id=author_id,
thread=get_thread(metadata),
at=at,
metadata=metadata,
)
# Plan participation change
elif delta_type == "lightweight_event_rsvp":
take_part = delta["untypedData"]["guest_status"] == "GOING"
self.on_plan_participation(
mid=mid,
plan=PlanData._from_pull(self.session, delta["untypedData"]),
take_part=take_part,
author_id=author_id,
thread=get_thread(metadata),
at=at,
metadata=metadata,
)
# Client payload (that weird numbers)
elif delta.get("class") == "ClientPayload":
if delta.get("class") == "ClientPayload":
for event in _client_payload.parse_client_payloads(self.session, delta):
self.on_event(event)
@@ -855,6 +620,9 @@ class Client:
if event:
self.on_event(event)
elif delta.get("type"):
self.on_event(_delta_type.parse_delta(self.session, delta))
# Unknown message type
else:
self.on_unknown_messsage_type(msg=delta)
@@ -984,120 +752,6 @@ class Client:
"""Called when the client is listening, and an event happens."""
log.info("Got event: %s", event)
def on_color_change(
self,
mid=None,
author_id=None,
new_color=None,
thread=None,
at=None,
metadata=None,
):
"""Called when the client is listening, and somebody changes a thread's color.
Args:
mid: The action ID
author_id: The ID of the person who changed the color
new_color: The new color. Not limited to the ones in `ThreadABC.set_color`
thread: Thread that the action was sent to. See :ref:`intro_threads`
at (datetime.datetime): When the action was executed
metadata: Extra metadata about the action
"""
log.info("Color change from {} in {}: {}".format(author_id, thread, new_color))
def on_emoji_change(
self,
mid=None,
author_id=None,
new_emoji=None,
thread=None,
at=None,
metadata=None,
):
"""Called when the client is listening, and somebody changes a thread's emoji.
Args:
mid: The action ID
author_id: The ID of the person who changed the emoji
new_emoji: The new emoji
thread: Thread that the action was sent to. See :ref:`intro_threads`
at (datetime.datetime): When the action was executed
metadata: Extra metadata about the action
"""
log.info("Emoji change from {} in {}: {}".format(author_id, thread, new_emoji))
def on_nickname_change(
self,
mid=None,
author_id=None,
changed_for=None,
new_nickname=None,
thread=None,
at=None,
metadata=None,
):
"""Called when the client is listening, and somebody changes a nickname.
Args:
mid: The action ID
author_id: The ID of the person who changed the nickname
changed_for: The ID of the person whom got their nickname changed
new_nickname: The new nickname
thread: Thread that the action was sent to. See :ref:`intro_threads`
at (datetime.datetime): When the action was executed
metadata: Extra metadata about the action
"""
log.info(
"Nickname change from {} in {} for {}: {}".format(
author_id, thread, changed_for, new_nickname
)
)
def on_admin_added(
self, mid=None, added_id=None, author_id=None, group=None, at=None
):
"""Called when the client is listening, and somebody adds an admin to a group.
Args:
mid: The action ID
added_id: The ID of the admin who got added
author_id: The ID of the person who added the admins
group: Group that the action was sent to. See :ref:`intro_threads`
at (datetime.datetime): When the action was executed
"""
log.info("{} added admin: {} in {}".format(author_id, added_id, group))
def on_admin_removed(
self, mid=None, removed_id=None, author_id=None, group=None, at=None
):
"""Called when the client is listening, and somebody is removed as an admin in a group.
Args:
mid: The action ID
removed_id: The ID of the admin who got removed
author_id: The ID of the person who removed the admins
group: Group that the action was sent to. See :ref:`intro_threads`
at (datetime.datetime): When the action was executed
"""
log.info("{} removed admin: {} in {}".format(author_id, removed_id, group))
def on_approval_mode_change(
self, mid=None, approval_mode=None, author_id=None, group=None, at=None,
):
"""Called when the client is listening, and somebody changes approval mode in a group.
Args:
mid: The action ID
approval_mode: True if approval mode is activated
author_id: The ID of the person who changed approval mode
group: Group that the action was sent to. See :ref:`intro_threads`
at (datetime.datetime): When the action was executed
"""
if approval_mode:
log.info("{} activated approval mode in {}".format(author_id, group))
else:
log.info("{} disabled approval mode in {}".format(author_id, group))
def on_friend_request(self, from_id=None):
"""Called when the client is listening, and somebody sends a friend request.
@@ -1128,229 +782,6 @@ class Client:
"""
pass
def on_game_played(
self,
mid=None,
author_id=None,
game_id=None,
game_name=None,
score=None,
leaderboard=None,
thread=None,
at=None,
metadata=None,
):
"""Called when the client is listening, and somebody plays a game.
Args:
mid: The action ID
author_id: The ID of the person who played the game
game_id: The ID of the game
game_name: Name of the game
score: Score obtained in the game
leaderboard: Actual leader board of the game in the thread
thread: Thread that the action was sent to. See :ref:`intro_threads`
at (datetime.datetime): When the action was executed
metadata: Extra metadata about the action
"""
log.info('{} played "{}" in {}'.format(author_id, game_name, thread))
def on_call_started(
self,
mid=None,
caller_id=None,
is_video_call=None,
thread=None,
at=None,
metadata=None,
):
"""Called when the client is listening, and somebody starts a call in a group.
Todo:
Make this work with private calls.
Args:
mid: The action ID
caller_id: The ID of the person who started the call
is_video_call: True if it's video call
thread: Thread that the action was sent to. See :ref:`intro_threads`
at (datetime.datetime): When the action was executed
metadata: Extra metadata about the action
"""
log.info("{} started call in {}".format(caller_id, thread))
def on_call_ended(
self,
mid=None,
caller_id=None,
is_video_call=None,
call_duration=None,
thread=None,
at=None,
metadata=None,
):
"""Called when the client is listening, and somebody ends a call in a group.
Todo:
Make this work with private calls.
Args:
mid: The action ID
caller_id: The ID of the person who ended the call
is_video_call: True if it was video call
call_duration (datetime.timedelta): Call duration
thread: Thread that the action was sent to. See :ref:`intro_threads`
at (datetime.datetime): When the action was executed
metadata: Extra metadata about the action
"""
log.info("{} ended call in {}".format(caller_id, thread))
def on_user_joined_call(
self,
mid=None,
joined_id=None,
is_video_call=None,
thread=None,
at=None,
metadata=None,
):
"""Called when the client is listening, and somebody joins a group call.
Args:
mid: The action ID
joined_id: The ID of the person who joined the call
is_video_call: True if it's video call
thread: Thread that the action was sent to. See :ref:`intro_threads`
at (datetime.datetime): When the action was executed
metadata: Extra metadata about the action
"""
log.info("{} joined call in {}".format(joined_id, thread))
def on_poll_created(
self, mid=None, poll=None, author_id=None, thread=None, at=None, metadata=None,
):
"""Called when the client is listening, and somebody creates a group poll.
Args:
mid: The action ID
poll (Poll): Created poll
author_id: The ID of the person who created the poll
thread: Thread that the action was sent to. See :ref:`intro_threads`
at (datetime.datetime): When the action was executed
metadata: Extra metadata about the action
"""
log.info("{} created poll {} in {}".format(author_id, poll, thread))
def on_poll_voted(
self,
mid=None,
poll=None,
added_options=None,
removed_options=None,
author_id=None,
thread=None,
at=None,
metadata=None,
):
"""Called when the client is listening, and somebody votes in a group poll.
Args:
mid: The action ID
poll (Poll): Poll, that user voted in
author_id: The ID of the person who voted in the poll
thread: Thread that the action was sent to. See :ref:`intro_threads`
at (datetime.datetime): When the action was executed
metadata: Extra metadata about the action
"""
log.info("{} voted in poll {} in {}".format(author_id, poll, thread))
def on_plan_created(
self, mid=None, plan=None, author_id=None, thread=None, at=None, metadata=None,
):
"""Called when the client is listening, and somebody creates a plan.
Args:
mid: The action ID
plan (Plan): Created plan
author_id: The ID of the person who created the plan
thread: Thread that the action was sent to. See :ref:`intro_threads`
at (datetime.datetime): When the action was executed
metadata: Extra metadata about the action
"""
log.info("{} created plan {} in {}".format(author_id, plan, thread))
def on_plan_ended(self, mid=None, plan=None, thread=None, at=None, metadata=None):
"""Called when the client is listening, and a plan ends.
Args:
mid: The action ID
plan (Plan): Ended plan
thread: Thread that the action was sent to. See :ref:`intro_threads`
at (datetime.datetime): When the action was executed
metadata: Extra metadata about the action
"""
log.info("Plan {} has ended in {}".format(plan, thread))
def on_plan_edited(
self, mid=None, plan=None, author_id=None, thread=None, at=None, metadata=None,
):
"""Called when the client is listening, and somebody edits a plan.
Args:
mid: The action ID
plan (Plan): Edited plan
author_id: The ID of the person who edited the plan
thread: Thread that the action was sent to. See :ref:`intro_threads`
at (datetime.datetime): When the action was executed
metadata: Extra metadata about the action
"""
log.info("{} edited plan {} in {}".format(author_id, plan, thread))
def on_plan_deleted(
self, mid=None, plan=None, author_id=None, thread=None, at=None, metadata=None,
):
"""Called when the client is listening, and somebody deletes a plan.
Args:
mid: The action ID
plan (Plan): Deleted plan
author_id: The ID of the person who deleted the plan
thread: Thread that the action was sent to. See :ref:`intro_threads`
at (datetime.datetime): When the action was executed
metadata: Extra metadata about the action
"""
log.info("{} deleted plan {} in {}".format(author_id, plan, thread))
def on_plan_participation(
self,
mid=None,
plan=None,
take_part=None,
author_id=None,
thread=None,
at=None,
metadata=None,
):
"""Called when the client is listening, and somebody takes part in a plan or not.
Args:
mid: The action ID
plan (Plan): Plan
take_part (bool): Whether the person takes part in the plan or not
author_id: The ID of the person who will participate in the plan or not
thread: Thread that the action was sent to. See :ref:`intro_threads`
at (datetime.datetime): When the action was executed
metadata: Extra metadata about the action
"""
if take_part:
log.info(
"{} will take part in {} in {} ({})".format(author_id, plan, thread)
)
else:
log.info(
"{} won't take part in {} in {} ({})".format(author_id, plan, thread)
)
def on_chat_timestamp(self, buddylist=None):
"""Called when the client receives chat online presence update.

329
fbchat/_delta_type.py Normal file
View File

@@ -0,0 +1,329 @@
import attr
import datetime
from ._event_common import attrs_event, Event, UnknownEvent, ThreadEvent
from . import _util, _user, _thread, _poll, _plan
from typing import Sequence, Optional
@attrs_event
class ColorSet(ThreadEvent):
"""Somebody set the color in a thread."""
#: The new color. Not limited to the ones in `ThreadABC.set_color`
color = attr.ib(type=str)
#: When the color was set
at = attr.ib(type=datetime.datetime)
@classmethod
def _parse(cls, session, data):
author, thread, at = cls._parse_metadata(session, data)
color = _thread.ThreadABC._parse_color(data["untypedData"]["theme_color"])
return cls(author=author, thread=thread, color=color, at=at)
@attrs_event
class EmojiSet(ThreadEvent):
"""Somebody set the emoji in a thread."""
#: The new emoji
emoji = attr.ib(type=str)
#: When the emoji was set
at = attr.ib(type=datetime.datetime)
@classmethod
def _parse(cls, session, data):
author, thread, at = cls._parse_metadata(session, data)
emoji = data["untypedData"]["thread_icon"]
return cls(author=author, thread=thread, emoji=emoji, at=at)
@attrs_event
class NicknameSet(ThreadEvent):
"""Somebody set the nickname of a person in a thread."""
#: The person whose nickname was set
subject = attr.ib(type=str)
#: The new nickname. If ``None``, the nickname was cleared
nickname = attr.ib(type=Optional[str])
#: When the nickname was set
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"]["participant_id"])
nickname = data["untypedData"]["nickname"] or None # None if ""
return cls(
author=author, thread=thread, subject=subject, nickname=nickname, at=at
)
@attrs_event
class AdminsAdded(ThreadEvent):
"""Somebody added admins to a group."""
#: The people that were set as admins
added = attr.ib(type=Sequence[_user.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"])
return cls(author=author, thread=thread, added=[subject], at=at)
@attrs_event
class AdminsRemoved(ThreadEvent):
"""Somebody removed admins from a group."""
#: The people that were removed as admins
removed = attr.ib(type=Sequence[_user.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"])
return cls(author=author, thread=thread, removed=[subject], at=at)
@attrs_event
class ApprovalModeSet(ThreadEvent):
"""Somebody changed the approval mode in a group."""
require_admin_approval = attr.ib(type=bool)
#: When the approval mode was set
at = attr.ib(type=datetime.datetime)
@classmethod
def _parse(cls, session, data):
author, thread, at = cls._parse_metadata(session, data)
raa = data["untypedData"]["APPROVAL_MODE"] == "1"
return cls(author=author, thread=thread, require_admin_approval=raa, at=at)
@attrs_event
class CallStarted(ThreadEvent):
"""Somebody started a call."""
#: When the call was started
at = attr.ib(type=datetime.datetime)
@classmethod
def _parse(cls, session, data):
author, thread, at = cls._parse_metadata(session, data)
return cls(author=author, thread=thread, at=at)
@attrs_event
class CallEnded(ThreadEvent):
"""Somebody ended a call."""
#: How long the call took
duration = attr.ib(type=datetime.timedelta)
#: When the call ended
at = attr.ib(type=datetime.datetime)
@classmethod
def _parse(cls, session, data):
author, thread, at = cls._parse_metadata(session, data)
duration = _util.seconds_to_timedelta(int(data["untypedData"]["call_duration"]))
return cls(author=author, thread=thread, duration=duration, at=at)
@attrs_event
class CallJoined(ThreadEvent):
"""Somebody joined a call."""
#: When the call ended
at = attr.ib(type=datetime.datetime)
@classmethod
def _parse(cls, session, data):
author, thread, at = cls._parse_metadata(session, data)
return cls(author=author, thread=thread, at=at)
@attrs_event
class PollCreated(ThreadEvent):
"""Somebody created a group poll."""
#: The new poll
poll = attr.ib(type=_poll.Poll)
#: When the poll was created
at = attr.ib(type=datetime.datetime)
@classmethod
def _parse(cls, session, data):
author, thread, at = cls._parse_metadata(session, data)
poll_data = _util.parse_json(data["untypedData"]["question_json"])
poll = _poll.Poll._from_graphql(session, poll_data)
return cls(author=author, thread=thread, poll=poll, at=at)
@attrs_event
class PollVoted(ThreadEvent):
"""Somebody voted in a group poll."""
#: The updated poll
poll = attr.ib(type=_poll.Poll)
#: Ids of the voted options
added_ids = attr.ib(type=Sequence[str])
#: Ids of the un-voted options
removed_ids = attr.ib(type=Sequence[str])
#: When the poll was voted in
at = attr.ib(type=datetime.datetime)
@classmethod
def _parse(cls, session, data):
author, thread, at = cls._parse_metadata(session, data)
poll_data = _util.parse_json(data["untypedData"]["question_json"])
poll = _poll.Poll._from_graphql(session, poll_data)
added_ids = _util.parse_json(data["untypedData"]["added_option_ids"])
removed_ids = _util.parse_json(data["untypedData"]["removed_option_ids"])
return cls(
author=author,
thread=thread,
poll=poll,
added_ids=[str(x) for x in added_ids],
removed_ids=[str(x) for x in removed_ids],
at=at,
)
@attrs_event
class PlanCreated(ThreadEvent):
"""Somebody created a plan in a group."""
#: The new plan
plan = attr.ib(type=_plan.PlanData)
#: When the plan was created
at = attr.ib(type=datetime.datetime)
@classmethod
def _parse(cls, session, data):
author, thread, at = cls._parse_metadata(session, data)
plan = _plan.PlanData._from_pull(session, data["untypedData"])
return cls(author=author, thread=thread, plan=plan, at=at)
@attrs_event
class PlanEnded(ThreadEvent):
"""A plan ended."""
#: The ended plan
plan = attr.ib(type=_plan.PlanData)
#: When the plan ended
at = attr.ib(type=datetime.datetime)
@classmethod
def _parse(cls, session, data):
author, thread, at = cls._parse_metadata(session, data)
plan = _plan.PlanData._from_pull(session, data["untypedData"])
return cls(author=author, thread=thread, plan=plan, at=at)
@attrs_event
class PlanEdited(ThreadEvent):
"""Somebody changed a plan in a group."""
#: The updated plan
plan = attr.ib(type=_plan.PlanData)
#: When the plan was updated
at = attr.ib(type=datetime.datetime)
@classmethod
def _parse(cls, session, data):
author, thread, at = cls._parse_metadata(session, data)
plan = _plan.PlanData._from_pull(session, data["untypedData"])
return cls(author=author, thread=thread, plan=plan, at=at)
@attrs_event
class PlanDeleted(ThreadEvent):
"""Somebody removed a plan in a group."""
#: The removed plan
plan = attr.ib(type=_plan.PlanData)
#: When the plan was removed
at = attr.ib(type=datetime.datetime)
@classmethod
def _parse(cls, session, data):
author, thread, at = cls._parse_metadata(session, data)
plan = _plan.PlanData._from_pull(session, data["untypedData"])
return cls(author=author, thread=thread, plan=plan, at=at)
@attrs_event
class PlanResponded(ThreadEvent):
"""Somebody responded to a plan in a group."""
#: The plan that was responded to
plan = attr.ib(type=_plan.PlanData)
#: Whether the author will go to the plan or not
take_part = attr.ib(type=bool)
#: When the plan was removed
at = attr.ib(type=datetime.datetime)
@classmethod
def _parse(cls, session, data):
author, thread, at = cls._parse_metadata(session, data)
plan = _plan.PlanData._from_pull(session, data["untypedData"])
take_part = data["untypedData"]["guest_status"] == "GOING"
return cls(author=author, thread=thread, plan=plan, take_part=take_part, at=at)
def parse_delta(session, data):
type_ = data.get("type")
if type_ == "change_thread_theme":
return ColorSet._parse(session, data)
elif type_ == "change_thread_icon":
return EmojiSet._parse(session, data)
elif type_ == "change_thread_nickname":
return NicknameSet._parse(session, data)
elif type_ == "change_thread_admins":
event_type = data["untypedData"]["ADMIN_EVENT"]
if event_type == "add_admin":
return AdminsAdded._parse(session, data)
elif event_type == "remove_admin":
return AdminsRemoved._parse(session, data)
else:
pass
elif type_ == "change_thread_approval_mode":
return ApprovalModeSet._parse(session, data)
elif type_ == "instant_game_update":
pass # TODO: This
elif type_ == "messenger_call_log": # Previously "rtc_call_log"
event_type = data["untypedData"]["event"]
if event_type == "group_call_started":
return CallStarted._parse(session, data)
elif event_type in ["group_call_ended", "one_on_one_call_ended"]:
return CallEnded._parse(session, data)
else:
pass
elif type_ == "participant_joined_group_call":
return CallJoined._parse(session, data)
elif type_ == "group_poll":
event_type = data["untypedData"]["event_type"]
if event_type == "question_creation":
return PollCreated._parse(session, data)
elif event_type == "update_vote":
return PollVoted._parse(session, data)
else:
pass
elif type_ == "lightweight_event_create":
return PlanCreated._parse(session, data)
elif type_ == "lightweight_event_notify":
return PlanEnded._parse(session, data)
elif type_ == "lightweight_event_update":
return PlanEdited._parse(session, data)
elif type_ == "lightweight_event_delete":
return PlanDeleted._parse(session, data)
elif type_ == "lightweight_event_rsvp":
return PlanResponded._parse(session, data)
return UnknownEvent(data=data)

954
tests/test_delta_type.py Normal file
View File

@@ -0,0 +1,954 @@
import datetime
import pytest
from fbchat import (
_util,
ParseError,
User,
Group,
Poll,
PollOption,
PlanData,
GuestStatus,
UnknownEvent,
ColorSet,
EmojiSet,
NicknameSet,
AdminsAdded,
AdminsRemoved,
ApprovalModeSet,
CallStarted,
CallEnded,
CallJoined,
PollCreated,
PollVoted,
PlanCreated,
PlanEnded,
PlanEdited,
PlanDeleted,
PlanResponded,
)
from fbchat._message import Message, MessageData
from fbchat._delta_type import parse_delta
def test_color_set(session):
data = {
"irisSeqId": "1111111",
"irisTags": ["DeltaAdminTextMessage", "is_from_iris_fanout"],
"messageMetadata": {
"actorFbId": "1234",
"adminText": "You changed the chat theme to Orange.",
"folderId": {"systemFolderId": "INBOX"},
"messageId": "mid.$XYZ",
"offlineThreadingId": "11223344556677889900",
"skipBumpThread": False,
"tags": ["source:titan:web", "no_push"],
"threadKey": {"threadFbId": "4321"},
"threadReadStateEffect": "MARK_UNREAD",
"timestamp": "1500000000000",
"unsendType": "deny_log_message",
},
"participants": ["1234", "2345", "3456"],
"requestContext": {"apiArgs": {}},
"tqSeqId": "1111",
"type": "change_thread_theme",
"untypedData": {
"should_show_icon": "1",
"theme_color": "FFFF7E29",
"accessibility_label": "Orange",
},
"class": "AdminTextMessage",
}
assert ColorSet(
author=User(session=session, id="1234"),
thread=Group(session=session, id="4321"),
color="#ff7e29",
at=datetime.datetime(2017, 7, 14, 2, 40, tzinfo=datetime.timezone.utc),
) == parse_delta(session, data)
def test_emoji_set(session):
data = {
"irisSeqId": "1111111",
"irisTags": ["DeltaAdminTextMessage"],
"messageMetadata": {
"actorFbId": "1234",
"adminText": "You set the emoji to 🌟.",
"folderId": {"systemFolderId": "INBOX"},
"messageId": "mid.$XYZ",
"offlineThreadingId": "11223344556677889900",
"skipBumpThread": False,
"skipSnippetUpdate": False,
"tags": ["source:generic_admin_text"],
"threadKey": {"otherUserFbId": "1234"},
"threadReadStateEffect": "MARK_UNREAD",
"timestamp": "1500000000000",
"unsendType": "deny_log_message",
},
"requestContext": {"apiArgs": {}},
"type": "change_thread_icon",
"untypedData": {
"thread_icon_url": "https://www.facebook.com/images/emoji.php/v9/te0/1/16/1f31f.png",
"thread_icon": "🌟",
},
"class": "AdminTextMessage",
}
assert EmojiSet(
author=User(session=session, id="1234"),
thread=User(session=session, id="1234"),
emoji="🌟",
at=datetime.datetime(2017, 7, 14, 2, 40, tzinfo=datetime.timezone.utc),
) == parse_delta(session, data)
def test_nickname_set(session):
data = {
"irisSeqId": "1111111",
"irisTags": ["DeltaAdminTextMessage", "is_from_iris_fanout"],
"messageMetadata": {
"actorFbId": "1234",
"adminText": "You set the nickname for Abc Def to abc.",
"folderId": {"systemFolderId": "INBOX"},
"messageId": "mid.$XYZ",
"offlineThreadingId": "11223344556677889900",
"skipBumpThread": False,
"tags": ["source:titan:web", "no_push"],
"threadKey": {"threadFbId": "4321"},
"threadReadStateEffect": "MARK_UNREAD",
"timestamp": "1500000000000",
"unsendType": "deny_log_message",
},
"participants": ["1234", "2345", "3456"],
"requestContext": {"apiArgs": {}},
"tqSeqId": "1111",
"type": "change_thread_nickname",
"untypedData": {"nickname": "abc", "participant_id": "2345"},
"class": "AdminTextMessage",
}
assert NicknameSet(
author=User(session=session, id="1234"),
thread=Group(session=session, id="4321"),
subject=User(session=session, id="2345"),
nickname="abc",
at=datetime.datetime(2017, 7, 14, 2, 40, tzinfo=datetime.timezone.utc),
) == parse_delta(session, data)
def test_nickname_clear(session):
data = {
"irisSeqId": "1111111",
"irisTags": ["DeltaAdminTextMessage"],
"messageMetadata": {
"actorFbId": "1234",
"adminText": "You cleared your nickname.",
"folderId": {"systemFolderId": "INBOX"},
"messageId": "mid.$XYZ",
"offlineThreadingId": "11223344556677889900",
"skipBumpThread": False,
"skipSnippetUpdate": False,
"tags": ["source:generic_admin_text"],
"threadKey": {"otherUserFbId": "1234"},
"threadReadStateEffect": "MARK_UNREAD",
"timestamp": "1500000000000",
"unsendType": "deny_log_message",
},
"requestContext": {"apiArgs": {}},
"type": "change_thread_nickname",
"untypedData": {"nickname": "", "participant_id": "1234"},
"class": "AdminTextMessage",
}
assert NicknameSet(
author=User(session=session, id="1234"),
thread=User(session=session, id="1234"),
subject=User(session=session, id="1234"),
nickname=None,
at=datetime.datetime(2017, 7, 14, 2, 40, tzinfo=datetime.timezone.utc),
) == parse_delta(session, data)
def test_admins_added(session):
data = {
"irisSeqId": "1111111",
"irisTags": ["DeltaAdminTextMessage", "is_from_iris_fanout"],
"messageMetadata": {
"actorFbId": "1234",
"adminText": "You added Abc Def as a group admin.",
"folderId": {"systemFolderId": "INBOX"},
"messageId": "mid.$XYZ",
"offlineThreadingId": "11223344556677889900",
"skipBumpThread": True,
"tags": ["source:titan:web"],
"threadKey": {"threadFbId": "4321"},
"threadReadStateEffect": "KEEP_AS_IS",
"timestamp": "1500000000000",
"unsendType": "deny_log_message",
},
"participants": ["1234", "2345", "3456"],
"requestContext": {"apiArgs": {}},
"tqSeqId": "1111",
"type": "change_thread_admins",
"untypedData": {
"THREAD_CATEGORY": "GROUP",
"TARGET_ID": "2345",
"ADMIN_TYPE": "0",
"ADMIN_EVENT": "add_admin",
},
"class": "AdminTextMessage",
}
assert AdminsAdded(
author=User(session=session, id="1234"),
thread=Group(session=session, id="4321"),
added=[User(session=session, id="2345")],
at=datetime.datetime(2017, 7, 14, 2, 40, tzinfo=datetime.timezone.utc),
) == parse_delta(session, data)
def test_admins_removed(session):
data = {
"irisSeqId": "1111111",
"irisTags": ["DeltaAdminTextMessage", "is_from_iris_fanout"],
"messageMetadata": {
"actorFbId": "1234",
"adminText": "You removed yourself as a group admin.",
"folderId": {"systemFolderId": "INBOX"},
"messageId": "mid.$XYZ",
"offlineThreadingId": "11223344556677889900",
"skipBumpThread": True,
"tags": ["source:titan:web"],
"threadKey": {"threadFbId": "4321"},
"threadReadStateEffect": "KEEP_AS_IS",
"timestamp": "1500000000000",
"unsendType": "deny_log_message",
},
"participants": ["1234", "2345", "3456"],
"requestContext": {"apiArgs": {}},
"tqSeqId": "1111",
"type": "change_thread_admins",
"untypedData": {
"THREAD_CATEGORY": "GROUP",
"TARGET_ID": "1234",
"ADMIN_TYPE": "0",
"ADMIN_EVENT": "remove_admin",
},
"class": "AdminTextMessage",
}
assert AdminsRemoved(
author=User(session=session, id="1234"),
thread=Group(session=session, id="4321"),
removed=[User(session=session, id="1234")],
at=datetime.datetime(2017, 7, 14, 2, 40, tzinfo=datetime.timezone.utc),
) == parse_delta(session, data)
def test_approvalmode_set(session):
data = {
"irisSeqId": "1111111",
"irisTags": ["DeltaAdminTextMessage", "is_from_iris_fanout"],
"messageMetadata": {
"actorFbId": "1234",
"adminText": "You turned on member approval and will review requests to join the group.",
"folderId": {"systemFolderId": "INBOX"},
"messageId": "mid.$XYZ",
"offlineThreadingId": "11223344556677889900",
"skipBumpThread": True,
"tags": ["source:titan:web", "no_push"],
"threadKey": {"threadFbId": "4321"},
"threadReadStateEffect": "KEEP_AS_IS",
"timestamp": "1500000000000",
"unsendType": "deny_log_message",
},
"participants": ["1234", "2345", "3456"],
"requestContext": {"apiArgs": {}},
"tqSeqId": "1111",
"type": "change_thread_approval_mode",
"untypedData": {"APPROVAL_MODE": "1", "THREAD_CATEGORY": "GROUP"},
"class": "AdminTextMessage",
}
assert ApprovalModeSet(
author=User(session=session, id="1234"),
thread=Group(session=session, id="4321"),
require_admin_approval=True,
at=datetime.datetime(2017, 7, 14, 2, 40, tzinfo=datetime.timezone.utc),
) == parse_delta(session, data)
def test_approvalmode_unset(session):
data = {
"irisSeqId": "1111111",
"irisTags": ["DeltaAdminTextMessage", "is_from_iris_fanout"],
"messageMetadata": {
"actorFbId": "1234",
"adminText": "You turned off member approval. Anyone with the link can join the group.",
"folderId": {"systemFolderId": "INBOX"},
"messageId": "mid.$XYZ",
"offlineThreadingId": "11223344556677889900",
"skipBumpThread": True,
"tags": ["source:titan:web", "no_push"],
"threadKey": {"threadFbId": "4321"},
"threadReadStateEffect": "KEEP_AS_IS",
"timestamp": "1500000000000",
"unsendType": "deny_log_message",
},
"participants": ["1234", "2345", "3456"],
"requestContext": {"apiArgs": {}},
"tqSeqId": "1111",
"type": "change_thread_approval_mode",
"untypedData": {"APPROVAL_MODE": "0", "THREAD_CATEGORY": "GROUP"},
"class": "AdminTextMessage",
}
assert ApprovalModeSet(
author=User(session=session, id="1234"),
thread=Group(session=session, id="4321"),
require_admin_approval=False,
at=datetime.datetime(2017, 7, 14, 2, 40, tzinfo=datetime.timezone.utc),
) == parse_delta(session, data)
def test_call_started(session):
data = {
"irisSeqId": "1111111",
"irisTags": ["DeltaAdminTextMessage", "is_from_iris_fanout"],
"messageMetadata": {
"actorFbId": "1234",
"adminText": "You started a call.",
"folderId": {"systemFolderId": "INBOX"},
"messageId": "mid.$XYZ",
"offlineThreadingId": "11223344556677889900",
"skipBumpThread": False,
"tags": ["source:titan:web", "no_push"],
"threadKey": {"threadFbId": "4321"},
"threadReadStateEffect": "MARK_UNREAD",
"timestamp": "1500000000000",
"unsendType": "deny_log_message",
},
"participants": ["1234", "2345", "3456"],
"requestContext": {"apiArgs": {}},
"tqSeqId": "1111",
"type": "messenger_call_log",
"untypedData": {
"call_capture_attachments": "",
"caller_id": "1234",
"conference_name": "MESSENGER:134845267536444",
"rating": "",
"messenger_call_instance_id": "0",
"video": "",
"event": "group_call_started",
"server_info": "XYZ123ABC",
"call_duration": "0",
"callee_id": "0",
},
"class": "AdminTextMessage",
}
data2 = {
"callState": "AUDIO_GROUP_CALL",
"messageMetadata": {
"actorFbId": "1234",
"folderId": {"systemFolderId": "INBOX"},
"messageId": "mid.$XYZ",
"offlineThreadingId": "11223344556677889900",
"skipBumpThread": False,
"tags": [],
"threadKey": {"threadFbId": "4321"},
"threadReadStateEffect": "KEEP_AS_IS",
"timestamp": "1500000000000",
},
"serverInfoData": "XYZ123ABC",
"class": "RtcCallData",
}
assert CallStarted(
author=User(session=session, id="1234"),
thread=Group(session=session, id="4321"),
at=datetime.datetime(2017, 7, 14, 2, 40, tzinfo=datetime.timezone.utc),
) == parse_delta(session, data)
def test_group_call_ended(session):
data = {
"irisSeqId": "1111111",
"irisTags": ["DeltaAdminTextMessage", "is_from_iris_fanout"],
"messageMetadata": {
"actorFbId": "1234",
"adminText": "The call ended.",
"folderId": {"systemFolderId": "INBOX"},
"messageId": "mid.$XYZ",
"offlineThreadingId": "11223344556677889900",
"skipBumpThread": False,
"tags": ["source:titan:web", "no_push"],
"threadKey": {"threadFbId": "4321"},
"threadReadStateEffect": "MARK_UNREAD",
"timestamp": "1500000000000",
"unsendType": "deny_log_message",
},
"participants": ["1234", "2345", "3456"],
"requestContext": {"apiArgs": {}},
"tqSeqId": "1111",
"type": "messenger_call_log",
"untypedData": {
"call_capture_attachments": "",
"caller_id": "1234",
"conference_name": "MESSENGER:1234567890",
"rating": "0",
"messenger_call_instance_id": "1234567890",
"video": "",
"event": "group_call_ended",
"server_info": "XYZ123ABC",
"call_duration": "31",
"callee_id": "0",
},
"class": "AdminTextMessage",
}
data2 = {
"callState": "NO_ONGOING_CALL",
"messageMetadata": {
"actorFbId": "1234",
"folderId": {"systemFolderId": "INBOX"},
"messageId": "mid.$XYZ",
"offlineThreadingId": "11223344556677889900",
"skipBumpThread": False,
"tags": [],
"threadKey": {"threadFbId": "4321"},
"threadReadStateEffect": "KEEP_AS_IS",
"timestamp": "1500000000000",
},
"class": "RtcCallData",
}
assert CallEnded(
author=User(session=session, id="1234"),
thread=Group(session=session, id="4321"),
duration=datetime.timedelta(seconds=31),
at=datetime.datetime(2017, 7, 14, 2, 40, tzinfo=datetime.timezone.utc),
) == parse_delta(session, data)
def test_user_call_ended(session):
data = {
"irisSeqId": "1111111",
"irisTags": ["DeltaAdminTextMessage", "is_from_iris_fanout"],
"messageMetadata": {
"actorFbId": "1234",
"adminText": "Abc called you.",
"folderId": {"systemFolderId": "INBOX"},
"messageId": "mid.$XYZ",
"offlineThreadingId": "11223344556677889900",
"skipBumpThread": False,
"skipSnippetUpdate": False,
"tags": ["source:generic_admin_text", "no_push"],
"threadKey": {"otherUserFbId": "1234"},
"threadReadStateEffect": "KEEP_AS_IS",
"timestamp": "1500000000000",
"unsendType": "deny_log_message",
},
"requestContext": {"apiArgs": {}},
"type": "messenger_call_log",
"untypedData": {
"call_capture_attachments": "",
"caller_id": "1234",
"conference_name": "MESSENGER:1234567890",
"rating": "0",
"messenger_call_instance_id": "1234567890",
"video": "",
"event": "one_on_one_call_ended",
"server_info": "",
"call_duration": "3",
"callee_id": "100002950119740",
},
"class": "AdminTextMessage",
}
assert CallEnded(
author=User(session=session, id="1234"),
thread=User(session=session, id="1234"),
duration=datetime.timedelta(seconds=3),
at=datetime.datetime(2017, 7, 14, 2, 40, tzinfo=datetime.timezone.utc),
) == parse_delta(session, data)
def test_call_joined(session):
data = {
"irisSeqId": "1111111",
"irisTags": ["DeltaAdminTextMessage", "is_from_iris_fanout"],
"messageMetadata": {
"actorFbId": "1234",
"adminText": "Abc joined the call.",
"folderId": {"systemFolderId": "INBOX"},
"messageId": "mid.$XYZ",
"offlineThreadingId": "11223344556677889900",
"skipBumpThread": False,
"tags": ["source:titan:web"],
"threadKey": {"threadFbId": "4321"},
"threadReadStateEffect": "MARK_UNREAD",
"timestamp": "1500000000000",
"unsendType": "deny_log_message",
},
"participants": ["1234", "2345", "3456"],
"requestContext": {"apiArgs": {}},
"tqSeqId": "1111",
"type": "participant_joined_group_call",
"untypedData": {
"server_info_data": "XYZ123ABC",
"group_call_type": "0",
"joining_user": "2345",
},
"class": "AdminTextMessage",
}
assert CallJoined(
author=User(session=session, id="1234"),
thread=Group(session=session, id="4321"),
at=datetime.datetime(2017, 7, 14, 2, 40, tzinfo=datetime.timezone.utc),
) == parse_delta(session, data)
def test_poll_created(session):
poll_data = {
"id": "112233",
"text": "A poll",
"total_count": 2,
"viewer_has_voted": "true",
"options": [
{
"id": "1001",
"text": "Option A",
"total_count": 1,
"viewer_has_voted": "true",
"voters": ["1234"],
},
{
"id": "1002",
"text": "Option B",
"total_count": 0,
"viewer_has_voted": "false",
"voters": [],
},
],
}
data = {
"irisSeqId": "1111111",
"irisTags": ["DeltaAdminTextMessage", "is_from_iris_fanout"],
"messageMetadata": {
"actorFbId": "1234",
"adminText": "You created a poll: A poll.",
"folderId": {"systemFolderId": "INBOX"},
"messageId": "mid.$XYZ",
"offlineThreadingId": "11223344556677889900",
"skipBumpThread": False,
"tags": ["source:titan:web"],
"threadKey": {"threadFbId": "4321"},
"threadReadStateEffect": "MARK_UNREAD",
"timestamp": "1500000000000",
"unsendType": "deny_log_message",
},
"participants": ["1234", "2345", "3456"],
"requestContext": {"apiArgs": {}},
"tqSeqId": "1111",
"type": "group_poll",
"untypedData": {
"added_option_ids": "[]",
"removed_option_ids": "[]",
"question_json": _util.json_minimal(poll_data),
"event_type": "question_creation",
"question_id": "112233",
},
"class": "AdminTextMessage",
}
assert PollCreated(
author=User(session=session, id="1234"),
thread=Group(session=session, id="4321"),
poll=Poll(
session=session,
id="112233",
question="A poll",
options=[
PollOption(
id="1001",
text="Option A",
vote=True,
voters=["1234"],
votes_count=1,
),
PollOption(
id="1002", text="Option B", vote=False, voters=[], votes_count=0
),
],
options_count=2,
),
at=datetime.datetime(2017, 7, 14, 2, 40, tzinfo=datetime.timezone.utc),
) == parse_delta(session, data)
def test_poll_answered(session):
poll_data = {
"id": "112233",
"text": "A poll",
"total_count": 3,
"viewer_has_voted": "true",
"options": [
{
"id": "1002",
"text": "Option B",
"total_count": 2,
"viewer_has_voted": "true",
"voters": ["1234", "2345"],
},
{
"id": "1003",
"text": "Option C",
"total_count": 1,
"viewer_has_voted": "true",
"voters": ["1234"],
},
{
"id": "1001",
"text": "Option A",
"total_count": 0,
"viewer_has_voted": "false",
"voters": [],
},
],
}
data = {
"irisSeqId": "1111111",
"irisTags": ["DeltaAdminTextMessage", "is_from_iris_fanout"],
"messageMetadata": {
"actorFbId": "1234",
"adminText": 'You changed your vote to "Option B" and 1 other option in the poll: A poll.',
"folderId": {"systemFolderId": "INBOX"},
"messageId": "mid.$XYZ",
"offlineThreadingId": "11223344556677889900",
"skipBumpThread": False,
"tags": ["source:titan:web"],
"threadKey": {"threadFbId": "4321"},
"threadReadStateEffect": "MARK_UNREAD",
"timestamp": "1500000000000",
"unsendType": "deny_log_message",
},
"participants": ["1234", "2345", "3456"],
"requestContext": {"apiArgs": {}},
"tqSeqId": "1111",
"type": "group_poll",
"untypedData": {
"added_option_ids": "[1002,1003]",
"removed_option_ids": "[1001]",
"question_json": _util.json_minimal(poll_data),
"event_type": "update_vote",
"question_id": "112233",
},
"class": "AdminTextMessage",
}
assert PollVoted(
author=User(session=session, id="1234"),
thread=Group(session=session, id="4321"),
poll=Poll(
session=session,
id="112233",
question="A poll",
options=[
PollOption(
id="1002",
text="Option B",
vote=True,
voters=["1234", "2345"],
votes_count=2,
),
PollOption(
id="1003",
text="Option C",
vote=True,
voters=["1234"],
votes_count=1,
),
PollOption(
id="1001", text="Option A", vote=False, voters=[], votes_count=0
),
],
options_count=3,
),
added_ids=["1002", "1003"],
removed_ids=["1001"],
at=datetime.datetime(2017, 7, 14, 2, 40, tzinfo=datetime.timezone.utc),
) == parse_delta(session, data)
def test_plan_created(session):
guest_list = [
{"guest_list_state": "INVITED", "node": {"id": "3456"}},
{"guest_list_state": "INVITED", "node": {"id": "2345"}},
{"guest_list_state": "GOING", "node": {"id": "1234"}},
]
data = {
"irisSeqId": "1111111",
"irisTags": ["DeltaAdminTextMessage", "is_from_iris_fanout"],
"messageMetadata": {
"actorFbId": "1234",
"adminText": "You created a plan.",
"folderId": {"systemFolderId": "INBOX"},
"messageId": "mid.$XYZ",
"offlineThreadingId": "11223344556677889900",
"skipBumpThread": False,
"tags": ["source:titan:web", "no_push"],
"threadKey": {"threadFbId": "4321"},
"threadReadStateEffect": "MARK_UNREAD",
"timestamp": "1500000000000",
"unsendType": "deny_log_message",
},
"participants": ["1234", "2345", "3456"],
"requestContext": {"apiArgs": {}},
"tqSeqId": "1111",
"type": "lightweight_event_create",
"untypedData": {
"event_timezone": "",
"event_creator_id": "1234",
"event_id": "112233",
"event_type": "EVENT",
"event_track_rsvp": "1",
"event_title": "A plan",
"event_time": "1600000000",
"event_seconds_to_notify_before": "3600",
"guest_state_list": _util.json_minimal(guest_list),
},
"class": "AdminTextMessage",
}
assert PlanCreated(
author=User(session=session, id="1234"),
thread=Group(session=session, id="4321"),
plan=PlanData(
session=session,
id="112233",
time=datetime.datetime(
2020, 9, 13, 12, 26, 40, tzinfo=datetime.timezone.utc
),
title="A plan",
author_id="1234",
guests={
"1234": GuestStatus.GOING,
"2345": GuestStatus.INVITED,
"3456": GuestStatus.INVITED,
},
),
at=datetime.datetime(2017, 7, 14, 2, 40, tzinfo=datetime.timezone.utc),
) == parse_delta(session, data)
@pytest.mark.skip(reason="Need to gather test data")
def test_plan_ended(session):
data = {}
assert PlanEnded(
author=User(session=session, id="1234"),
thread=Group(session=session, id="4321"),
plan=PlanData(
session=session,
id="112233",
time=datetime.datetime(
2020, 9, 13, 12, 26, 40, tzinfo=datetime.timezone.utc
),
title="A plan",
author_id="1234",
guests={
"1234": GuestStatus.GOING,
"2345": GuestStatus.INVITED,
"3456": GuestStatus.INVITED,
},
),
at=datetime.datetime(2017, 7, 14, 2, 40, tzinfo=datetime.timezone.utc),
) == parse_delta(session, data)
def test_plan_edited(session):
guest_list = [
{"guest_list_state": "INVITED", "node": {"id": "3456"}},
{"guest_list_state": "INVITED", "node": {"id": "2345"}},
{"guest_list_state": "GOING", "node": {"id": "1234"}},
]
data = {
"irisSeqId": "1111111",
"irisTags": ["DeltaAdminTextMessage", "is_from_iris_fanout"],
"messageMetadata": {
"actorFbId": "1234",
"adminText": "You named the plan A plan.",
"folderId": {"systemFolderId": "INBOX"},
"messageId": "mid.$XYZ",
"offlineThreadingId": "11223344556677889900",
"skipBumpThread": False,
"tags": ["source:titan:web", "no_push"],
"threadKey": {"threadFbId": "4321"},
"threadReadStateEffect": "MARK_UNREAD",
"timestamp": "1500000000000",
"unsendType": "deny_log_message",
},
"participants": ["1234", "2345", "3456"],
"requestContext": {"apiArgs": {}},
"tqSeqId": "1111",
"type": "lightweight_event_update",
"untypedData": {
"event_creator_id": "1234",
"latitude": "0",
"event_title": "A plan",
"event_seconds_to_notify_before": "3600",
"guest_state_list": _util.json_minimal(guest_list),
"event_end_time": "0",
"event_timezone": "",
"event_id": "112233",
"event_type": "EVENT",
"event_location_id": "2233445566",
"event_location_name": "",
"event_time": "1600000000",
"event_note": "",
"longitude": "0",
},
"class": "AdminTextMessage",
}
assert PlanEdited(
author=User(session=session, id="1234"),
thread=Group(session=session, id="4321"),
plan=PlanData(
session=session,
id="112233",
time=datetime.datetime(
2020, 9, 13, 12, 26, 40, tzinfo=datetime.timezone.utc
),
title="A plan",
location_id="2233445566",
author_id="1234",
guests={
"1234": GuestStatus.GOING,
"2345": GuestStatus.INVITED,
"3456": GuestStatus.INVITED,
},
),
at=datetime.datetime(2017, 7, 14, 2, 40, tzinfo=datetime.timezone.utc),
) == parse_delta(session, data)
def test_plan_deleted(session):
guest_list = [
{"guest_list_state": "GOING", "node": {"id": "1234"}},
{"guest_list_state": "INVITED", "node": {"id": "3456"}},
{"guest_list_state": "INVITED", "node": {"id": "2345"}},
]
data = {
"irisSeqId": "1111111",
"irisTags": ["DeltaAdminTextMessage", "is_from_iris_fanout"],
"messageMetadata": {
"actorFbId": "1234",
"adminText": "You deleted the plan A plan for Mon, 20 Jan at 15:30.",
"folderId": {"systemFolderId": "INBOX"},
"messageId": "mid.$XYZ",
"offlineThreadingId": "11223344556677889900",
"skipBumpThread": False,
"tags": ["source:titan:web", "no_push"],
"threadKey": {"threadFbId": "4321"},
"threadReadStateEffect": "MARK_UNREAD",
"timestamp": "1500000000000",
"unsendType": "deny_log_message",
},
"participants": ["1234", "2345", "3456"],
"requestContext": {"apiArgs": {}},
"tqSeqId": "1111",
"type": "lightweight_event_delete",
"untypedData": {
"event_end_time": "0",
"event_timezone": "",
"event_id": "112233",
"event_type": "EVENT",
"event_location_id": "2233445566",
"latitude": "0",
"event_title": "A plan",
"event_time": "1600000000",
"event_seconds_to_notify_before": "3600",
"guest_state_list": _util.json_minimal(guest_list),
"event_note": "",
"longitude": "0",
},
"class": "AdminTextMessage",
}
assert PlanDeleted(
author=User(session=session, id="1234"),
thread=Group(session=session, id="4321"),
plan=PlanData(
session=session,
id="112233",
time=datetime.datetime(
2020, 9, 13, 12, 26, 40, tzinfo=datetime.timezone.utc
),
title="A plan",
location_id="2233445566",
author_id=None,
guests={
"1234": GuestStatus.GOING,
"2345": GuestStatus.INVITED,
"3456": GuestStatus.INVITED,
},
),
at=datetime.datetime(2017, 7, 14, 2, 40, tzinfo=datetime.timezone.utc),
) == parse_delta(session, data)
def test_plan_participation(session):
guest_list = [
{"guest_list_state": "DECLINED", "node": {"id": "1234"}},
{"guest_list_state": "GOING", "node": {"id": "2345"}},
{"guest_list_state": "INVITED", "node": {"id": "3456"}},
]
data = {
"irisSeqId": "1111111",
"irisTags": ["DeltaAdminTextMessage", "is_from_iris_fanout"],
"messageMetadata": {
"actorFbId": "1234",
"adminText": "You responded Can't Go to def.",
"folderId": {"systemFolderId": "INBOX"},
"messageId": "mid.$XYZ",
"offlineThreadingId": "11223344556677889900",
"skipBumpThread": False,
"tags": ["source:titan:web", "no_push"],
"threadKey": {"threadFbId": "4321"},
"threadReadStateEffect": "MARK_UNREAD",
"timestamp": "1500000000000",
"unsendType": "deny_log_message",
},
"participants": ["1234", "2345", "3456"],
"requestContext": {"apiArgs": {}},
"tqSeqId": "1111",
"type": "lightweight_event_rsvp",
"untypedData": {
"event_creator_id": "2345",
"guest_status": "DECLINED",
"latitude": "0",
"event_track_rsvp": "1",
"event_title": "A plan",
"event_seconds_to_notify_before": "3600",
"guest_state_list": _util.json_minimal(guest_list),
"event_end_time": "0",
"event_timezone": "",
"event_id": "112233",
"event_type": "EVENT",
"guest_id": "1234",
"event_location_id": "2233445566",
"event_time": "1600000000",
"event_note": "",
"longitude": "0",
},
"class": "AdminTextMessage",
}
assert PlanResponded(
author=User(session=session, id="1234"),
thread=Group(session=session, id="4321"),
plan=PlanData(
session=session,
id="112233",
time=datetime.datetime(
2020, 9, 13, 12, 26, 40, tzinfo=datetime.timezone.utc
),
title="A plan",
location_id="2233445566",
author_id="2345",
guests={
"1234": GuestStatus.DECLINED,
"2345": GuestStatus.GOING,
"3456": GuestStatus.INVITED,
},
),
take_part=False,
at=datetime.datetime(2017, 7, 14, 2, 40, tzinfo=datetime.timezone.utc),
) == parse_delta(session, data)
def test_parse_delta_unknown(session):
assert UnknownEvent(data={"abc": 10}) == parse_delta(session, {"abc": 10})