From d12f9fd6451cc67dd4032f64a01c9a1eb6b571b0 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 8 Sep 2019 15:20:30 +0200 Subject: [PATCH 01/10] Add datetime helper functions --- fbchat/_util.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/fbchat/_util.py b/fbchat/_util.py index 87dd185..09ad6f1 100644 --- a/fbchat/_util.py +++ b/fbchat/_util.py @@ -1,3 +1,4 @@ +import datetime import json import time import random @@ -232,3 +233,39 @@ def prefix_url(url): if url.startswith("/"): return "https://www.facebook.com" + url return url + + +def seconds_to_datetime(timestamp_in_seconds): + """Convert an UTC timestamp to a timezone-aware datetime object.""" + # `.utcfromtimestamp` will return a "naive" datetime object, which is why we use the + # following: + return datetime.datetime.fromtimestamp( + timestamp_in_seconds, tz=datetime.timezone.utc + ) + + +def millis_to_datetime(timestamp_in_milliseconds): + """Convert an UTC timestamp, in milliseconds, to a timezone-aware datetime.""" + return seconds_to_datetime(timestamp_in_milliseconds / 1000) + + +def datetime_to_seconds(dt): + """Convert a datetime to an UTC timestamp. + + Naive datetime objects are presumed to represent time in the system timezone. + + The returned seconds will be rounded to the nearest whole number. + """ + # We could've implemented some fancy "convert naive timezones to UTC" logic, but + # it's not really worth the effort. + return round(dt.timestamp()) + + +def datetime_to_millis(dt): + """Convert a datetime to an UTC timestamp, in milliseconds. + + Naive datetime objects are presumed to represent time in the system timezone. + + The returned milliseconds will be rounded to the nearest whole number. + """ + return round(dt.timestamp() * 1000) From ba088d45a72d685d1fbb9b85b6b6ca960c46afcc Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 8 Sep 2019 14:49:49 +0200 Subject: [PATCH 02/10] Make Client fetching methods use datetime objects On: - Client.fetchThreads after and before arguments - Client.fetchThreadMessages before argument - Client.fetchThreadList before argument --- fbchat/_client.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/fbchat/_client.py b/fbchat/_client.py index 78c8820..421c3b6 100644 --- a/fbchat/_client.py +++ b/fbchat/_client.py @@ -289,8 +289,10 @@ class Client: Args: thread_location (ThreadLocation): INBOX, PENDING, ARCHIVED or OTHER - before: Fetch only thread before this epoch (in ms) (default all threads) - after: Fetch only thread after this epoch (in ms) (default all threads) + before (datetime.datetime): Fetch only threads before this (default all + threads). Must be timezone-aware! + after (datetime.datetime): Fetch only threads after this (default all + threads). Must be timezone-aware! limit: The max. amount of threads to fetch (default all threads) Returns: @@ -301,15 +303,15 @@ class Client: """ threads = [] - last_thread_timestamp = None + last_thread_dt = None while True: # break if limit is exceeded if limit and len(threads) >= limit: break - # fetchThreadList returns at max 20 threads before last_thread_timestamp (included) + # fetchThreadList returns at max 20 threads before last_thread_dt (included) candidates = self.fetchThreadList( - before=last_thread_timestamp, thread_location=thread_location + before=last_thread_dt, thread_location=thread_location ) if len(candidates) > 1: @@ -317,20 +319,22 @@ class Client: else: # End of threads break - last_thread_timestamp = threads[-1].last_message_timestamp + last_thread_dt = _util.millis_to_datetime( + threads[-1].last_message_timestamp + ) # FB returns a sorted list of threads - if (before is not None and int(last_thread_timestamp) > before) or ( - after is not None and int(last_thread_timestamp) < after + if (before is not None and last_thread_dt > before) or ( + after is not None and last_thread_dt < after ): break # Return only threads between before and after (if set) if before is not None or after is not None: for t in threads: - last_message_timestamp = int(t.last_message_timestamp) - if (before is not None and last_message_timestamp > before) or ( - after is not None and last_message_timestamp < after + last_message_dt = _util.millis_to_datetime(t.last_message_timestamp) + if (before is not None and last_message_dt > before) or ( + after is not None and last_message_dt < after ): threads.remove(t) @@ -746,7 +750,7 @@ class Client: Args: thread_id: User/Group ID to get messages from. See :ref:`intro_threads` limit (int): Max. number of messages to retrieve - before (int): A timestamp, indicating from which point to retrieve messages + before (datetime.datetime): The point from which to retrieve messages Returns: list: :class:`Message` objects @@ -761,7 +765,7 @@ class Client: "message_limit": limit, "load_messages": True, "load_read_receipts": True, - "before": before, + "before": _util.datetime_to_millis(before) if before else None, } j, = self.graphql_requests(_graphql.from_doc_id("1860982147341344", params)) @@ -792,7 +796,7 @@ class Client: offset: Deprecated. Do not use! limit (int): Max. number of threads to retrieve. Capped at 20 thread_location (ThreadLocation): INBOX, PENDING, ARCHIVED or OTHER - before (int): A timestamp (in milliseconds), indicating from which point to retrieve threads + before (datetime.datetime): The point from which to retrieve threads Returns: list: :class:`Thread` objects @@ -818,7 +822,7 @@ class Client: params = { "limit": limit, "tags": [loc_str], - "before": before, + "before": _util.datetime_to_millis(before) if before else None, "includeDeliveryReceipts": True, "includeSeqID": False, } From 4b34a063e8e79d2e593dc6bda9ff35d5913ba8be Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 3 Sep 2019 12:39:03 +0200 Subject: [PATCH 03/10] Rename Thread.last_message_timestamp to .last_active, and use datetimes --- fbchat/_client.py | 9 +++------ fbchat/_group.py | 12 ++++++------ fbchat/_thread.py | 8 ++++---- fbchat/_user.py | 12 ++++++------ 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/fbchat/_client.py b/fbchat/_client.py index 421c3b6..0465ba5 100644 --- a/fbchat/_client.py +++ b/fbchat/_client.py @@ -319,9 +319,7 @@ class Client: else: # End of threads break - last_thread_dt = _util.millis_to_datetime( - threads[-1].last_message_timestamp - ) + last_thread_dt = threads[-1].last_active # FB returns a sorted list of threads if (before is not None and last_thread_dt > before) or ( @@ -332,9 +330,8 @@ class Client: # Return only threads between before and after (if set) if before is not None or after is not None: for t in threads: - last_message_dt = _util.millis_to_datetime(t.last_message_timestamp) - if (before is not None and last_message_dt > before) or ( - after is not None and last_message_dt < after + if (before is not None and t.last_active > before) or ( + after is not None and t.last_active < after ): threads.remove(t) diff --git a/fbchat/_group.py b/fbchat/_group.py index e4af880..c0eeddd 100644 --- a/fbchat/_group.py +++ b/fbchat/_group.py @@ -1,5 +1,5 @@ import attr -from . import _plan +from . import _util, _plan from ._thread import ThreadType, Thread @@ -63,11 +63,11 @@ class Group(Thread): if data.get("image") is None: data["image"] = {} c_info = cls._parse_customization_info(data) - last_message_timestamp = None + last_active = None if "last_message" in data: - last_message_timestamp = data["last_message"]["nodes"][0][ - "timestamp_precise" - ] + last_active = _util.millis_to_datetime( + int(data["last_message"]["nodes"][0]["timestamp_precise"]) + ) plan = None if data.get("event_reminders") and data["event_reminders"].get("nodes"): plan = _plan.Plan._from_graphql(data["event_reminders"]["nodes"][0]) @@ -97,7 +97,7 @@ class Group(Thread): photo=data["image"].get("uri"), name=data.get("name"), message_count=data.get("messages_count"), - last_message_timestamp=last_message_timestamp, + last_active=last_active, plan=plan, ) diff --git a/fbchat/_thread.py b/fbchat/_thread.py index e39b4b4..6cc444c 100644 --- a/fbchat/_thread.py +++ b/fbchat/_thread.py @@ -81,8 +81,8 @@ class Thread: photo = attr.ib(None) #: The name of the thread name = attr.ib(None) - #: Timestamp of last message - last_message_timestamp = attr.ib(None) + #: Datetime when the thread was last active / when the last message was sent + last_active = attr.ib(None) #: Number of messages in the thread message_count = attr.ib(None) #: Set :class:`Plan` @@ -94,7 +94,7 @@ class Thread: uid, photo=None, name=None, - last_message_timestamp=None, + last_active=None, message_count=None, plan=None, ): @@ -102,7 +102,7 @@ class Thread: self.type = _type self.photo = photo self.name = name - self.last_message_timestamp = last_message_timestamp + self.last_active = last_active self.message_count = message_count self.plan = plan diff --git a/fbchat/_user.py b/fbchat/_user.py index b864b13..d0dd42d 100644 --- a/fbchat/_user.py +++ b/fbchat/_user.py @@ -1,6 +1,6 @@ import attr from ._core import Enum -from . import _plan +from . import _util, _plan from ._thread import ThreadType, Thread @@ -131,11 +131,11 @@ class User(Thread): user = next( p for p in participants if p["id"] == data["thread_key"]["other_user_id"] ) - last_message_timestamp = None + last_active = None if "last_message" in data: - last_message_timestamp = data["last_message"]["nodes"][0][ - "timestamp_precise" - ] + last_active = _util.millis_to_datetime( + int(data["last_message"]["nodes"][0]["timestamp_precise"]) + ) first_name = user.get("short_name") if first_name is None: @@ -162,7 +162,7 @@ class User(Thread): own_nickname=c_info.get("own_nickname"), photo=user["big_image_src"].get("uri"), message_count=data.get("messages_count"), - last_message_timestamp=last_message_timestamp, + last_active=last_active, plan=plan, ) From 6d13937c4aa5c82e02d75562d8ecc74a931fbb16 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 8 Sep 2019 15:24:57 +0200 Subject: [PATCH 04/10] Make Plan.time a datetime object --- fbchat/_client.py | 4 ++-- fbchat/_plan.py | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/fbchat/_client.py b/fbchat/_client.py index 0465ba5..20784ab 100644 --- a/fbchat/_client.py +++ b/fbchat/_client.py @@ -1733,7 +1733,7 @@ class Client: data = { "event_type": "EVENT", - "event_time": plan.time, + "event_time": _util.datetime_to_seconds(plan.time), "title": plan.title, "thread_id": thread_id, "location_id": plan.location_id or "", @@ -1760,7 +1760,7 @@ class Client: data = { "event_reminder_id": plan.uid, "delete": "false", - "date": new_plan.time, + "date": _util.datetime_to_seconds(new_plan.time), "location_name": new_plan.location or "", "location_id": new_plan.location_id or "", "title": new_plan.title, diff --git a/fbchat/_plan.py b/fbchat/_plan.py index 1a3c5bd..373f8da 100644 --- a/fbchat/_plan.py +++ b/fbchat/_plan.py @@ -1,6 +1,7 @@ import attr import json from ._core import Enum +from . import _util class GuestStatus(Enum): @@ -15,8 +16,8 @@ class Plan: #: ID of the plan uid = attr.ib(None, init=False) - #: Plan time (timestamp), only precise down to the minute - time = attr.ib(converter=int) + #: Plan time (datetime), only precise down to the minute + time = attr.ib() #: Plan title title = attr.ib() #: Plan location name @@ -58,7 +59,7 @@ class Plan: @classmethod def _from_pull(cls, data): rtn = cls( - time=data.get("event_time"), + time=_util.seconds_to_datetime(int(data.get("event_time"))), title=data.get("event_title"), location=data.get("event_location_name"), location_id=data.get("event_location_id"), @@ -74,7 +75,7 @@ class Plan: @classmethod def _from_fetch(cls, data): rtn = cls( - time=data.get("event_time"), + time=_util.seconds_to_datetime(data.get("event_time")), title=data.get("title"), location=data.get("location_name"), location_id=str(data["location_id"]) if data.get("location_id") else None, @@ -87,7 +88,7 @@ class Plan: @classmethod def _from_graphql(cls, data): rtn = cls( - time=data.get("time"), + time=_util.seconds_to_datetime(data.get("time")), title=data.get("event_title"), location=data.get("location_name"), ) From aef64e5c29f211531a3d3670062537d5cbf4c575 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 8 Sep 2019 12:59:14 +0200 Subject: [PATCH 05/10] Make Message.timestamp a datetime object, and rename to .created_at --- fbchat/_client.py | 9 ++++++--- fbchat/_message.py | 12 ++++++------ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/fbchat/_client.py b/fbchat/_client.py index 20784ab..e977679 100644 --- a/fbchat/_client.py +++ b/fbchat/_client.py @@ -779,7 +779,10 @@ class Client: for message in messages: for receipt in read_receipts: - if int(receipt["watermark"]) >= int(message.timestamp): + if ( + _util.millis_to_datetime(int(receipt["watermark"])) + >= message.created_at + ): message.read_by.append(receipt["actor"]["id"]) return messages @@ -2721,7 +2724,7 @@ class Client: message_object=message, thread_id=thread_id, thread_type=thread_type, - ts=message.timestamp, + ts=_util.datetime_to_millis(message.created_at), metadata=metadata, msg=m, ) @@ -2738,7 +2741,7 @@ class Client: mid=mid, tags=metadata.get("tags"), author=author_id, - timestamp=ts, + created_at=_util.millis_to_datetime(ts), ), thread_id=thread_id, thread_type=thread_type, diff --git a/fbchat/_message.py b/fbchat/_message.py index 672f6c1..97eb2e9 100644 --- a/fbchat/_message.py +++ b/fbchat/_message.py @@ -68,8 +68,8 @@ class Message: uid = attr.ib(None, init=False) #: ID of the sender author = attr.ib(None, init=False) - #: Timestamp of when the message was sent - timestamp = attr.ib(None, init=False) + #: Datetime of when the message was sent + created_at = attr.ib(None, init=False) #: Whether the message is read is_read = attr.ib(None, init=False) #: A list of people IDs who read the message, works only with :func:`fbchat.Client.fetchThreadMessages` @@ -220,7 +220,7 @@ class Message: rtn.forwarded = cls._get_forwarded_from_tags(tags) rtn.uid = str(data["message_id"]) rtn.author = str(data["message_sender"]["id"]) - rtn.timestamp = data.get("timestamp_precise") + rtn.created_at = _util.millis_to_datetime(int(data.get("timestamp_precise"))) rtn.unsent = False if data.get("unread") is not None: rtn.is_read = not data["unread"] @@ -271,7 +271,7 @@ class Message: rtn.forwarded = cls._get_forwarded_from_tags(tags) rtn.uid = metadata.get("messageId") rtn.author = str(metadata.get("actorFbId")) - rtn.timestamp = metadata.get("timestamp") + rtn.created_at = _util.millis_to_datetime(metadata.get("timestamp")) rtn.unsent = False if data.get("data", {}).get("platform_xmd"): quick_replies = json.loads(data["data"]["platform_xmd"]).get( @@ -307,11 +307,11 @@ class Message: return rtn @classmethod - def _from_pull(cls, data, mid=None, tags=None, author=None, timestamp=None): + def _from_pull(cls, data, mid=None, tags=None, author=None, created_at=None): rtn = cls(text=data.get("body")) rtn.uid = mid rtn.author = author - rtn.timestamp = timestamp + rtn.created_at = created_at if data.get("data") and data["data"].get("prng"): try: From 61842b199f9e9918f65d3d5c974bbb9d88abdcda Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 8 Sep 2019 13:02:57 +0200 Subject: [PATCH 06/10] Make ActiveStatus.last_active a datetime object --- fbchat/_user.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fbchat/_user.py b/fbchat/_user.py index d0dd42d..4eb0fbc 100644 --- a/fbchat/_user.py +++ b/fbchat/_user.py @@ -183,7 +183,7 @@ class User(Thread): class ActiveStatus: #: Whether the user is active now active = attr.ib(None) - #: Timestamp when the user was last active + #: Datetime when the user was last active last_active = attr.ib(None) #: Whether the user is playing Messenger game now in_game = attr.ib(None) @@ -192,7 +192,7 @@ class ActiveStatus: def _from_chatproxy_presence(cls, id_, data): return cls( active=data["p"] in [2, 3] if "p" in data else None, - last_active=data.get("lat"), + last_active=_util.millis_to_datetime(data.get("lat")), in_game=int(id_) in data.get("gamers", {}), ) @@ -200,6 +200,6 @@ class ActiveStatus: def _from_buddylist_overlay(cls, data, in_game=None): return cls( active=data["a"] in [2, 3] if "a" in data else None, - last_active=data.get("la"), + last_active=_util.millis_to_datetime(data.get("la")), in_game=None, ) From 2e53963398b3cf7609c17430c88979af4c5cebc4 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 8 Sep 2019 13:03:46 +0200 Subject: [PATCH 07/10] Make LiveLocationAttachment.expires_at a datetime object Renamed from .expiration_time --- fbchat/_location.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fbchat/_location.py b/fbchat/_location.py index a3dc829..3a4f2af 100644 --- a/fbchat/_location.py +++ b/fbchat/_location.py @@ -59,14 +59,14 @@ class LiveLocationAttachment(LocationAttachment): #: Name of the location name = attr.ib(None) - #: Timestamp when live location expires - expiration_time = attr.ib(None) + #: Datetime when live location expires + expires_at = attr.ib(None) #: True if live location is expired is_expired = attr.ib(None) - def __init__(self, name=None, expiration_time=None, is_expired=None, **kwargs): + def __init__(self, name=None, expires_at=None, is_expired=None, **kwargs): super(LiveLocationAttachment, self).__init__(**kwargs) - self.expiration_time = expiration_time + self.expires_at = expires_at self.is_expired = is_expired @classmethod @@ -80,7 +80,7 @@ class LiveLocationAttachment(LocationAttachment): if not data.get("stopReason") else None, name=data.get("locationTitle"), - expiration_time=data["expirationTime"], + expires_at=_util.millis_to_datetime(data["expirationTime"]), is_expired=bool(data.get("stopReason")), ) @@ -96,7 +96,7 @@ class LiveLocationAttachment(LocationAttachment): if target.get("coordinate") else None, name=data["title_with_entities"]["text"], - expiration_time=target.get("expiration_time"), + expires_at=_util.millis_to_datetime(target.get("expiration_time")), is_expired=target.get("is_expired"), ) media = data.get("media") From 24cf4047b7b201e809d5a25ef76eb09412bd67ed Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 8 Sep 2019 15:04:53 +0200 Subject: [PATCH 08/10] Convert durations to timedeltas On: - AudioAttachment.duration - VideoAttachment.duration - Client.onCallEnded call_duration argument - Client.muteThread mute_time argument --- fbchat/_client.py | 19 +++++++++++++------ fbchat/_file.py | 11 ++++++----- fbchat/_util.py | 18 ++++++++++++++++++ 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/fbchat/_client.py b/fbchat/_client.py index e977679..62ad30c 100644 --- a/fbchat/_client.py +++ b/fbchat/_client.py @@ -1,3 +1,4 @@ +import datetime import time import json import requests @@ -2109,15 +2110,19 @@ class Client: j = self._payload_post("/ajax/mercury/delete_messages.php?dpr=1", data) return True - def muteThread(self, mute_time=-1, thread_id=None): + def muteThread(self, mute_time=None, thread_id=None): """Mute thread. Args: - mute_time: Mute time in seconds, leave blank to mute forever + mute_time (datetime.timedelta): Time to mute, use ``None`` to mute forever thread_id: User/Group ID to mute. See :ref:`intro_threads` """ thread_id, thread_type = self._getThread(thread_id, None) - data = {"mute_settings": str(mute_time), "thread_fbid": thread_id} + if mute_time is None: + mute_settings = -1 + else: + mute_settings = _util.timedelta_to_seconds(mute_time) + data = {"mute_settings": str(mute_settings), "thread_fbid": thread_id} j = self._payload_post("/ajax/mercury/change_mute_thread.php?dpr=1", data) def unmuteThread(self, thread_id=None): @@ -2126,7 +2131,7 @@ class Client: Args: thread_id: User/Group ID to unmute. See :ref:`intro_threads` """ - return self.muteThread(0, thread_id) + return self.muteThread(datetime.timedelta(0), thread_id) def muteThreadReactions(self, mute=True, thread_id=None): """Mute thread reactions. @@ -2468,7 +2473,9 @@ class Client: elif delta_type == "rtc_call_log": thread_id, thread_type = getThreadIdAndThreadType(metadata) call_status = delta["untypedData"]["event"] - call_duration = int(delta["untypedData"]["call_duration"]) + 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.onCallStarted( @@ -3595,7 +3602,7 @@ class Client: 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: Call duration in seconds + call_duration (datetime.timedelta): Call duration thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` ts: A timestamp of the action diff --git a/fbchat/_file.py b/fbchat/_file.py index db3ebf1..b475663 100644 --- a/fbchat/_file.py +++ b/fbchat/_file.py @@ -1,4 +1,5 @@ import attr +from . import _util from ._attachment import Attachment @@ -36,7 +37,7 @@ class AudioAttachment(Attachment): filename = attr.ib(None) #: URL of the audio file url = attr.ib(None) - #: Duration of the audio clip in milliseconds + #: Duration of the audio clip as a timedelta duration = attr.ib(None) #: Audio type audio_type = attr.ib(None) @@ -49,7 +50,7 @@ class AudioAttachment(Attachment): return cls( filename=data.get("filename"), url=data.get("playable_url"), - duration=data.get("playable_duration_in_ms"), + duration=_util.millis_to_timedelta(data.get("playable_duration_in_ms")), audio_type=data.get("audio_type"), ) @@ -175,7 +176,7 @@ class VideoAttachment(Attachment): width = attr.ib(None) #: Height of original video height = attr.ib(None) - #: Length of video in milliseconds + #: Length of video as a timedelta duration = attr.ib(None) #: URL to very compressed preview video preview_url = attr.ib(None) @@ -243,7 +244,7 @@ class VideoAttachment(Attachment): return cls( width=data.get("original_dimensions", {}).get("width"), height=data.get("original_dimensions", {}).get("height"), - duration=data.get("playable_duration_in_ms"), + duration=_util.millis_to_timedelta(data.get("playable_duration_in_ms")), preview_url=data.get("playable_url"), small_image=data.get("chat_image"), medium_image=data.get("inbox_image"), @@ -255,7 +256,7 @@ class VideoAttachment(Attachment): def _from_subattachment(cls, data): media = data["media"] return cls( - duration=media.get("playable_duration_in_ms"), + duration=_util.millis_to_timedelta(media.get("playable_duration_in_ms")), preview_url=media.get("playable_url"), medium_image=media.get("image"), uid=data["target"].get("video_id"), diff --git a/fbchat/_util.py b/fbchat/_util.py index 09ad6f1..61dec26 100644 --- a/fbchat/_util.py +++ b/fbchat/_util.py @@ -269,3 +269,21 @@ def datetime_to_millis(dt): The returned milliseconds will be rounded to the nearest whole number. """ return round(dt.timestamp() * 1000) + + +def seconds_to_timedelta(seconds): + """Convert seconds to a timedelta.""" + return datetime.timedelta(seconds=seconds) + + +def millis_to_timedelta(milliseconds): + """Convert a duration (in milliseconds) to a timedelta object.""" + return datetime.timedelta(milliseconds=milliseconds) + + +def timedelta_to_seconds(td): + """Convert a timedelta to seconds. + + The returned seconds will be rounded to the nearest whole number. + """ + return round(td.total_seconds()) From 47bdb849576b5330dd340b5c4dfd3835dcae32b5 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 8 Sep 2019 15:05:34 +0200 Subject: [PATCH 09/10] Make seen_ts a datetime, and rename to seen_at in onX methods - onMessageSeen - onMarkedSeen --- fbchat/_client.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/fbchat/_client.py b/fbchat/_client.py index 62ad30c..bd4d953 100644 --- a/fbchat/_client.py +++ b/fbchat/_client.py @@ -2410,14 +2410,14 @@ class Client: # Message seen elif delta_class == "ReadReceipt": seen_by = str(delta.get("actorFbId") or delta["threadKey"]["otherUserFbId"]) - seen_ts = int(delta["actionTimestampMs"]) + seen_at = _util.millis_to_datetime(int(delta["actionTimestampMs"])) delivered_ts = int(delta["watermarkTimestampMs"]) thread_id, thread_type = getThreadIdAndThreadType(delta) self.onMessageSeen( seen_by=seen_by, thread_id=thread_id, thread_type=thread_type, - seen_ts=seen_ts, + seen_at=seen_at, ts=delivered_ts, metadata=metadata, msg=m, @@ -2425,8 +2425,8 @@ class Client: # Messages marked as seen elif delta_class == "MarkRead": - seen_ts = int( - delta.get("actionTimestampMs") or delta.get("actionTimestamp") + seen_at = _util.millis_to_datetime( + int(delta.get("actionTimestampMs") or delta.get("actionTimestamp")) ) delivered_ts = int( delta.get("watermarkTimestampMs") or delta.get("watermarkTimestamp") @@ -2441,7 +2441,7 @@ class Client: # thread_id, thread_type = getThreadIdAndThreadType(delta) self.onMarkedSeen( - threads=threads, seen_ts=seen_ts, ts=delivered_ts, metadata=delta, msg=m + threads=threads, seen_at=seen_at, ts=delivered_ts, metadata=delta, msg=m ) # Game played @@ -3231,7 +3231,7 @@ class Client: seen_by=None, thread_id=None, thread_type=ThreadType.USER, - seen_ts=None, + seen_at=None, ts=None, metadata=None, msg=None, @@ -3242,14 +3242,14 @@ class Client: seen_by: The ID of the person who marked the message as seen thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - seen_ts: A timestamp of when the person saw the message + seen_at (datetime.datetime): When the person saw the message ts: A timestamp of the action metadata: Extra metadata about the action msg: A full set of the data received """ log.info( - "Messages seen by {} in {} ({}) at {}s".format( - seen_by, thread_id, thread_type.name, seen_ts / 1000 + "Messages seen by {} in {} ({}) at {}".format( + seen_by, thread_id, thread_type.name, seen_at ) ) @@ -3281,21 +3281,21 @@ class Client: ) def onMarkedSeen( - self, threads=None, seen_ts=None, ts=None, metadata=None, msg=None + self, threads=None, seen_at=None, ts=None, metadata=None, msg=None ): """Called when the client is listening, and the client has successfully marked threads as seen. Args: threads: The threads that were marked author_id: The ID of the person who changed the emoji - seen_ts: A timestamp of when the threads were seen + seen_at (datetime.datetime): When the threads were seen ts: A timestamp of the action metadata: Extra metadata about the action msg: A full set of the data received """ log.info( - "Marked messages as seen in threads {} at {}s".format( - [(x[0], x[1].name) for x in threads], seen_ts / 1000 + "Marked messages as seen in threads {} at {}".format( + [(x[0], x[1].name) for x in threads], seen_at ) ) From 72ab8695f16da0672761a91f50c9c050e14bd41d Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 8 Sep 2019 13:34:33 +0200 Subject: [PATCH 10/10] Make `ts` a datetime, and rename to `at` in all onX methods --- fbchat/_client.py | 223 +++++++++++++++++++++++----------------------- 1 file changed, 113 insertions(+), 110 deletions(-) diff --git a/fbchat/_client.py b/fbchat/_client.py index bd4d953..bf6308e 100644 --- a/fbchat/_client.py +++ b/fbchat/_client.py @@ -2230,7 +2230,7 @@ class Client: if metadata: mid = metadata["messageId"] author_id = str(metadata["actorFbId"]) - ts = int(metadata.get("timestamp")) + at = _util.millis_to_datetime(int(metadata.get("timestamp"))) # Added participants if "addedParticipants" in delta: @@ -2241,7 +2241,7 @@ class Client: added_ids=added_ids, author_id=author_id, thread_id=thread_id, - ts=ts, + at=at, msg=m, ) @@ -2254,7 +2254,7 @@ class Client: removed_id=removed_id, author_id=author_id, thread_id=thread_id, - ts=ts, + at=at, msg=m, ) @@ -2268,7 +2268,7 @@ class Client: new_color=new_color, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, metadata=metadata, msg=m, ) @@ -2283,7 +2283,7 @@ class Client: new_emoji=new_emoji, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, metadata=metadata, msg=m, ) @@ -2298,7 +2298,7 @@ class Client: new_title=new_title, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, metadata=metadata, msg=m, ) @@ -2313,7 +2313,7 @@ class Client: fetch_info = self._forcedFetch(thread_id, mid) fetch_data = fetch_info["message"] author_id = fetch_data["message_sender"]["id"] - ts = fetch_data["timestamp_precise"] + at = _util.millis_to_datetime(int(fetch_data["timestamp_precise"])) if fetch_data.get("__typename") == "ThreadImageMessage": # Thread image change image_metadata = fetch_data.get("image_with_metadata") @@ -2328,7 +2328,7 @@ class Client: new_image=image_id, thread_id=thread_id, thread_type=ThreadType.GROUP, - ts=ts, + at=at, msg=m, ) @@ -2344,7 +2344,7 @@ class Client: new_nickname=new_nickname, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, metadata=metadata, msg=m, ) @@ -2361,7 +2361,7 @@ class Client: author_id=author_id, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, msg=m, ) elif admin_event == "remove_admin": @@ -2371,7 +2371,7 @@ class Client: author_id=author_id, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, msg=m, ) @@ -2385,7 +2385,7 @@ class Client: author_id=author_id, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, msg=m, ) @@ -2395,14 +2395,14 @@ class Client: delivered_for = str( delta.get("actorFbId") or delta["threadKey"]["otherUserFbId"] ) - ts = int(delta["deliveredWatermarkTimestampMs"]) + at = _util.millis_to_datetime(int(delta["deliveredWatermarkTimestampMs"])) thread_id, thread_type = getThreadIdAndThreadType(delta) self.onMessageDelivered( msg_ids=message_ids, delivered_for=delivered_for, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, metadata=metadata, msg=m, ) @@ -2411,14 +2411,14 @@ class Client: elif delta_class == "ReadReceipt": seen_by = str(delta.get("actorFbId") or delta["threadKey"]["otherUserFbId"]) seen_at = _util.millis_to_datetime(int(delta["actionTimestampMs"])) - delivered_ts = int(delta["watermarkTimestampMs"]) + at = _util.millis_to_datetime(int(delta["watermarkTimestampMs"])) thread_id, thread_type = getThreadIdAndThreadType(delta) self.onMessageSeen( seen_by=seen_by, thread_id=thread_id, thread_type=thread_type, seen_at=seen_at, - ts=delivered_ts, + at=at, metadata=metadata, msg=m, ) @@ -2428,9 +2428,10 @@ class Client: seen_at = _util.millis_to_datetime( int(delta.get("actionTimestampMs") or delta.get("actionTimestamp")) ) - delivered_ts = int( - delta.get("watermarkTimestampMs") or delta.get("watermarkTimestamp") + watermark_ts = delta.get("watermarkTimestampMs") or delta.get( + "watermarkTimestamp" ) + at = _util.millis_to_datetime(int(watermark_ts)) threads = [] if "folders" not in delta: @@ -2441,7 +2442,7 @@ class Client: # thread_id, thread_type = getThreadIdAndThreadType(delta) self.onMarkedSeen( - threads=threads, seen_at=seen_at, ts=delivered_ts, metadata=delta, msg=m + threads=threads, seen_at=seen_at, at=at, metadata=delta, msg=m ) # Game played @@ -2464,7 +2465,7 @@ class Client: leaderboard=leaderboard, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, metadata=metadata, msg=m, ) @@ -2484,7 +2485,7 @@ class Client: is_video_call=is_video_call, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, metadata=metadata, msg=m, ) @@ -2496,7 +2497,7 @@ class Client: call_duration=call_duration, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, metadata=metadata, msg=m, ) @@ -2511,7 +2512,7 @@ class Client: is_video_call=is_video_call, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, metadata=metadata, msg=m, ) @@ -2530,7 +2531,7 @@ class Client: author_id=author_id, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, metadata=metadata, msg=m, ) @@ -2546,7 +2547,7 @@ class Client: author_id=author_id, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, metadata=metadata, msg=m, ) @@ -2560,7 +2561,7 @@ class Client: author_id=author_id, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, metadata=metadata, msg=m, ) @@ -2573,7 +2574,7 @@ class Client: plan=Plan._from_pull(delta["untypedData"]), thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, metadata=metadata, msg=m, ) @@ -2587,7 +2588,7 @@ class Client: author_id=author_id, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, metadata=metadata, msg=m, ) @@ -2601,7 +2602,7 @@ class Client: author_id=author_id, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, metadata=metadata, msg=m, ) @@ -2617,7 +2618,7 @@ class Client: author_id=author_id, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, metadata=metadata, msg=m, ) @@ -2625,7 +2626,7 @@ class Client: # Client payload (that weird numbers) elif delta_class == "ClientPayload": payload = json.loads("".join(chr(z) for z in delta["payload"])) - ts = m.get("ofd_ts") + at = _util.millis_to_datetime(m.get("ofd_ts")) for d in payload.get("deltas", []): # Message reaction @@ -2645,7 +2646,7 @@ class Client: author_id=author_id, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, msg=m, ) else: @@ -2654,7 +2655,7 @@ class Client: author_id=author_id, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, msg=m, ) @@ -2671,7 +2672,7 @@ class Client: author_id=author_id, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, msg=m, ) else: @@ -2679,7 +2680,7 @@ class Client: author_id=author_id, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, msg=m, ) @@ -2697,7 +2698,7 @@ class Client: author_id=author_id, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, msg=m, ) @@ -2706,14 +2707,14 @@ class Client: i = d["deltaRecallMessageData"] thread_id, thread_type = getThreadIdAndThreadType(i) mid = i["messageID"] - ts = i["deletionTimestamp"] + at = _util.millis_to_datetime(i["deletionTimestamp"]) author_id = str(i["senderID"]) self.onMessageUnsent( mid=mid, author_id=author_id, thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, msg=m, ) @@ -2731,7 +2732,7 @@ class Client: message_object=message, thread_id=thread_id, thread_type=thread_type, - ts=_util.datetime_to_millis(message.created_at), + at=message.created_at, metadata=metadata, msg=m, ) @@ -2748,11 +2749,11 @@ class Client: mid=mid, tags=metadata.get("tags"), author=author_id, - created_at=_util.millis_to_datetime(ts), + created_at=at, ), thread_id=thread_id, thread_type=thread_type, - ts=ts, + at=at, metadata=metadata, msg=m, ) @@ -2829,7 +2830,9 @@ class Client: # Happens on every login elif mtype == "qprimer": - self.onQprimer(ts=m.get("made"), msg=m) + self.onQprimer( + at=_util.millis_to_datetime(int(m.get("made"))), msg=m + ) # Is sent before any other message elif mtype == "deltaflow": @@ -2997,7 +3000,7 @@ class Client: message_object=None, thread_id=None, thread_type=ThreadType.USER, - ts=None, + at=None, metadata=None, msg=None, ): @@ -3010,7 +3013,7 @@ class Client: message_object (Message): The message (As a `Message` object) thread_id: Thread ID that the message was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the message was sent to. See :ref:`intro_threads` - ts: The timestamp of the message + at (datetime.datetime): When the message was sent metadata: Extra metadata about the message msg: A full set of the data received """ @@ -3023,7 +3026,7 @@ class Client: new_color=None, thread_id=None, thread_type=ThreadType.USER, - ts=None, + at=None, metadata=None, msg=None, ): @@ -3035,7 +3038,7 @@ class Client: new_color (ThreadColor): The new color thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed metadata: Extra metadata about the action msg: A full set of the data received """ @@ -3052,7 +3055,7 @@ class Client: new_emoji=None, thread_id=None, thread_type=ThreadType.USER, - ts=None, + at=None, metadata=None, msg=None, ): @@ -3064,7 +3067,7 @@ class Client: new_emoji: The new emoji thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed metadata: Extra metadata about the action msg: A full set of the data received """ @@ -3081,7 +3084,7 @@ class Client: new_title=None, thread_id=None, thread_type=ThreadType.USER, - ts=None, + at=None, metadata=None, msg=None, ): @@ -3093,7 +3096,7 @@ class Client: new_title: The new title thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed metadata: Extra metadata about the action msg: A full set of the data received """ @@ -3110,7 +3113,7 @@ class Client: new_image=None, thread_id=None, thread_type=ThreadType.GROUP, - ts=None, + at=None, msg=None, ): """Called when the client is listening, and somebody changes a thread's image. @@ -3121,7 +3124,7 @@ class Client: new_image: The ID of the new image thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed msg: A full set of the data received """ log.info("{} changed thread image in {}".format(author_id, thread_id)) @@ -3134,7 +3137,7 @@ class Client: new_nickname=None, thread_id=None, thread_type=ThreadType.USER, - ts=None, + at=None, metadata=None, msg=None, ): @@ -3147,7 +3150,7 @@ class Client: new_nickname: The new nickname thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed metadata: Extra metadata about the action msg: A full set of the data received """ @@ -3164,7 +3167,7 @@ class Client: author_id=None, thread_id=None, thread_type=ThreadType.GROUP, - ts=None, + at=None, msg=None, ): """Called when the client is listening, and somebody adds an admin to a group. @@ -3174,7 +3177,7 @@ class Client: added_id: The ID of the admin who got added author_id: The ID of the person who added the admins thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed msg: A full set of the data received """ log.info("{} added admin: {} in {}".format(author_id, added_id, thread_id)) @@ -3186,7 +3189,7 @@ class Client: author_id=None, thread_id=None, thread_type=ThreadType.GROUP, - ts=None, + at=None, msg=None, ): """Called when the client is listening, and somebody is removed as an admin in a group. @@ -3196,7 +3199,7 @@ class Client: removed_id: The ID of the admin who got removed author_id: The ID of the person who removed the admins thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed msg: A full set of the data received """ log.info("{} removed admin: {} in {}".format(author_id, removed_id, thread_id)) @@ -3208,7 +3211,7 @@ class Client: author_id=None, thread_id=None, thread_type=ThreadType.GROUP, - ts=None, + at=None, msg=None, ): """Called when the client is listening, and somebody changes approval mode in a group. @@ -3218,7 +3221,7 @@ class Client: approval_mode: True if approval mode is activated author_id: The ID of the person who changed approval mode thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed msg: A full set of the data received """ if approval_mode: @@ -3232,7 +3235,7 @@ class Client: thread_id=None, thread_type=ThreadType.USER, seen_at=None, - ts=None, + at=None, metadata=None, msg=None, ): @@ -3243,7 +3246,7 @@ class Client: thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` seen_at (datetime.datetime): When the person saw the message - ts: A timestamp of the action + at (datetime.datetime): When the action was executed metadata: Extra metadata about the action msg: A full set of the data received """ @@ -3259,7 +3262,7 @@ class Client: delivered_for=None, thread_id=None, thread_type=ThreadType.USER, - ts=None, + at=None, metadata=None, msg=None, ): @@ -3270,18 +3273,18 @@ class Client: delivered_for: The person that marked the messages as delivered thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed metadata: Extra metadata about the action msg: A full set of the data received """ log.info( - "Messages {} delivered to {} in {} ({}) at {}s".format( - msg_ids, delivered_for, thread_id, thread_type.name, ts / 1000 + "Messages {} delivered to {} in {} ({}) at {}".format( + msg_ids, delivered_for, thread_id, thread_type.name, at ) ) def onMarkedSeen( - self, threads=None, seen_at=None, ts=None, metadata=None, msg=None + self, threads=None, seen_at=None, at=None, metadata=None, msg=None ): """Called when the client is listening, and the client has successfully marked threads as seen. @@ -3289,7 +3292,7 @@ class Client: threads: The threads that were marked author_id: The ID of the person who changed the emoji seen_at (datetime.datetime): When the threads were seen - ts: A timestamp of the action + at (datetime.datetime): When the action was executed metadata: Extra metadata about the action msg: A full set of the data received """ @@ -3305,7 +3308,7 @@ class Client: author_id=None, thread_id=None, thread_type=None, - ts=None, + at=None, msg=None, ): """Called when the client is listening, and someone unsends (deletes for everyone) a message. @@ -3315,12 +3318,12 @@ class Client: author_id: The ID of the person who unsent the message thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed msg: A full set of the data received """ log.info( - "{} unsent the message {} in {} ({}) at {}s".format( - author_id, repr(mid), thread_id, thread_type.name, ts / 1000 + "{} unsent the message {} in {} ({}) at {}".format( + author_id, repr(mid), thread_id, thread_type.name, at ) ) @@ -3330,7 +3333,7 @@ class Client: added_ids=None, author_id=None, thread_id=None, - ts=None, + at=None, msg=None, ): """Called when the client is listening, and somebody adds people to a group thread. @@ -3340,7 +3343,7 @@ class Client: added_ids: The IDs of the people who got added author_id: The ID of the person who added the people thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed msg: A full set of the data received """ log.info( @@ -3353,7 +3356,7 @@ class Client: removed_id=None, author_id=None, thread_id=None, - ts=None, + at=None, msg=None, ): """Called when the client is listening, and somebody removes a person from a group thread. @@ -3363,7 +3366,7 @@ class Client: removed_id: The ID of the person who got removed author_id: The ID of the person who removed the person thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed msg: A full set of the data received """ log.info("{} removed: {} in {}".format(author_id, removed_id, thread_id)) @@ -3414,7 +3417,7 @@ class Client: leaderboard=None, thread_id=None, thread_type=None, - ts=None, + at=None, metadata=None, msg=None, ): @@ -3429,7 +3432,7 @@ class Client: leaderboard: Actual leader board of the game in the thread thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed metadata: Extra metadata about the action msg: A full set of the data received """ @@ -3446,7 +3449,7 @@ class Client: author_id=None, thread_id=None, thread_type=None, - ts=None, + at=None, msg=None, ): """Called when the client is listening, and somebody reacts to a message. @@ -3458,7 +3461,7 @@ class Client: author_id: The ID of the person who reacted to the message thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed msg: A full set of the data received """ log.info( @@ -3473,7 +3476,7 @@ class Client: author_id=None, thread_id=None, thread_type=None, - ts=None, + at=None, msg=None, ): """Called when the client is listening, and somebody removes reaction from a message. @@ -3483,7 +3486,7 @@ class Client: author_id: The ID of the person who removed reaction thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed msg: A full set of the data received """ log.info( @@ -3493,7 +3496,7 @@ class Client: ) def onBlock( - self, author_id=None, thread_id=None, thread_type=None, ts=None, msg=None + self, author_id=None, thread_id=None, thread_type=None, at=None, msg=None ): """Called when the client is listening, and somebody blocks client. @@ -3501,7 +3504,7 @@ class Client: author_id: The ID of the person who blocked thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed msg: A full set of the data received """ log.info( @@ -3509,7 +3512,7 @@ class Client: ) def onUnblock( - self, author_id=None, thread_id=None, thread_type=None, ts=None, msg=None + self, author_id=None, thread_id=None, thread_type=None, at=None, msg=None ): """Called when the client is listening, and somebody blocks client. @@ -3517,7 +3520,7 @@ class Client: author_id: The ID of the person who unblocked thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed msg: A full set of the data received """ log.info( @@ -3531,7 +3534,7 @@ class Client: author_id=None, thread_id=None, thread_type=None, - ts=None, + at=None, msg=None, ): """Called when the client is listening and somebody sends live location info. @@ -3542,7 +3545,7 @@ class Client: author_id: The ID of the person who sent location info thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed msg: A full set of the data received """ log.info( @@ -3558,7 +3561,7 @@ class Client: is_video_call=None, thread_id=None, thread_type=None, - ts=None, + at=None, metadata=None, msg=None, ): @@ -3573,7 +3576,7 @@ class Client: is_video_call: True if it's video call thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed metadata: Extra metadata about the action msg: A full set of the data received """ @@ -3589,7 +3592,7 @@ class Client: call_duration=None, thread_id=None, thread_type=None, - ts=None, + at=None, metadata=None, msg=None, ): @@ -3605,7 +3608,7 @@ class Client: call_duration (datetime.timedelta): Call duration thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed metadata: Extra metadata about the action msg: A full set of the data received """ @@ -3620,7 +3623,7 @@ class Client: is_video_call=None, thread_id=None, thread_type=None, - ts=None, + at=None, metadata=None, msg=None, ): @@ -3632,7 +3635,7 @@ class Client: is_video_call: True if it's video call thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed metadata: Extra metadata about the action msg: A full set of the data received """ @@ -3647,7 +3650,7 @@ class Client: author_id=None, thread_id=None, thread_type=None, - ts=None, + at=None, metadata=None, msg=None, ): @@ -3659,7 +3662,7 @@ class Client: author_id: The ID of the person who created the poll thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed metadata: Extra metadata about the action msg: A full set of the data received """ @@ -3678,7 +3681,7 @@ class Client: author_id=None, thread_id=None, thread_type=None, - ts=None, + at=None, metadata=None, msg=None, ): @@ -3690,7 +3693,7 @@ class Client: author_id: The ID of the person who voted in the poll thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed metadata: Extra metadata about the action msg: A full set of the data received """ @@ -3707,7 +3710,7 @@ class Client: author_id=None, thread_id=None, thread_type=None, - ts=None, + at=None, metadata=None, msg=None, ): @@ -3719,7 +3722,7 @@ class Client: author_id: The ID of the person who created the plan thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed metadata: Extra metadata about the action msg: A full set of the data received """ @@ -3735,7 +3738,7 @@ class Client: plan=None, thread_id=None, thread_type=None, - ts=None, + at=None, metadata=None, msg=None, ): @@ -3746,7 +3749,7 @@ class Client: plan (Plan): Ended plan thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed metadata: Extra metadata about the action msg: A full set of the data received """ @@ -3761,7 +3764,7 @@ class Client: author_id=None, thread_id=None, thread_type=None, - ts=None, + at=None, metadata=None, msg=None, ): @@ -3773,7 +3776,7 @@ class Client: author_id: The ID of the person who edited the plan thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed metadata: Extra metadata about the action msg: A full set of the data received """ @@ -3790,7 +3793,7 @@ class Client: author_id=None, thread_id=None, thread_type=None, - ts=None, + at=None, metadata=None, msg=None, ): @@ -3802,7 +3805,7 @@ class Client: author_id: The ID of the person who deleted the plan thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed metadata: Extra metadata about the action msg: A full set of the data received """ @@ -3820,7 +3823,7 @@ class Client: author_id=None, thread_id=None, thread_type=None, - ts=None, + at=None, metadata=None, msg=None, ): @@ -3833,7 +3836,7 @@ class Client: author_id: The ID of the person who will participate in the plan or not thread_id: Thread ID that the action was sent to. See :ref:`intro_threads` thread_type (ThreadType): Type of thread that the action was sent to. See :ref:`intro_threads` - ts: A timestamp of the action + at (datetime.datetime): When the action was executed metadata: Extra metadata about the action msg: A full set of the data received """ @@ -3850,11 +3853,11 @@ class Client: ) ) - def onQprimer(self, ts=None, msg=None): + def onQprimer(self, at=None, msg=None): """Called when the client just started listening. Args: - ts: A timestamp of the action + at (datetime.datetime): When the action was executed msg: A full set of the data received """ pass