From aaeff805479f973bd8631b2b8b845386626ff39e Mon Sep 17 00:00:00 2001 From: KnugiHK <24708955+KnugiHK@users.noreply.github.com> Date: Sun, 9 Feb 2025 18:37:05 +0800 Subject: [PATCH 01/36] Remove TODO flag as it is fixed already --- Whatsapp_Chat_Exporter/ios_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Whatsapp_Chat_Exporter/ios_handler.py b/Whatsapp_Chat_Exporter/ios_handler.py index 97b0c88..2724392 100644 --- a/Whatsapp_Chat_Exporter/ios_handler.py +++ b/Whatsapp_Chat_Exporter/ios_handler.py @@ -149,7 +149,7 @@ def messages(db, data, media_folder, timezone_offset, filter_date, filter_chat, message = Message( from_me=content["ZISFROMME"], timestamp=ts, - time=ts, # TODO: Could be bug + time=ts, key_id=content["ZSTANZAID"][:17], timezone_offset=timezone_offset if timezone_offset else CURRENT_TZ_OFFSET, message_type=content["ZMESSAGETYPE"] From cfe04c8c0bd663040aea3e92cbedf78467523c53 Mon Sep 17 00:00:00 2001 From: KnugiHK <24708955+KnugiHK@users.noreply.github.com> Date: Sun, 9 Feb 2025 18:44:18 +0800 Subject: [PATCH 02/36] Display the metadata from the messages sent by "me" (#69) For now, only the time for "delivered" (android & ios) and "read" (android only) is support. --- Whatsapp_Chat_Exporter/android_handler.py | 21 +++++----- Whatsapp_Chat_Exporter/data_model.py | 32 +++++++++++++-- Whatsapp_Chat_Exporter/ios_handler.py | 7 +++- Whatsapp_Chat_Exporter/whatsapp_new.html | 47 ++++++++++++++++++++++- 4 files changed, 89 insertions(+), 18 deletions(-) diff --git a/Whatsapp_Chat_Exporter/android_handler.py b/Whatsapp_Chat_Exporter/android_handler.py index 0713232..0948fcd 100644 --- a/Whatsapp_Chat_Exporter/android_handler.py +++ b/Whatsapp_Chat_Exporter/android_handler.py @@ -230,11 +230,8 @@ def messages(db, data, media_folder, timezone_offset, filter_date, filter_chat, jid_old.raw_string as old_jid, jid_new.raw_string as new_jid, jid_global.type as jid_type, - group_concat(receipt_user.receipt_timestamp) as receipt_timestamp, - group_concat(messages.received_timestamp) as received_timestamp, - group_concat(receipt_user.read_timestamp) as read_timestamp, - group_concat(receipt_user.played_timestamp) as played_timestamp, - group_concat(messages.read_device_timestamp) as read_device_timestamp + COALESCE(receipt_user.receipt_timestamp, messages.received_timestamp) as received_timestamp, + COALESCE(receipt_user.read_timestamp, receipt_user.played_timestamp, messages.read_device_timestamp) as read_timestamp FROM messages LEFT JOIN messages_quotes ON messages.quoted_row_id = messages_quotes._id @@ -290,10 +287,8 @@ def messages(db, data, media_folder, timezone_offset, filter_date, filter_chat, jid_old.raw_string as old_jid, jid_new.raw_string as new_jid, jid_global.type as jid_type, - group_concat(receipt_user.receipt_timestamp) as receipt_timestamp, - group_concat(message.received_timestamp) as received_timestamp, - group_concat(receipt_user.read_timestamp) as read_timestamp, - group_concat(receipt_user.played_timestamp) as played_timestamp + COALESCE(receipt_user.receipt_timestamp, message.received_timestamp) as received_timestamp, + COALESCE(receipt_user.read_timestamp, receipt_user.played_timestamp) as read_timestamp FROM message LEFT JOIN message_quoted ON message_quoted.message_row_id = message._id @@ -361,7 +356,9 @@ def messages(db, data, media_folder, timezone_offset, filter_date, filter_chat, time=content["timestamp"], key_id=content["key_id"], timezone_offset=timezone_offset if timezone_offset else CURRENT_TZ_OFFSET, - message_type=content["media_wa_type"] + message_type=content["media_wa_type"], + received_timestamp=content["received_timestamp"], + read_timestamp=content["read_timestamp"] ) if isinstance(content["data"], bytes): message.data = ("The message is binary data and its base64 is " @@ -736,7 +733,9 @@ def calls(db, data, timezone_offset, filter_chat): timestamp=content["timestamp"], time=content["timestamp"], key_id=content["call_id"], - timezone_offset=timezone_offset if timezone_offset else CURRENT_TZ_OFFSET + timezone_offset=timezone_offset if timezone_offset else CURRENT_TZ_OFFSET, + received_timestamp=None, # TODO: Add timestamp + read_timestamp=None # TODO: Add timestamp ) _jid = content["raw_string"] name = data[_jid].name if _jid in data else content["chat_subject"] or None diff --git a/Whatsapp_Chat_Exporter/data_model.py b/Whatsapp_Chat_Exporter/data_model.py index f08acca..ee4d066 100644 --- a/Whatsapp_Chat_Exporter/data_model.py +++ b/Whatsapp_Chat_Exporter/data_model.py @@ -5,6 +5,18 @@ from datetime import datetime, tzinfo, timedelta from typing import Union +class Timing(): + def __init__(self, timezone_offset: Union[int, None]): + self.timezone_offset = timezone_offset + + def format_timestamp(self, timestamp, format): + if timestamp: + timestamp = timestamp / 1000 if timestamp > 9999999999 else timestamp + return datetime.fromtimestamp(timestamp, TimeZone(self.timezone_offset)).strftime(format) + else: + return None + + class TimeZone(tzinfo): def __init__(self, offset): self.offset = offset @@ -65,11 +77,23 @@ class ChatStore(): class Message(): - def __init__(self, from_me: Union[bool,int], timestamp: int, time: Union[int,float,str], key_id: int, timezone_offset: int = 0, message_type: int = None): + def __init__( + self, + *, + from_me: Union[bool,int], + timestamp: int, + time: Union[int,float,str], + key_id: int, + received_timestamp: int, + read_timestamp: int, + timezone_offset: int = 0, + message_type: int = None + ): self.from_me = bool(from_me) self.timestamp = timestamp / 1000 if timestamp > 9999999999 else timestamp + timing = Timing(timezone_offset) if isinstance(time, int) or isinstance(time, float): - self.time = datetime.fromtimestamp(self.timestamp, TimeZone(timezone_offset)).strftime("%H:%M") + self.time = timing.format_timestamp(self.timestamp, "%H:%M") elif isinstance(time, str): self.time = time else: @@ -81,7 +105,9 @@ class Message(): self.sender = None self.safe = False self.mime = None - self.message_type = message_type + self.message_type = message_type, + self.received_timestamp = timing.format_timestamp(received_timestamp, "%Y/%m/%d %H:%M") + self.read_timestamp = timing.format_timestamp(read_timestamp, "%Y/%m/%d %H:%M") # Extra self.reply = None self.quoted_data = None diff --git a/Whatsapp_Chat_Exporter/ios_handler.py b/Whatsapp_Chat_Exporter/ios_handler.py index 2724392..1c72072 100644 --- a/Whatsapp_Chat_Exporter/ios_handler.py +++ b/Whatsapp_Chat_Exporter/ios_handler.py @@ -114,7 +114,8 @@ def messages(db, data, media_folder, timezone_offset, filter_date, filter_chat, ZWAGROUPMEMBER.ZMEMBERJID, ZMETADATA, ZSTANZAID, - ZGROUPINFO + ZGROUPINFO, + ZSENTDATE FROM ZWAMESSAGE LEFT JOIN ZWAGROUPMEMBER ON ZWAMESSAGE.ZGROUPMEMBER = ZWAGROUPMEMBER.Z_PK @@ -152,7 +153,9 @@ def messages(db, data, media_folder, timezone_offset, filter_date, filter_chat, time=ts, key_id=content["ZSTANZAID"][:17], timezone_offset=timezone_offset if timezone_offset else CURRENT_TZ_OFFSET, - message_type=content["ZMESSAGETYPE"] + message_type=content["ZMESSAGETYPE"], + received_timestamp=APPLE_TIME + content["ZSENTDATE"] if content["ZSENTDATE"] else None, + read_timestamp=None # TODO: Add timestamp ) invalid = False if is_group_message and content["ZISFROMME"] == 0: diff --git a/Whatsapp_Chat_Exporter/whatsapp_new.html b/Whatsapp_Chat_Exporter/whatsapp_new.html index 7502a7b..2aa2b7e 100644 --- a/Whatsapp_Chat_Exporter/whatsapp_new.html +++ b/Whatsapp_Chat_Exporter/whatsapp_new.html @@ -123,6 +123,10 @@ .reply-box:active { background-color:rgb(200 202 205 / var(--tw-bg-opacity, 1)); } + .info-box-tooltip { + --tw-translate-x: -50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + }