mirror of
https://github.com/LC044/WeChatMsg
synced 2025-02-22 02:22:17 +08:00
支持导出引用消息类型
This commit is contained in:
parent
3a5131b39c
commit
cdf8a59ae2
@ -38,11 +38,11 @@ def decompress_CompressContent(data):
|
|||||||
if data is None or not isinstance(data, bytes):
|
if data is None or not isinstance(data, bytes):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
dst = lz4.block.decompress(data, uncompressed_size=len(data) << 10)
|
dst = lz4.block.decompress(data, uncompressed_size=len(data) << 10)
|
||||||
decoded_string = dst.decode().replace('\x00', '') # Remove any null characters
|
decoded_string = dst.decode().replace('\x00', '') # Remove any null characters
|
||||||
except lz4.block.LZ4BlockError:
|
except lz4.block.LZ4BlockError:
|
||||||
print("Decompression failed: potentially corrupt input or insufficient buffer size.")
|
print("Decompression failed: potentially corrupt input or insufficient buffer size.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# 处理 HTML 转义字符串如 &gt; 等。可能会递归嵌套,我们只考虑原会话和第一级引用会话,不考虑更深的引用,故只执行两遍。
|
# 处理 HTML 转义字符串如 &gt; 等。可能会递归嵌套,我们只考虑原会话和第一级引用会话,不考虑更深的引用,故只执行两遍。
|
||||||
@ -384,9 +384,14 @@ if __name__ == '__main__':
|
|||||||
db_path = "./Msg/MSG.db"
|
db_path = "./Msg/MSG.db"
|
||||||
msg = Msg()
|
msg = Msg()
|
||||||
msg.init_database()
|
msg.init_database()
|
||||||
result = msg.get_message_by_num('wxid_0o18ef858vnu22', 9999999)
|
result = msg.get_message_by_num('wxid_vtz9jk9ulzjt22', 9999999)
|
||||||
print(result)
|
print(result)
|
||||||
result = msg.get_messages_by_type('wxid_0o18ef858vnu22',43)
|
result = msg.get_messages_by_type('wxid_vtz9jk9ulzjt22',49)
|
||||||
bytes_ = result[-1][-1]
|
for r in result:
|
||||||
print(bytes_)
|
type_ = r[2]
|
||||||
print(bytes_)
|
sub_type = r[3]
|
||||||
|
if type_ == 49 and sub_type == 57:
|
||||||
|
# print(r)
|
||||||
|
# print(r[-1])
|
||||||
|
print(decompress_CompressContent(r[-1]))
|
||||||
|
break
|
@ -12,6 +12,7 @@ from ..person_pc import MePC
|
|||||||
from ..util import path
|
from ..util import path
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
|
from ..util.compress_content import parser_reply
|
||||||
from ..util.emoji import get_emoji
|
from ..util.emoji import get_emoji
|
||||||
|
|
||||||
os.makedirs('./data/聊天记录', exist_ok=True)
|
os.makedirs('./data/聊天记录', exist_ok=True)
|
||||||
@ -179,7 +180,7 @@ class ChildThread(QThread):
|
|||||||
if emoji_text in emoji:
|
if emoji_text in emoji:
|
||||||
str_content = str_content.replace(emoji_text, emoji[emoji_text])
|
str_content = str_content.replace(emoji_text, emoji[emoji_text])
|
||||||
doc.write(
|
doc.write(
|
||||||
f'''{{ type:{type_}, text: '{str_content}',is_send:{is_send},avatar_path:'{avatar}'}},'''
|
f'''{{ type:{1}, text: '{str_content}',is_send:{is_send},avatar_path:'{avatar}'}},'''
|
||||||
)
|
)
|
||||||
elif self.output_type == Output.TXT:
|
elif self.output_type == Output.TXT:
|
||||||
name = '你' if is_send else self.contact.remark
|
name = '你' if is_send else self.contact.remark
|
||||||
@ -248,8 +249,33 @@ class ChildThread(QThread):
|
|||||||
def retract_message(self, doc, isSend, content, status):
|
def retract_message(self, doc, isSend, content, status):
|
||||||
return
|
return
|
||||||
|
|
||||||
def reply(self, doc, isSend, content, status):
|
def refermsg(self, doc,message):
|
||||||
return
|
"""
|
||||||
|
处理回复消息
|
||||||
|
@param doc:
|
||||||
|
@param message:
|
||||||
|
@return:
|
||||||
|
"""
|
||||||
|
type_ = message[2]
|
||||||
|
str_content = message[7]
|
||||||
|
str_time = message[8]
|
||||||
|
is_send = message[4]
|
||||||
|
avatar = 'myhead.png' if is_send else 'tahead.png'
|
||||||
|
content = parser_reply(message[11])
|
||||||
|
refer_msg = content.get('refer')
|
||||||
|
if self.output_type == Output.HTML:
|
||||||
|
doc.write(
|
||||||
|
f'''{{ type:1, text: '{content.get('title')}',is_send:{is_send},avatar_path:'{avatar}'}},'''
|
||||||
|
)
|
||||||
|
|
||||||
|
doc.write(
|
||||||
|
f'''{{ type:{49},sub_type:{content.get('type')}, text: '{refer_msg.get('displayname')}:{refer_msg.get('content')}',is_send:{is_send},avatar_path:''}},'''
|
||||||
|
)
|
||||||
|
elif self.output_type==Output.TXT:
|
||||||
|
name = '你' if is_send else self.contact.remark
|
||||||
|
doc.write(
|
||||||
|
f'''{str_time} {name}\n{content.get('title')}\n引用:{refer_msg.get('displayname')}:{refer_msg.get('content')}\n\n'''
|
||||||
|
)
|
||||||
|
|
||||||
def system_msg(self, doc, message):
|
def system_msg(self, doc, message):
|
||||||
str_content = message[7]
|
str_content = message[7]
|
||||||
@ -349,6 +375,7 @@ class ChildThread(QThread):
|
|||||||
total_steps = len(messages)
|
total_steps = len(messages)
|
||||||
for index, message in enumerate(messages):
|
for index, message in enumerate(messages):
|
||||||
type_ = message[2]
|
type_ = message[2]
|
||||||
|
sub_type = message[3]
|
||||||
self.progressSignal.emit(int((index + 1) / total_steps * 100))
|
self.progressSignal.emit(int((index + 1) / total_steps * 100))
|
||||||
if type_ == 1 and self.message_types.get(type_):
|
if type_ == 1 and self.message_types.get(type_):
|
||||||
self.text(f, message)
|
self.text(f, message)
|
||||||
@ -360,6 +387,8 @@ class ChildThread(QThread):
|
|||||||
self.emoji(f, message)
|
self.emoji(f, message)
|
||||||
elif type_ == 10000 and self.message_types.get(type_):
|
elif type_ == 10000 and self.message_types.get(type_):
|
||||||
self.system_msg(f, message)
|
self.system_msg(f, message)
|
||||||
|
elif type_ == 49 and sub_type == 57:
|
||||||
|
self.refermsg(f,message)
|
||||||
f.write(html_end)
|
f.write(html_end)
|
||||||
f.close()
|
f.close()
|
||||||
self.okSignal.emit(1)
|
self.okSignal.emit(1)
|
||||||
@ -373,6 +402,7 @@ class ChildThread(QThread):
|
|||||||
with open(filename, mode='w', newline='', encoding='utf-8') as f:
|
with open(filename, mode='w', newline='', encoding='utf-8') as f:
|
||||||
for index, message in enumerate(messages):
|
for index, message in enumerate(messages):
|
||||||
type_ = message[2]
|
type_ = message[2]
|
||||||
|
sub_type = message[3]
|
||||||
self.progressSignal.emit(int((index + 1) / total_steps * 100))
|
self.progressSignal.emit(int((index + 1) / total_steps * 100))
|
||||||
if type_ == 1 and self.message_types.get(type_):
|
if type_ == 1 and self.message_types.get(type_):
|
||||||
self.text(f, message)
|
self.text(f, message)
|
||||||
@ -384,6 +414,8 @@ class ChildThread(QThread):
|
|||||||
self.emoji(f, message)
|
self.emoji(f, message)
|
||||||
elif type_ == 10000 and self.message_types.get(type_):
|
elif type_ == 10000 and self.message_types.get(type_):
|
||||||
self.system_msg(f, message)
|
self.system_msg(f, message)
|
||||||
|
elif type_ == 49 and sub_type == 57:
|
||||||
|
self.refermsg(f, message)
|
||||||
self.okSignal.emit(1)
|
self.okSignal.emit(1)
|
||||||
def run(self):
|
def run(self):
|
||||||
if self.output_type == Output.DOCX:
|
if self.output_type == Output.DOCX:
|
||||||
@ -576,6 +608,22 @@ body{
|
|||||||
word-wrap:break-word;
|
word-wrap:break-word;
|
||||||
word-break:normal;
|
word-break:normal;
|
||||||
}
|
}
|
||||||
|
.chat-refer{
|
||||||
|
max-width: 400px;
|
||||||
|
padding: 3px;
|
||||||
|
border-radius: 5px;
|
||||||
|
position: relative;
|
||||||
|
color: #000;
|
||||||
|
background-color: #e8e8e8;
|
||||||
|
word-wrap:break-word;
|
||||||
|
word-break:normal;
|
||||||
|
}
|
||||||
|
.chat-refer-right{
|
||||||
|
margin-right:55px;
|
||||||
|
}
|
||||||
|
.chat-refer-left{
|
||||||
|
margin-left:55px;
|
||||||
|
}
|
||||||
.item-left .bubble{
|
.item-left .bubble{
|
||||||
margin-left: 15px;
|
margin-left: 15px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
@ -700,7 +748,7 @@ textarea{
|
|||||||
.pagination-container {
|
.pagination-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
margin-left: 20px; /* 新增的左边距 */
|
margin-left: 20px; /* 新增的左边距 */
|
||||||
}
|
}
|
||||||
@ -802,47 +850,6 @@ input {
|
|||||||
'''
|
'''
|
||||||
html_end = '''
|
html_end = '''
|
||||||
];
|
];
|
||||||
function renderMessages(messages) {
|
|
||||||
for (const message of messages) {
|
|
||||||
const messageElement = document.createElement('div');
|
|
||||||
if (message.type == 1) {
|
|
||||||
if (message.is_send == 1) {
|
|
||||||
messageElement.className = "item item-right";
|
|
||||||
messageElement.innerHTML = `<div class='bubble bubble-right'>${message.text}</div><div class='avatar'><img src="${message.avatar_path}" /></div>`
|
|
||||||
}
|
|
||||||
else if (message.is_send == 0) {
|
|
||||||
messageElement.className = "item item-left";
|
|
||||||
messageElement.innerHTML = `<div class='avatar'><img src="${message.avatar_path}" /></div><div class='bubble bubble-right'>${message.text}</div>`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (message.type == 0) {
|
|
||||||
messageElement.className = "item item-center";
|
|
||||||
messageElement.innerHTML = `<span>${message.text}</span>`
|
|
||||||
}
|
|
||||||
else if (message.type == 3) {
|
|
||||||
if (message.is_send == 1) {
|
|
||||||
messageElement.className = "item item-right";
|
|
||||||
messageElement.innerHTML = `<div class='chat-image'><img src="${message.text}" /></div><div class='avatar'><img src="${message.avatar_path}" /></div>`
|
|
||||||
}
|
|
||||||
else if (message.is_send == 0) {
|
|
||||||
messageElement.className = "item item-left";
|
|
||||||
messageElement.innerHTML = `<div class='avatar'><img src="${message.avatar_path}" /></div><div class='chat-image'><img src="${message.text}" /></div>`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (message.type == 43) {
|
|
||||||
if (message.is_send == 1) {
|
|
||||||
messageElement.className = "item item-right";
|
|
||||||
messageElement.innerHTML = `<div class='chat-video'><video src="${message.text}" controls /></div><div class='avatar'><img src="${message.avatar_path}" /></div>`
|
|
||||||
}
|
|
||||||
else if (message.is_send == 0) {
|
|
||||||
messageElement.className = "item item-left";
|
|
||||||
messageElement.innerHTML = `<div class='avatar'><img src="${message.avatar_path}" /></div><div class='chat-video'><video src="${message.text}" controls /></div>`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chatContainer.appendChild(messageElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkEnter(event) {
|
function checkEnter(event) {
|
||||||
if (event.keyCode === 13) {
|
if (event.keyCode === 13) {
|
||||||
gotoPage();
|
gotoPage();
|
||||||
@ -898,6 +905,18 @@ html_end = '''
|
|||||||
messageElement.innerHTML = `<div class='avatar'><img src="${message.avatar_path}" /></div><div class='chat-video'><video src="${message.text}" controls "/></div>`
|
messageElement.innerHTML = `<div class='avatar'><img src="${message.avatar_path}" /></div><div class='chat-video'><video src="${message.text}" controls "/></div>`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (message.type == 49) {
|
||||||
|
if (message.sub_type == 57){
|
||||||
|
if (message.is_send == 1) {
|
||||||
|
messageElement.className = "item item-right";
|
||||||
|
messageElement.innerHTML = `<div class='chat-refer chat-refer-right'>${message.text}</div></div>`
|
||||||
|
}
|
||||||
|
else if (message.is_send == 0) {
|
||||||
|
messageElement.className = "item item-left";
|
||||||
|
messageElement.innerHTML = `<div class='chat-refer chat-refer-left'>${message.text}</div></div>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
chatContainer.appendChild(messageElement);
|
chatContainer.appendChild(messageElement);
|
||||||
}
|
}
|
||||||
document.querySelector("#chat-container").scrollTop = 0;
|
document.querySelector("#chat-container").scrollTop = 0;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import os.path
|
import os.path
|
||||||
|
import re
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
@ -47,7 +48,7 @@ class ContactPC:
|
|||||||
self.nickName = contact_info.get('NickName')
|
self.nickName = contact_info.get('NickName')
|
||||||
if not self.remark:
|
if not self.remark:
|
||||||
self.remark = self.nickName
|
self.remark = self.nickName
|
||||||
self.remark.replace('*', '_').replace('/', '_').replace('\\', '_')
|
self.remark = re.sub(r'[\/:*?"<>|]', '_', self.remark)
|
||||||
self.smallHeadImgUrl = contact_info.get('smallHeadImgUrl')
|
self.smallHeadImgUrl = contact_info.get('smallHeadImgUrl')
|
||||||
self.smallHeadImgBLOG = b''
|
self.smallHeadImgBLOG = b''
|
||||||
self.avatar = QPixmap()
|
self.avatar = QPixmap()
|
||||||
|
66
app/util/compress_content.py
Normal file
66
app/util/compress_content.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import html
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
|
import lz4
|
||||||
|
|
||||||
|
|
||||||
|
def decompress_CompressContent(data):
|
||||||
|
"""
|
||||||
|
解压缩Msg:CompressContent内容
|
||||||
|
:param data:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if data is None or not isinstance(data, bytes):
|
||||||
|
return ''
|
||||||
|
try:
|
||||||
|
dst = lz4.block.decompress(data, uncompressed_size=len(data) << 10)
|
||||||
|
decoded_string = dst.decode().replace('\x00', '') # Remove any null characters
|
||||||
|
except lz4.block.LZ4BlockError:
|
||||||
|
print("Decompression failed: potentially corrupt input or insufficient buffer size.")
|
||||||
|
return ''
|
||||||
|
return decoded_string
|
||||||
|
def escape_js_and_html(input_str):
|
||||||
|
# 转义HTML特殊字符
|
||||||
|
html_escaped = html.escape(input_str, quote=False)
|
||||||
|
|
||||||
|
# 手动处理JavaScript转义字符
|
||||||
|
js_escaped = (
|
||||||
|
html_escaped
|
||||||
|
.replace("\\", "\\\\")
|
||||||
|
.replace("'", r"\'")
|
||||||
|
.replace('"', r'\"')
|
||||||
|
.replace("\n", r'\n')
|
||||||
|
.replace("\r", r'\r')
|
||||||
|
.replace("\t", r'\t')
|
||||||
|
)
|
||||||
|
|
||||||
|
return js_escaped
|
||||||
|
|
||||||
|
def parser_reply(data: bytes):
|
||||||
|
xml_content = decompress_CompressContent(data)
|
||||||
|
if not xml_content:
|
||||||
|
return {
|
||||||
|
'type': 57,
|
||||||
|
'title': "发生错误",
|
||||||
|
'refer': {
|
||||||
|
'type': '1',
|
||||||
|
'content': '引用错误',
|
||||||
|
'displayname': '用户名',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root = ET.XML(xml_content)
|
||||||
|
appmsg = root.find('appmsg')
|
||||||
|
msg_type = appmsg.find('type').text
|
||||||
|
title = appmsg.find('title').text
|
||||||
|
refermsg_content = appmsg.find('refermsg').find('content').text
|
||||||
|
refermsg_type = appmsg.find('refermsg').find('type').text
|
||||||
|
refermsg_displayname = appmsg.find('refermsg').find('displayname').text
|
||||||
|
return {
|
||||||
|
'type': msg_type,
|
||||||
|
'title': title,
|
||||||
|
'refer': {
|
||||||
|
'type': refermsg_type,
|
||||||
|
'content': escape_js_and_html(refermsg_content),
|
||||||
|
'displayname': escape_js_and_html(refermsg_displayname),
|
||||||
|
}
|
||||||
|
}
|
@ -15,4 +15,4 @@ jieba==0.42.1
|
|||||||
google==3.0.0
|
google==3.0.0
|
||||||
protobuf==4.25.1
|
protobuf==4.25.1
|
||||||
soupsieve==2.5
|
soupsieve==2.5
|
||||||
lz4
|
lz4==4.3.2
|
Loading…
Reference in New Issue
Block a user