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.
This commit is contained in:
KnugiHK
2025-02-09 18:44:18 +08:00
parent aaeff80547
commit cfe04c8c0b
4 changed files with 89 additions and 18 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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));
}
</style>
<script>
function search(event) {
@@ -207,7 +211,25 @@
{% endif %}
<!--Actual messages-->
{% if msg.from_me == true %}
<div class="flex justify-end" id="{{ msg.key_id }}">
<div class="flex justify-end items-center group" id="{{ msg.key_id }}">
<div class="opacity-0 group-hover:opacity-100 transition-opacity duration-200 relative mr-2">
<div class="relative">
<div class="relative group/tooltip">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-[#8696a0] hover:text-[#54656f] cursor-pointer" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<use href="#info-icon"></use>
</svg>
<div class="absolute bottom-full info-box-tooltip mb-2 hidden group-hover/tooltip:block z-50">
<div class="bg-black text-white text-xs rounded py-1 px-2 whitespace-nowrap">
Delivered at {{msg.received_timestamp or 'unknown'}}
{% if msg.read_timestamp is not none %}
<br>Read at {{ msg.read_timestamp }}
{% endif %}
</div>
<div class="absolute top-full right-3 -mt-1 border-4 border-transparent border-t-black"></div>
</div>
</div>
</div>
</div>
<div class="bg-whatsapp-light rounded-lg p-2 max-w-[80%] shadow-sm">
{% if msg.reply is not none %}
<a href="#{{msg.reply}}" target="_self" class="no-base">
@@ -268,7 +290,7 @@
</div>
</div>
{% else %}
<div class="flex justify-start" id="{{ msg.key_id }}">
<div class="flex justify-start items-center group" id="{{ msg.key_id }}">
<div class="bg-white rounded-lg p-2 max-w-[80%] shadow-sm">
{% if msg.reply is not none %}
<a href="#{{msg.reply}}" target="_self" class="no-base">
@@ -335,6 +357,21 @@
<span class="flex-shrink-0">{{ msg.time }}</span>
</div>
</div>
<!-- <div class="opacity-0 group-hover:opacity-100 transition-opacity duration-200 relative ml-2">
<div class="relative">
<div class="relative group/tooltip">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-[#8696a0] hover:text-[#54656f] cursor-pointer" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<use href="#info-icon"></use>
</svg>
<div class="absolute bottom-full info-box-tooltip mb-2 hidden group-hover/tooltip:block z-50">
<div class="bg-black text-white text-xs rounded py-1 px-2 whitespace-nowrap">
Received at {{msg.received_timestamp or 'unknown'}}
</div>
<div class="absolute top-full right-3 ml-1 border-4 border-transparent border-t-black"></div>
</div>
</div>
</div>
</div> -->
</div>
{% endif %}
{% endfor %}
@@ -348,6 +385,12 @@
<br>
Portions of this page are reproduced from <a href="https://web.dev/articles/lazy-loading-video">work</a> created and <a href="https://developers.google.com/readme/policies">shared by Google</a> and used according to terms described in the <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache 2.0 License</a>.
</footer>
<svg style="display: none;">
<!-- Tooltip info icon -->
<symbol id="info-icon" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</symbol>
</svg>
</div>
</article>
</body>