Merge pull request #20 from LC044/master

pull
This commit is contained in:
SiYuan 2023-11-21 20:54:23 +08:00 committed by GitHub
commit edbe83ff0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 377 additions and 161 deletions

1
.gitignore vendored
View File

@ -9,6 +9,7 @@ TEST
app/data/avatar app/data/avatar
app/data/image2 app/data/image2
app/data/emoji app/data/emoji
app/DataBase/Msg/*
*.db *.db
*.pyc *.pyc
*.log *.log

View File

@ -4,13 +4,15 @@
<option name="autoReloadType" value="SELECTIVE" /> <option name="autoReloadType" value="SELECTIVE" />
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="84e65474-7da9-466d-baf3-cc88dde3ffdd" name="变更" comment="修复修改wixd的bug"> <list default="true" id="84e65474-7da9-466d-baf3-cc88dde3ffdd" name="变更" comment="更新wx选择的路径">
<change afterPath="$PROJECT_DIR$/app/util/path.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" 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/components/bubble_message.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/components/bubble_message.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/Icon.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/ui_pc/Icon.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/decrypt/get_wx_info.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/decrypt/get_wx_info.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/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/ui_pc/tool/pc_decrypt/pc_decrypt.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/ui_pc/tool/pc_decrypt/pc_decrypt.py" afterDir="false" /> <change beforePath="$PROJECT_DIR$/app/ui_pc/mainview.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/ui_pc/mainview.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/util/__init__.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/util/__init__.py" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -30,10 +32,23 @@
<option name="myRunOnSave" value="true" /> <option name="myRunOnSave" value="true" />
</component> </component>
<component name="Git.Settings"> <component name="Git.Settings">
<excluded-from-favorite>
<branch-storage>
<map>
<entry type="LOCAL">
<value>
<list>
<branch-info repo="$PROJECT_DIR$" source="master" />
</list>
</value>
</entry>
</map>
</branch-storage>
</excluded-from-favorite>
<option name="PUSH_AUTO_UPDATE" value="true" /> <option name="PUSH_AUTO_UPDATE" value="true" />
<option name="RECENT_BRANCH_BY_REPOSITORY"> <option name="RECENT_BRANCH_BY_REPOSITORY">
<map> <map>
<entry key="$PROJECT_DIR$" value="master" /> <entry key="$PROJECT_DIR$" value="dev_zsk" />
</map> </map>
</option> </option>
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
@ -54,12 +69,12 @@
<option name="branches"> <option name="branches">
<list> <list>
<RecentBranch> <RecentBranch>
<option name="branchName" value="dev_zsk" /> <option name="branchName" value="master" />
<option name="lastUsedInstant" value="1700046130" /> <option name="lastUsedInstant" value="1700478623" />
</RecentBranch> </RecentBranch>
<RecentBranch> <RecentBranch>
<option name="branchName" value="master" /> <option name="branchName" value="dev_zsk" />
<option name="lastUsedInstant" value="1700046129" /> <option name="lastUsedInstant" value="1700046130" />
</RecentBranch> </RecentBranch>
</list> </list>
</option> </option>
@ -96,7 +111,7 @@
"RunOnceActivity.OpenProjectViewOnStart": "true", "RunOnceActivity.OpenProjectViewOnStart": "true",
"RunOnceActivity.ShowReadmeOnStart": "true", "RunOnceActivity.ShowReadmeOnStart": "true",
"last_opened_file_path": "D:/Program Files/Python310/Scripts/pyuic5.exe", "last_opened_file_path": "D:/Program Files/Python310/Scripts/pyuic5.exe",
"settings.editor.selected.configurable": "preferences.lookFeel" "settings.editor.selected.configurable": "preferences.pluginManager"
} }
}]]></component> }]]></component>
<component name="RecentsManager"> <component name="RecentsManager">
@ -106,28 +121,7 @@
<recent name="D:\Project\PythonProject\WeChatMsg\app\Ui" /> <recent name="D:\Project\PythonProject\WeChatMsg\app\Ui" />
</key> </key>
</component> </component>
<component name="RunManager" selected="Python.decrypt_window"> <component name="RunManager" selected="Python.main_pc">
<configuration name="bubble_message" 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/components" />
<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/components/bubble_message.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="decrypt_window" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true"> <configuration name="decrypt_window" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="WeChatMsg" /> <module name="WeChatMsg" />
<option name="INTERPRETER_OPTIONS" value="" /> <option name="INTERPRETER_OPTIONS" value="" />
@ -149,6 +143,48 @@
<option name="INPUT_FILE" value="" /> <option name="INPUT_FILE" value="" />
<method v="2" /> <method v="2" />
</configuration> </configuration>
<configuration name="hard_link" 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/DataBase" />
<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/DataBase/hard_link.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="main" 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$/main.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" 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="" />
<method v="2" />
</configuration>
<configuration name="main_pc" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true"> <configuration name="main_pc" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="WeChatMsg" /> <module name="WeChatMsg" />
<option name="INTERPRETER_OPTIONS" value="" /> <option name="INTERPRETER_OPTIONS" value="" />
@ -164,28 +200,7 @@
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/main_pc.py" /> <option name="SCRIPT_NAME" value="$PROJECT_DIR$/main_pc.py" />
<option name="PARAMETERS" value="" /> <option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" /> <option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="true" /> <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="mainview" type="PythonConfigurationType" factoryName="Python" 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/ui_pc" />
<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/ui_pc/mainview.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="true" />
<option name="MODULE_MODE" value="false" /> <option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" /> <option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" /> <option name="INPUT_FILE" value="" />
@ -212,7 +227,7 @@
<option name="INPUT_FILE" value="" /> <option name="INPUT_FILE" value="" />
<method v="2" /> <method v="2" />
</configuration> </configuration>
<configuration name="test (1)" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true"> <configuration name="test" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="WeChatMsg" /> <module name="WeChatMsg" />
<option name="INTERPRETER_OPTIONS" value="" /> <option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" /> <option name="PARENT_ENVS" value="true" />
@ -233,34 +248,13 @@
<option name="INPUT_FILE" value="" /> <option name="INPUT_FILE" value="" />
<method v="2" /> <method v="2" />
</configuration> </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$/app/components" />
<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/components/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> <recent_temporary>
<list> <list>
<item itemvalue="Python.decrypt_window" />
<item itemvalue="Python.main_pc" /> <item itemvalue="Python.main_pc" />
<item itemvalue="Python.test (1)" />
<item itemvalue="Python.bubble_message" />
<item itemvalue="Python.test" /> <item itemvalue="Python.test" />
<item itemvalue="Python.hard_link" />
<item itemvalue="Python.decrypt_window" />
<item itemvalue="Python.main" />
</list> </list>
</recent_temporary> </recent_temporary>
</component> </component>
@ -276,20 +270,6 @@
<option name="presentableId" value="Default" /> <option name="presentableId" value="Default" />
<updated>1672848140146</updated> <updated>1672848140146</updated>
</task> </task>
<task id="LOCAL-00034" summary="修改部分UI">
<created>1698592548209</created>
<option name="number" value="00034" />
<option name="presentableId" value="LOCAL-00034" />
<option name="project" value="LOCAL" />
<updated>1698592548210</updated>
</task>
<task id="LOCAL-00035" summary="update readme">
<created>1698681328723</created>
<option name="number" value="00035" />
<option name="presentableId" value="LOCAL-00035" />
<option name="project" value="LOCAL" />
<updated>1698681328723</updated>
</task>
<task id="LOCAL-00036" summary="重构一些class删除一些不必要的文件"> <task id="LOCAL-00036" summary="重构一些class删除一些不必要的文件">
<created>1698765961025</created> <created>1698765961025</created>
<option name="number" value="00036" /> <option name="number" value="00036" />
@ -619,7 +599,21 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1700410804352</updated> <updated>1700410804352</updated>
</task> </task>
<option name="localTasksCounter" value="83" /> <task id="LOCAL-00083" summary="文字消息设置圆角">
<created>1700478493497</created>
<option name="number" value="00083" />
<option name="presentableId" value="LOCAL-00083" />
<option name="project" value="LOCAL" />
<updated>1700478493497</updated>
</task>
<task id="LOCAL-00084" summary="更新wx选择的路径">
<created>1700490633275</created>
<option name="number" value="00084" />
<option name="presentableId" value="LOCAL-00084" />
<option name="project" value="LOCAL" />
<updated>1700490633275</updated>
</task>
<option name="localTasksCounter" value="85" />
<servers /> <servers />
</component> </component>
<component name="UnknownFeatures"> <component name="UnknownFeatures">
@ -642,7 +636,7 @@
<entry key="branch"> <entry key="branch">
<value> <value>
<list> <list>
<option value="origin/dev_zsk" /> <option value="master" />
</list> </list>
</value> </value>
</entry> </entry>
@ -655,8 +649,6 @@
</option> </option>
</component> </component>
<component name="VcsManagerConfiguration"> <component name="VcsManagerConfiguration">
<MESSAGE value="增加日志模块" />
<MESSAGE value="增加PC端微信解密条件的判断" />
<MESSAGE value="删除多余的Word文件" /> <MESSAGE value="删除多余的Word文件" />
<MESSAGE value="修复无法查找wxid的bug" /> <MESSAGE value="修复无法查找wxid的bug" />
<MESSAGE value="修改UI" /> <MESSAGE value="修改UI" />
@ -680,7 +672,9 @@
<MESSAGE value="修复第一次启动的显示问题" /> <MESSAGE value="修复第一次启动的显示问题" />
<MESSAGE value="支持查找功能" /> <MESSAGE value="支持查找功能" />
<MESSAGE value="修复修改wixd的bug" /> <MESSAGE value="修复修改wixd的bug" />
<option name="LAST_COMMIT_MESSAGE" value="修复修改wixd的bug" /> <MESSAGE value="文字消息设置圆角" />
<MESSAGE value="更新wx选择的路径" />
<option name="LAST_COMMIT_MESSAGE" value="更新wx选择的路径" />
<option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="true" /> <option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="true" />
<option name="REFORMAT_BEFORE_PROJECT_COMMIT" value="true" /> <option name="REFORMAT_BEFORE_PROJECT_COMMIT" value="true" />
</component> </component>
@ -699,24 +693,9 @@
</line-breakpoint> </line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line"> <line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/app/decrypt/decrypt.py</url> <url>file://$PROJECT_DIR$/app/decrypt/decrypt.py</url>
<line>105</line> <line>103</line>
<option name="timeStamp" value="9" /> <option name="timeStamp" value="9" />
</line-breakpoint> </line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/app/person.py</url>
<line>100</line>
<option name="timeStamp" value="10" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/app/person.py</url>
<line>98</line>
<option name="timeStamp" value="11" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/app/person.py</url>
<line>99</line>
<option name="timeStamp" value="12" />
</line-breakpoint>
</breakpoints> </breakpoints>
<default-breakpoints> <default-breakpoints>
<breakpoint type="python-exception"> <breakpoint type="python-exception">

78
app/DataBase/hard_link.py Normal file
View File

@ -0,0 +1,78 @@
import binascii
import os.path
import sqlite3
import threading
import xml.etree.ElementTree as ET
lock = threading.Lock()
DB = None
cursor = None
db_path = "./app/Database/Msg/HardLinkImage.db"
root_path = 'FileStorage/MsgAttach/'
if os.path.exists(db_path):
DB = sqlite3.connect(db_path, check_same_thread=False)
# '''创建游标'''
cursor = DB.cursor()
def init_database():
global DB
global cursor
if not DB:
if os.path.exists(db_path):
DB = sqlite3.connect(db_path, check_same_thread=False)
# '''创建游标'''
cursor = DB.cursor()
def get_image_by_md5(md5: bytes):
sql = '''
select Md5Hash,MD5,FileName,HardLinkImageID.Dir as DirName1,HardLinkImageID2.Dir as DirName2
from HardLinkImageAttribute
join HardLinkImageID on HardLinkImageAttribute.DirID1 = HardLinkImageID.DirID
join HardLinkImageID as HardLinkImageID2 on HardLinkImageAttribute.DirID2 = HardLinkImageID2.DirID
where MD5 = ?;
'''
try:
lock.acquire(True)
cursor.execute(sql, [md5, ])
result = cursor.fetchone()
return result
finally:
lock.release()
def get_md5_from_xml(content):
# 解析XML
root = ET.fromstring(content)
# 提取md5的值
md5_value = root.find(".//img").get("md5")
# print(md5_value)
return md5_value
def get_image(content, thumb=False):
md5 = get_md5_from_xml(content)
# md5 = 'bc37a58c32cb203ee9ac587b068e5853'
result = get_image_by_md5(binascii.unhexlify(md5))
if result:
# print(result)
dir1 = result[3]
dir2 = result[4]
data_image = result[2]
dir0 = 'Thumb' if thumb else 'Image'
dat_image = os.path.join(root_path, dir1, dir0, dir2, data_image)
return dat_image
# 6b02292eecea118f06be3a5b20075afc_t
if __name__ == '__main__':
msg_root_path = './Msg/'
db_path = "./Msg/HardLinkImage.db"
init_database()
content = '''<?xml version="1.0"?><msg>\n\t<img aeskey="bc37a58c32cb203ee9ac587b068e5853" encryver="1" cdnthumbaeskey="bc37a58c32cb203ee9ac587b068e5853" cdnthumburl="3057020100044b30490201000204d181705002032f5405020428a7b4de02046537869d042462313532363539632d663930622d343463302d616636662d333837646434633061626534020401150a020201000405004c4c6d00" cdnthumblength="3097" cdnthumbheight="120" cdnthumbwidth="68" cdnmidheight="0" cdnmidwidth="0" cdnhdheight="0" cdnhdwidth="0" cdnmidimgurl="3057020100044b30490201000204d181705002032f5405020428a7b4de02046537869d042462313532363539632d663930622d343463302d616636662d333837646434633061626534020401150a020201000405004c4c6d00" length="57667" md5="6844b812d5d514eb6878657e0bf4cdbb" originsourcemd5="1dfdfa24922270ea1cb5daba103f45ca" />\n\t<platform_signature></platform_signature>\n\t<imgdatahash></imgdatahash>\n</msg>\n'''
print(get_image(content))
print(get_image(content, thumb=False))
result = get_md5_from_xml(content)
print(result)

View File

@ -6,6 +6,9 @@ from PyQt5.QtCore import pyqtSignal, QThread
from . import msg from . import msg
from ..log import log from ..log import log
if not os.path.exists('./data/聊天记录'):
os.mkdir('./data/聊天记录')
class Output(QThread): class Output(QThread):
""" """

View File

@ -8,7 +8,7 @@ from PyQt5.QtWidgets import QWidget, QLabel, QHBoxLayout, QSizePolicy, QVBoxLayo
class MessageType: class MessageType:
Text = 1 Text = 1
Image = 2 Image = 3
class TextMessage(QLabel): class TextMessage(QLabel):
@ -104,20 +104,28 @@ class OpenImageThread(QThread):
class ImageMessage(QLabel): class ImageMessage(QLabel):
def __init__(self, avatar, parent=None): def __init__(self, image, image_link='', max_width=480, max_height=720, parent=None):
"""
param:image 图像路径或者QPixmap对象
param:image_link='' 点击图像打开的文件路径
"""
super().__init__(parent) super().__init__(parent)
self.image = QLabel(self) self.image = QLabel(self)
if isinstance(avatar, str):
self.setPixmap(QPixmap(avatar)) if isinstance(image, str):
self.image_path = avatar self.setPixmap(QPixmap(image))
elif isinstance(avatar, QPixmap): self.image_path = image
self.setPixmap(avatar) elif isinstance(image, QPixmap):
self.setMaximumWidth(480) self.setPixmap(image)
self.setMaximumHeight(720) if image_link:
self.setScaledContents(True) self.image_path = image_link
self.setMaximumWidth(max_width)
self.setMaximumHeight(max_height)
# self.setScaledContents(True)
def mousePressEvent(self, event): def mousePressEvent(self, event):
if event.buttons() == Qt.LeftButton: # 左键按下 if event.buttons() == Qt.LeftButton: # 左键按下
print('打开图像', self.image_path)
self.open_image_thread = OpenImageThread(self.image_path) self.open_image_thread = OpenImageThread(self.image_path)
self.open_image_thread.start() self.open_image_thread.start()

BIN
app/data/icons/404.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

91
app/decrypt/dat2pic.py Normal file
View File

@ -0,0 +1,91 @@
import os
# 图片字节头信息,
# [0][1]为jpg头信息
# [2][3]为png头信息
# [4][5]为gif头信息
pic_head = [0xff, 0xd8, 0x89, 0x50, 0x47, 0x49]
# 解密码
decode_code = 0
def get_code(file_path):
"""
自动判断文件类型并获取dat文件解密码
:param file_path: dat文件路径
:return: 如果文件为jpg/png/gif格式则返回解密码否则返回-1
"""
if os.path.isdir(file_path):
return -1, -1
if file_path[-4:] != ".dat":
return -1, -1
dat_file = open(file_path, "rb")
dat_read = dat_file.read(2)
print(dat_read)
head_index = 0
while head_index < len(pic_head):
# 使用第一个头信息字节来计算加密码
# 第二个字节来验证解密码是否正确
code = dat_read[0] ^ pic_head[head_index]
idf_code = dat_read[1] ^ code
head_index = head_index + 1
if idf_code == pic_head[head_index]:
dat_file.close()
return head_index, code
head_index = head_index + 1
print("not jpg, png, gif")
return -1, -1
def decode_dat(file_path, out_path):
"""
解密文件并生成图片
:param file_path: dat文件路径
:return:
"""
file_type, decode_code = get_code(file_path)
if decode_code == -1:
return
if file_type == 1:
pic_name = file_path.split("\\")[-1][:-4] + ".jpg"
elif file_type == 3:
pic_name = file_path[:-4] + ".png"
elif file_type == 5:
pic_name = file_path[:-4] + ".gif"
else:
pic_name = file_path[:-4] + ".jpg"
dat_file = open(file_path, "rb")
file_outpath = os.path.join(out_path, pic_name)
print(pic_name)
print(file_outpath)
pic_write = open(file_outpath, "wb")
for dat_data in dat_file:
for dat_byte in dat_data:
pic_data = dat_byte ^ decode_code
pic_write.write(bytes([pic_data]))
print(pic_name + "完成")
dat_file.close()
pic_write.close()
def find_datfile(dir_path, out_path):
"""
获取dat文件目录下所有的文件
:param dir_path: dat文件目录
:return:
"""
files_list = os.listdir(dir_path)
for file_name in files_list:
file_path = dir_path + "\\" + file_name
decode_dat(file_path, out_path)
if __name__ == "__main__":
path = r"D:\download\wechat\WeChat Files\wxid_0o18ef858vnu22\FileStorage\MsgAttach\febd8caf62dd403a7212aef63fd55910\Thumb\2023-11"
outpath = "D:\\test"
if not os.path.exists(outpath):
os.mkdir(outpath)
find_datfile(path, outpath)

View File

@ -65,7 +65,7 @@ def read_info(version_list, is_logging=False):
if len(wechat_process) == 0: if len(wechat_process) == 0:
error = "[-] WeChat No Run" error = "[-] WeChat No Run"
if is_logging: print(error) if is_logging: print(error)
return error return -1
for process in wechat_process: for process in wechat_process:
tmp_rd = {} tmp_rd = {}
@ -76,8 +76,9 @@ def read_info(version_list, is_logging=False):
bias_list = version_list.get(tmp_rd['version'], None) bias_list = version_list.get(tmp_rd['version'], None)
if not isinstance(bias_list, list): if not isinstance(bias_list, list):
error = f"[-] WeChat Current Version {tmp_rd['version']} Is Not Supported" error = f"[-] WeChat Current Version {tmp_rd['version']} Is Not Supported"
if is_logging: print(error) if is_logging:
return error print(error)
return -2
wechat_base_address = 0 wechat_base_address = 0
for module in process.memory_maps(grouped=False): for module in process.memory_maps(grouped=False):
@ -86,8 +87,9 @@ def read_info(version_list, is_logging=False):
break break
if wechat_base_address == 0: if wechat_base_address == 0:
error = f"[-] WeChat WeChatWin.dll Not Found" error = f"[-] WeChat WeChatWin.dll Not Found"
if is_logging: print(error) if is_logging:
return error print(error)
return -3
Handle = ctypes.windll.kernel32.OpenProcess(0x1F0FFF, False, process.pid) Handle = ctypes.windll.kernel32.OpenProcess(0x1F0FFF, False, process.pid)
@ -128,7 +130,7 @@ def get_info():
with open(VERSION_LIST_PATH, "r", encoding="utf-8") as f: with open(VERSION_LIST_PATH, "r", encoding="utf-8") as f:
VERSION_LIST = json.load(f) VERSION_LIST = json.load(f)
result = read_info(VERSION_LIST) # 读取微信信息 result = read_info(VERSION_LIST, True) # 读取微信信息
return result return result

View File

@ -55,6 +55,10 @@ def singleton(cls):
class MePC: class MePC:
def __init__(self): def __init__(self):
self.avatar = QPixmap(Icon.Default_avatar_path) self.avatar = QPixmap(Icon.Default_avatar_path)
self.wxid = ''
self.wx_dir = ''
self.name = ''
self.mobile = ''
def set_avatar(self, img_bytes): def set_avatar(self, img_bytes):
if not img_bytes: if not img_bytes:

View File

@ -3,6 +3,7 @@ from PyQt5.QtGui import QIcon
class Icon: class Icon:
Default_avatar_path = './app/data/icons/default_avatar.svg' Default_avatar_path = './app/data/icons/default_avatar.svg'
Default_image_path = './app/data/icons/404.png'
MainWindow_Icon = QIcon('./app/data/icons/logo.svg') MainWindow_Icon = QIcon('./app/data/icons/logo.svg')
Default_avatar = QIcon(Default_avatar_path) Default_avatar = QIcon(Default_avatar_path)
Output = QIcon('./app/data/icons/output.svg') Output = QIcon('./app/data/icons/output.svg')

View File

@ -1,9 +1,12 @@
import traceback
from PyQt5.QtCore import QThread, pyqtSignal from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QHBoxLayout from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QHBoxLayout
from app.DataBase import msg from app.DataBase import msg, hard_link
from app.components.bubble_message import BubbleMessage, ChatWidget, Notice from app.components.bubble_message import BubbleMessage, ChatWidget, Notice
from app.person import MePC from app.person import MePC
from app.util import get_abs_path
class ChatInfo(QWidget): class ChatInfo(QWidget):
@ -84,7 +87,7 @@ class ChatInfo(QWidget):
is_send = message[4] is_send = message[4]
avatar = MePC().avatar if is_send else self.contact.avatar avatar = MePC().avatar if is_send else self.contact.avatar
timestamp = message[5] timestamp = message[5]
if type_ == 1 or type_ == 3: if type_ == 1:
if self.is_5_min(timestamp): if self.is_5_min(timestamp):
time_message = Notice(self.last_str_time) time_message = Notice(self.last_str_time)
self.last_str_time = str_time self.last_str_time = str_time
@ -96,8 +99,23 @@ class ChatInfo(QWidget):
is_send is_send
) )
self.chat_window.add_message_item(bubble_message, 0) self.chat_window.add_message_item(bubble_message, 0)
elif type_ == 3:
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 = hard_link.get_image(content=str_content, thumb=False)
image_path = get_abs_path(image_path)
bubble_message = BubbleMessage(
image_path,
avatar,
type_,
is_send
)
self.chat_window.add_message_item(bubble_message, 0)
except: except:
print(message) print(message)
traceback.print_exc()
class ShowChatThread(QThread): class ShowChatThread(QThread):

View File

@ -85,6 +85,10 @@ class MainWinController(QMainWindow, mainwindow.Ui_MainWindow):
wxid = dic.get('wxid') wxid = dic.get('wxid')
if wxid: if wxid:
me = MePC() me = MePC()
me.wxid = dic.get('wxid')
me.name = dic.get('name')
me.mobile = dic.get('mobile')
me.wx_dir = dic.get('wx_dir')
self.set_my_info(wxid) self.set_my_info(wxid)
else: else:
QMessageBox.information( QMessageBox.information(
@ -158,11 +162,6 @@ class MainWinController(QMainWindow, mainwindow.Ui_MainWindow):
self.avatar.scaled(60, 60) self.avatar.scaled(60, 60)
me = MePC() me = MePC()
me.set_avatar(img_bytes) me.set_avatar(img_bytes)
dic = {
'wxid': wxid
}
with open('./app/data/info.json', 'w', encoding='utf-8') as f:
f.write(json.dumps(dic))
self.myavatar.setScaledContents(True) self.myavatar.setScaledContents(True)
self.myavatar.setPixmap(self.avatar) self.myavatar.setPixmap(self.avatar)

View File

@ -53,14 +53,14 @@ class Ui_Dialog(object):
self.lineEdit = QtWidgets.QLineEdit(Dialog) self.lineEdit = QtWidgets.QLineEdit(Dialog)
self.lineEdit.setStyleSheet("background:transparent;\n" self.lineEdit.setStyleSheet("background:transparent;\n"
"\n" "\n"
"border-radius:5px;\n" " border-radius:5px;\n"
"border-top: 0px solid #b2e281;\n" " border-top: 0px solid #b2e281;\n"
"border-bottom: 2px solid black;\n" " border-bottom: 2px solid black;\n"
"border-right: 0px solid #b2e281;\n" " border-right: 0px solid #b2e281;\n"
"border-left: 0px solid #b2e281;\n" " border-left: 0px solid #b2e281;\n"
"\n" "\n"
" \n" "\n"
"border-style:outset\n" " border-style:outset\n"
" ") " ")
self.lineEdit.setFrame(False) self.lineEdit.setFrame(False)
self.lineEdit.setObjectName("lineEdit") self.lineEdit.setObjectName("lineEdit")
@ -72,7 +72,7 @@ class Ui_Dialog(object):
self.label_6.setObjectName("label_6") self.label_6.setObjectName("label_6")
self.gridLayout.addWidget(self.label_6, 5, 0, 1, 1) self.gridLayout.addWidget(self.label_6, 5, 0, 1, 1)
self.label_key = QtWidgets.QLabel(Dialog) self.label_key = QtWidgets.QLabel(Dialog)
self.label_key.setMaximumSize(QtCore.QSize(200, 16777215)) self.label_key.setMaximumSize(QtCore.QSize(400, 16777215))
self.label_key.setText("") self.label_key.setText("")
self.label_key.setObjectName("label_key") self.label_key.setObjectName("label_key")
self.gridLayout.addWidget(self.label_key, 5, 1, 1, 1) self.gridLayout.addWidget(self.label_key, 5, 1, 1, 1)
@ -101,7 +101,7 @@ class Ui_Dialog(object):
self.label_8.setObjectName("label_8") self.label_8.setObjectName("label_8")
self.gridLayout.addWidget(self.label_8, 6, 0, 1, 1) self.gridLayout.addWidget(self.label_8, 6, 0, 1, 1)
self.label_db_dir = QtWidgets.QLabel(Dialog) self.label_db_dir = QtWidgets.QLabel(Dialog)
self.label_db_dir.setMaximumSize(QtCore.QSize(200, 300)) self.label_db_dir.setMaximumSize(QtCore.QSize(400, 300))
self.label_db_dir.setText("") self.label_db_dir.setText("")
self.label_db_dir.setObjectName("label_db_dir") self.label_db_dir.setObjectName("label_db_dir")
self.gridLayout.addWidget(self.label_db_dir, 6, 1, 1, 1) self.gridLayout.addWidget(self.label_db_dir, 6, 1, 1, 1)

View File

@ -127,7 +127,7 @@
<widget class="QLabel" name="label_key"> <widget class="QLabel" name="label_key">
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
<width>200</width> <width>400</width>
<height>16777215</height> <height>16777215</height>
</size> </size>
</property> </property>
@ -189,7 +189,7 @@
<widget class="QLabel" name="label_db_dir"> <widget class="QLabel" name="label_db_dir">
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
<width>200</width> <width>400</width>
<height>300</height> <height>300</height>
</size> </size>
</property> </property>

View File

@ -1,3 +1,4 @@
import json
import os.path import os.path
import time import time
import traceback import traceback
@ -35,11 +36,13 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog):
def get_info(self): def get_info(self):
try: try:
result = get_wx_info.get_info() result = get_wx_info.get_info()
print(result)
if result == -1: if result == -1:
QMessageBox.critical(self, "错误", "请登录微信") QMessageBox.critical(self, "错误", "请登录微信")
elif result == -2: elif result == -2:
QMessageBox.critical(self, "错误", "微信版本不匹配\n请更新微信版本为:3.9.8.15") QMessageBox.critical(self, "错误", "微信版本不匹配\n请更新微信版本为:3.9.8.15")
# print(result) elif result == -3:
QMessageBox.critical(self, "错误", "WeChat WeChatWin.dll Not Found")
else: else:
self.ready = True self.ready = True
self.info = result[0] self.info = result[0]
@ -52,7 +55,7 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog):
self.lineEdit.setFocus() self.lineEdit.setFocus()
self.checkBox.setChecked(True) self.checkBox.setChecked(True)
self.get_wxidSignal.emit(self.info['wxid']) self.get_wxidSignal.emit(self.info['wxid'])
if self.wx_dir and os.path.exists(os.path.join(self.wx_dir, self.info['wxid'])): if self.wx_dir and os.path.exists(os.path.join(self.wx_dir)):
self.label_ready.setText('已就绪') self.label_ready.setText('已就绪')
except Exception as e: except Exception as e:
print(e) print(e)
@ -71,7 +74,11 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog):
directory = QtWidgets.QFileDialog.getExistingDirectory( directory = QtWidgets.QFileDialog.getExistingDirectory(
self, "选取微信安装目录——能看到All Users文件夹", self, "选取微信安装目录——能看到All Users文件夹",
"C:/") # 起始路径 "C:/") # 起始路径
if directory: db_dir = os.path.join(directory, 'Msg')
if not os.path.exists(db_dir):
QMessageBox.critical(self, "错误", "文件夹选择错误\n一般以wxid_xxx结尾")
return
self.label_db_dir.setText(directory) self.label_db_dir.setText(directory)
self.wx_dir = directory self.wx_dir = directory
self.checkBox_2.setChecked(True) self.checkBox_2.setChecked(True)
@ -88,11 +95,12 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog):
if self.lineEdit.text() == 'None': if self.lineEdit.text() == 'None':
QMessageBox.critical(self, "错误", "请填入wxid") QMessageBox.critical(self, "错误", "请填入wxid")
return return
db_dir = os.path.join(self.wx_dir, 'Msg')
if self.ready: if self.ready:
if not os.path.exists(os.path.join(self.wx_dir, self.info['wxid'])): if not os.path.exists(db_dir):
QMessageBox.critical(self, "错误", "文件夹选择错误\n一般以WeChat Files结尾") QMessageBox.critical(self, "错误", "文件夹选择错误\n一般以wxid_xxx结尾")
return return
db_dir = os.path.join(self.wx_dir, self.info['wxid'], 'Msg')
self.thread2 = DecryptThread(db_dir, self.info['key']) self.thread2 = DecryptThread(db_dir, self.info['key'])
self.thread2.maxNumSignal.connect(self.setProgressBarMaxNum) self.thread2.maxNumSignal.connect(self.setProgressBarMaxNum)
self.thread2.signal.connect(self.progressBar_view) self.thread2.signal.connect(self.progressBar_view)
@ -103,6 +111,7 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog):
# print("enter clicked") # print("enter clicked")
# 中间可以添加处理逻辑 # 中间可以添加处理逻辑
# QMessageBox.about(self, "解密成功", "数据库文件存储在app/DataBase/Msg文件夹下") # QMessageBox.about(self, "解密成功", "数据库文件存储在app/DataBase/Msg文件夹下")
self.DecryptSignal.emit('ok') self.DecryptSignal.emit('ok')
# self.close() # self.close()
@ -121,6 +130,14 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog):
def btnExitClicked(self): def btnExitClicked(self):
# print("Exit clicked") # print("Exit clicked")
dic = {
'wxid': self.info['wxid'],
'wx_dir': self.wx_dir,
'name': self.info['name'],
'mobile': self.info['mobile']
}
with open('./app/data/info.json', 'w', encoding='utf-8') as f:
f.write(json.dumps(dic))
self.DecryptSignal.emit('ok') self.DecryptSignal.emit('ok')
self.close() self.close()

View File

@ -0,0 +1 @@
from .path import get_abs_path

11
app/util/path.py Normal file
View File

@ -0,0 +1,11 @@
import os
from app.person import MePC
def get_abs_path(path):
return os.path.join(os.getcwd(), 'app/data/icons/404.png')
if path:
return os.path.join(MePC().wx_dir, path)
else:
return os.path.join(os.getcwd(), 'app/data/icons/404.png')

BIN
doc/images/path_select.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -40,7 +40,8 @@ pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
![](./images/setting.png) ![](./images/setting.png)
点击**设置微信路径**按钮,选择该文件夹路径 点击**设置微信路径**按钮选择该文件夹路径下的带有wxid_xxx的路径
![](./images/path_select.png)
5. 获取到密钥和微信路径之后点击开始解密 5. 获取到密钥和微信路径之后点击开始解密

View File

@ -88,7 +88,9 @@ pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
可以到微信->设置->文件管理查看 可以到微信->设置->文件管理查看
![](./doc/images/setting.png) ![](./doc/images/setting.png)
点击**设置微信路径**按钮,选择该文件夹路径 点击**设置微信路径**按钮选择该文件夹路径下的带有wxid_xxx的路径
![](./doc/images/path_select.png)
5. 获取到密钥和微信路径之后点击开始解密 5. 获取到密钥和微信路径之后点击开始解密
6. 解密后的数据库文件保存在./app/DataBase/Msg路径下 6. 解密后的数据库文件保存在./app/DataBase/Msg路径下