支持显示表情包

This commit is contained in:
shuaikangzhou 2023-11-24 23:49:37 +08:00
parent b732481d9c
commit cb571e937d
9 changed files with 164 additions and 58 deletions

View File

@ -4,13 +4,16 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="84e65474-7da9-466d-baf3-cc88dde3ffdd" name="变更" comment="添加资源文件">
<list default="true" id="84e65474-7da9-466d-baf3-cc88dde3ffdd" name="变更" comment="修改文本消息宽度">
<change afterPath="$PROJECT_DIR$/app/util/emoji.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/DataBase/hard_link.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/DataBase/hard_link.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/components/bubble_message.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/components/bubble_message.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/decrypt/dat2pic.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/decrypt/dat2pic.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/ui_pc/chat/chat_window.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/ui_pc/chat/chat_window.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/ui_pc/contact/contact_window.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/ui_pc/contact/contact_window.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/decrypt/dat2pic.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/util/dat2pic.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/decrypt/decrypt.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/decrypt/decrypt.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/ui_pc/chat/chat_info.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/ui_pc/chat/chat_info.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/util/path.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/util/path.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/main_pc.py" beforeDir="false" afterPath="$PROJECT_DIR$/main_pc.py" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -108,21 +111,21 @@
"DefaultHtmlFileTemplate": "HTML File",
"RunOnceActivity.OpenProjectViewOnStart": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"last_opened_file_path": "D:/Project/Python/WeChatMsg/app/resources",
"last_opened_file_path": "D:/Project/Python/WeChatMsg/app/util",
"settings.editor.selected.configurable": "preferences.pluginManager"
}
}]]></component>
<component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS">
<recent name="D:\Project\Python\WeChatMsg\app\util" />
<recent name="D:\Project\Python\WeChatMsg\app\resources" />
<recent name="D:\Project\Python\WeChatMsg\resource" />
<recent name="D:\Project\Python\WeChatMsg\app\ui_pc\tool" />
<recent name="D:\Project\Python\WeChatMsg\app\" />
<recent name="D:\Project\PythonProject\WeChatMsg\app\Ui" />
</key>
</component>
<component name="RunManager" selected="Python.dat2pic">
<configuration name="dat2pic" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<component name="RunManager" selected="Python.main_pc">
<configuration name="decrypt" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="WeChatMsg" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
@ -134,7 +137,7 @@
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/app/decrypt/dat2pic.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/app/decrypt/decrypt.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
@ -164,6 +167,27 @@
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="emoji" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="WeChatMsg" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/app/util" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/app/util/emoji.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="hard_link" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="WeChatMsg" />
<option name="INTERPRETER_OPTIONS" value="" />
@ -200,7 +224,7 @@
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/main_pc.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="EMULATE_TERMINAL" value="true" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
@ -227,34 +251,13 @@
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="test" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="WeChatMsg" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/test.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<recent_temporary>
<list>
<item itemvalue="Python.dat2pic" />
<item itemvalue="Python.hard_link" />
<item itemvalue="Python.main_pc" />
<item itemvalue="Python.hard_link" />
<item itemvalue="Python.emoji" />
<item itemvalue="Python.decrypt_window" />
<item itemvalue="Python.test" />
<item itemvalue="Python.decrypt" />
</list>
</recent_temporary>
</component>
@ -270,13 +273,6 @@
<option name="presentableId" value="Default" />
<updated>1672848140146</updated>
</task>
<task id="LOCAL-00037" summary="用stackedWidget实现导航栏">
<created>1698850498765</created>
<option name="number" value="00037" />
<option name="presentableId" value="LOCAL-00037" />
<option name="project" value="LOCAL" />
<updated>1698850498765</updated>
</task>
<task id="LOCAL-00038" summary="修复部分bug">
<created>1698853140384</created>
<option name="number" value="00038" />
@ -613,7 +609,14 @@
<option name="project" value="LOCAL" />
<updated>1700751510245</updated>
</task>
<option name="localTasksCounter" value="86" />
<task id="LOCAL-00086" summary="修改文本消息宽度">
<created>1700828162843</created>
<option name="number" value="00086" />
<option name="presentableId" value="LOCAL-00086" />
<option name="project" value="LOCAL" />
<updated>1700828162843</updated>
</task>
<option name="localTasksCounter" value="87" />
<servers />
</component>
<component name="UnknownFeatures">
@ -649,7 +652,6 @@
</option>
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="修复无法查找wxid的bug" />
<MESSAGE value="修改UI" />
<MESSAGE value="新增联系人头像组件" />
<MESSAGE value="头像支持显示二进制" />
@ -674,7 +676,8 @@
<MESSAGE value="文字消息设置圆角" />
<MESSAGE value="更新wx选择的路径" />
<MESSAGE value="添加资源文件" />
<option name="LAST_COMMIT_MESSAGE" value="添加资源文件" />
<MESSAGE value="修改文本消息宽度" />
<option name="LAST_COMMIT_MESSAGE" value="修改文本消息宽度" />
<option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="true" />
<option name="REFORMAT_BEFORE_PROJECT_COMMIT" value="true" />
</component>

View File

@ -54,7 +54,7 @@ def get_md5_from_xml(content):
def get_image(content, thumb=False):
md5 = get_md5_from_xml(content)
# md5 = 'bc37a58c32cb203ee9ac587b068e5853'
md5 = '41895664fc5a77878e2155fc96209a19'
# md5 = '41895664fc5a77878e2155fc96209a19'
result = get_image_by_md5(binascii.unhexlify(md5))
if result:
# print(result)

View File

@ -105,7 +105,7 @@ class OpenImageThread(QThread):
class ImageMessage(QLabel):
def __init__(self, image, image_link='', max_width=480, max_height=240, parent=None):
def __init__(self, image, is_send, image_link='', max_width=480, max_height=240, parent=None):
"""
param:image 图像路径或者QPixmap对象
param:image_link='' 点击图像打开的文件路径
@ -124,6 +124,8 @@ class ImageMessage(QLabel):
self.image_path = image_link
self.setMaximumWidth(self.max_width)
self.setMaximumHeight(self.max_height)
if is_send:
self.setAlignment(Qt.AlignCenter | Qt.AlignRight)
# self.setScaledContents(True)
def set_image(self, pixmap):
@ -161,7 +163,7 @@ class BubbleMessage(QWidget):
self.message = TextMessage(str_content, is_send)
# self.message.setMaximumWidth(int(self.width() * 0.6))
elif Type == MessageType.Image:
self.message = ImageMessage(str_content)
self.message = ImageMessage(str_content, is_send)
else:
raise ValueError("未知的消息类型")

View File

@ -119,11 +119,6 @@ def batch_decrypt(key: str, db_path: Union[str, List[str]], out_path: str):
if __name__ == '__main__':
# 调用 decrypt 函数,并传入参数
key = "2aafab10af7940328bb92ac9d2a8ab5fc07a685646b14f2e9ae6948a7060c0fc"
db_path = "D:\Project\Python\PyWxDump\pywxdump\decrypted"
out_path = "test"
result = batch_decrypt(key, db_path, out_path)
for i in result:
if isinstance(i, str):
print(i)
else:
print(f'[+] "{i[1]}" -> "{i[2]}"')
db_path = "E:\86390\Documents\WeChat Files\wxid_27hqbq7vx5hf22\FileStorage\CustomEmotion\\71\\71CE49ED3CE9E57E43E07F802983BF45"
out_path = "./test/1.png"
print(decrypt(key, db_path, out_path))

View File

@ -7,6 +7,7 @@ from app.DataBase import msg, hard_link
from app.components.bubble_message import BubbleMessage, ChatWidget, Notice
from app.person import MePC
from app.util import get_abs_path
from app.util.emoji import get_emoji
class ChatInfo(QWidget):
@ -113,6 +114,20 @@ class ChatInfo(QWidget):
is_send
)
self.chat_window.add_message_item(bubble_message, 0)
elif type_ == 47:
# return
if self.is_5_min(timestamp):
time_message = Notice(self.last_str_time)
self.last_str_time = str_time
self.chat_window.add_message_item(time_message, 0)
image_path = get_emoji(str_content, thumb=True)
bubble_message = BubbleMessage(
image_path,
avatar,
3,
is_send
)
self.chat_window.add_message_item(bubble_message, 0)
except:
print(message)
traceback.print_exc()

91
app/util/emoji.py Normal file
View File

@ -0,0 +1,91 @@
import os
import xml.etree.ElementTree as ET
import requests
root_path = './data/emoji/'
if not os.path.exists('./data'):
os.mkdir('./data')
if not os.path.exists(root_path):
os.mkdir(root_path)
def get_image_format(header):
# 定义图片格式的 magic numbers
image_formats = {
b'\xFF\xD8\xFF': 'jpeg',
b'\x89\x50\x4E\x47\x0D\x0A\x1A\x0A': 'png',
b'\x47\x49\x46': 'gif',
b'\x42\x4D': 'bmp',
# 添加其他图片格式的 magic numbers
}
# 判断文件的图片格式
for magic_number, image_format in image_formats.items():
if header.startswith(magic_number):
return image_format
# 如果无法识别格式,返回 None
return None
def parser_xml(xml_string):
# Parse the XML string
root = ET.fromstring(xml_string)
emoji = root.find('./emoji')
# Accessing attributes of the 'emoji' element
fromusername = emoji.get('fromusername')
tousername = emoji.get('tousername')
md5 = emoji.get('md5')
cdnurl = emoji.get('cdnurl')
encrypturl = emoji.get('encrypturl')
thumburl = emoji.get('thumburl')
externurl = emoji.get('externurl')
width = emoji.get('width')
height = emoji.get('height')
return {
'width': width,
'height': height,
'cdnurl': cdnurl,
'thumburl': thumburl if thumburl else cdnurl,
'md5': md5
}
def download(url, output_dir, name, thumb=False):
resp = requests.get(url)
byte = resp.content
image_format = get_image_format(byte[:8])
if image_format:
if thumb:
output_path = os.path.join(output_dir, 'th_' + name + '.' + image_format)
else:
output_path = os.path.join(output_dir, name + '.' + image_format)
else:
output_path = os.path.join(output_dir, name)
with open(output_path, 'wb') as f:
f.write(resp.content)
return output_path
def get_emoji(xml_string, thumb=True) -> str:
emoji_info = parser_xml(xml_string)
md5 = emoji_info['md5']
image_format = ['.png', '.gif', '.jpeg']
for f in image_format:
prefix = 'th_' if thumb else ''
file_path = os.path.join(root_path, prefix + md5 + f)
if os.path.exists(file_path):
return file_path
url = emoji_info['thumburl'] if thumb else emoji_info['cdnurl']
print("下载表情包ing:", url)
return download(url, root_path, md5, thumb)
if __name__ == '__main__':
xml_string = '<msg><emoji fromusername = "wxid_0o18ef858vnu22" tousername = "wxid_27hqbq7vx5hf22" type="2" idbuffer="media:0_0" md5="71ce49ed3ce9e57e43e07f802983bf45" len = "352588" productid="com.tencent.xin.emoticon.person.stiker_1678703862259eb01f2ef4a313" androidmd5="71ce49ed3ce9e57e43e07f802983bf45" androidlen="352588" s60v3md5 = "71ce49ed3ce9e57e43e07f802983bf45" s60v3len="352588" s60v5md5 = "71ce49ed3ce9e57e43e07f802983bf45" s60v5len="352588" cdnurl = "http://wxapp.tc.qq.com/262/20304/stodownload?m=71ce49ed3ce9e57e43e07f802983bf45&amp;filekey=30350201010421301f020201060402535a041071ce49ed3ce9e57e43e07f802983bf45020305614c040d00000004627466730000000132&amp;hy=SZ&amp;storeid=263ffa00b000720d03274c5820000010600004f50535a1ca0c950b64287022&amp;bizid=1023" designerid = "" thumburl = "http://mmbiz.qpic.cn/mmemoticon/ajNVdqHZLLDSKTMRgM8agiadpFhKz9IJ3cD5Ra2sTROibOaShdt3D4z6PfE92WkjQY/0" encrypturl = "http://wxapp.tc.qq.com/262/20304/stodownload?m=cbaae1d847aac6389652b65562bacaa2&amp;filekey=30350201010421301f020201060402535a0410cbaae1d847aac6389652b65562bacaa20203056150040d00000004627466730000000132&amp;hy=SZ&amp;storeid=263ffa00b0008d8223274c5820000010600004f50535a17b82910b64764739&amp;bizid=1023" aeskey= "7051ab2a34442dec63434832463f45ce" externurl = "http://wxapp.tc.qq.com/262/20304/stodownload?m=960f68693454dfa64b9966ca5d70dbd3&amp;filekey=30340201010420301e020201060402535a0410960f68693454dfa64b9966ca5d70dbd3020221a0040d00000004627466730000000132&amp;hy=SZ&amp;storeid=26423dbe3000793a8720e40de0000010600004f50535a1d40c950b71be0a50&amp;bizid=1023" externmd5 = "41895664fc5a77878e2155fc96209a19" width= "240" height= "240" tpurl= "" tpauthkey= "" attachedtext= "" attachedtextcolor= "" lensid= "" emojiattr= "" linkid= "" desc= "ChEKB2RlZmF1bHQSBuWNlee6rw==" ></emoji> </msg>'
res1 = parser_xml(xml_string)
print(res1, res1['md5'])
# download(res1['cdnurl'], "./data/emoji/", res1['md5'])
# download(res1['thumburl'], "./data/emoji/", res1['md5'], True)
print(get_emoji(xml_string, True))
print(get_emoji(xml_string, False))
# http://vweixinf.tc.qq.com/110/20403/stodownload?m=3a4d439aba02dce4834b2c54e9f15597&filekey=3043020101042f302d02016e0402534804203361346434333961626130326463653438333462326335346539663135353937020213f0040d00000004627466730000000131&hy=SH&storeid=323032313037323030373236313130303039653236646365316535316534383236386234306230303030303036653033303034666233&ef=3&bizid=1022

View File

@ -1,7 +1,7 @@
import os
from app.decrypt import dat2pic
from app.person_pc import MePC
from app.util import dat2pic
if not os.path.exists('./data/image'):
os.mkdir('./data/image')

View File

@ -15,7 +15,7 @@ class ViewController(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('解密')
self.setWindowIcon(QIcon('./app/data/icons/logo.svg'))
self.setWindowIcon(QIcon(':/icons/icons/logo.svg'))
self.viewMainWIndow = None
self.viewDecrypt = None
# 创建加载动画