mirror of
https://github.com/KnugiHK/WhatsApp-Chat-Exporter.git
synced 2026-02-10 19:22:24 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user