Merge branch 'LC044:master' into master

This commit is contained in:
STDquantum 2023-12-17 10:54:30 +08:00 committed by GitHub
commit f1c0ba2678
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 11940 additions and 10554 deletions

View File

@ -15,9 +15,20 @@ from .media_msg import MediaMsg
from .misc import Misc
from .msg import Msg
from .msg import MsgType
misc_db = Misc()
msg_db = Msg()
micro_msg_db = MicroMsg()
hard_link_db = HardLink()
media_msg_db = MediaMsg()
__all__ = ["data", 'output', 'misc_db', 'micro_msg_db', 'msg_db', 'hard_link_db','MsgType', "media_msg_db"]
def close_db():
misc_db.close()
msg_db.close()
micro_msg_db.close()
hard_link_db.close()
media_msg_db.close()
__all__ = ['output', 'misc_db', 'micro_msg_db', 'msg_db', 'hard_link_db', 'MsgType', "media_msg_db"]

View File

@ -100,7 +100,17 @@ class MediaMsg:
return transtext
except:
return ""
def close(self):
if self.open_flag:
try:
lock.acquire(True)
self.open_flag = False
self.DB.close()
finally:
lock.release()
def __del__(self):
self.close()
if __name__ == '__main__':
db_path = './Msg/MediaMSG.db'

View File

@ -3,8 +3,6 @@ import sqlite3
import threading
lock = threading.Lock()
DB = None
cursor = None
db_path = "./app/Database/Msg/MicroMsg.db"
@ -22,61 +20,6 @@ def singleton(cls):
def is_database_exist():
return os.path.exists(db_path)
lockMSG = threading.Lock()
DBMSG = None
cursorMSG = None
db_msg_path = "./app/Database/Msg/MSG.db"
@singleton
class MicroMSGMsg:
def __init__(self):
self.DBMSG = None
self.cursorMSG = None
self.open_flag = False
self.init_database()
def init_database(self):
if not self.open_flag:
if os.path.exists(db_msg_path):
self.DBMSG = sqlite3.connect(db_msg_path, check_same_thread=False)
# '''创建游标'''
self.cursorMSG = self.DBMSG.cursor()
self.open_flag = True
if lockMSG.locked():
lockMSG.release()
def get_contact(self, contacts):
if not self.open_flag:
return None
try:
lockMSG.acquire(True)
sql = '''select StrTalker, MAX(CreateTime) from MSG group by StrTalker'''
self.cursorMSG.execute(sql)
res = self.cursorMSG.fetchall()
res = {StrTalker: CreateTime for StrTalker, CreateTime in res}
contacts = [list(cur_contact) for cur_contact in contacts]
for i, cur_contact in enumerate(contacts):
if cur_contact[0] in res:
contacts[i].append(res[cur_contact[0]])
else:
contacts[i].append(0)
contacts.sort(key=lambda cur_contact: cur_contact[-1], reverse=True)
finally:
lockMSG.release()
return contacts
def close(self):
if self.open_flag:
try:
lockMSG.acquire(True)
self.open_flag = False
self.DBMSG.close()
finally:
lockMSG.release()
def __del__(self):
self.close()
@singleton
class MicroMsg:
@ -116,7 +59,8 @@ class MicroMsg:
result = self.cursor.fetchall()
finally:
lock.release()
return MicroMSGMsg().get_contact(result)
from app.DataBase import msg_db
return msg_db.get_contact(result)
def get_contact_by_username(self, username):
if not self.open_flag:

View File

@ -208,7 +208,25 @@ class Msg:
))
print(keyword,res)
return res
def get_contact(self, contacts):
if not self.open_flag:
return None
try:
lock.acquire(True)
sql = '''select StrTalker, MAX(CreateTime) from MSG group by StrTalker'''
self.cursor.execute(sql)
res = self.cursor.fetchall()
finally:
lock.release()
res = {StrTalker: CreateTime for StrTalker, CreateTime in res}
contacts = [list(cur_contact) for cur_contact in contacts]
for i, cur_contact in enumerate(contacts):
if cur_contact[0] in res:
contacts[i].append(res[cur_contact[0]])
else:
contacts[i].append(0)
contacts.sort(key=lambda cur_contact: cur_contact[-1], reverse=True)
return contacts
def get_messages_by_days(self, username_, is_Annual_report_=False, year_='2023'):
if is_Annual_report_:
sql = '''

View File

@ -383,8 +383,10 @@ class ChildThread(QThread):
if video_path is None and image_path is not None:
image_path = path.get_relative_path(image_path, base_path=f'/data/聊天记录/{self.contact.remark}/image')
image_path = image_path
os.utime(origin_docx_path + image_path[1:], (timestamp, timestamp))
try:
# todo 网络图片问题
print(origin_docx_path + image_path[1:])
os.utime(origin_docx_path + image_path[1:], (timestamp, timestamp))
image_path = image_path.replace('\\', '/')
# print(f"tohtml:---{image_path}")
if self.is_5_min(timestamp):
@ -394,6 +396,10 @@ class ChildThread(QThread):
doc.write(
f'''{{ type:3, text: '{image_path}',is_send:{is_send},avatar_path:'{avatar}'}},'''
)
except:
doc.write(
f'''{{ type:1, text: '视频丢失',is_send:{is_send},avatar_path:'{avatar}'}},'''
)
return
if video_path is None and image_path is None:
return

View File

@ -168,7 +168,7 @@ def get_key(db_path, addr_len):
return key_bytes
def verify_key(key, wx_db_path):
if not wx_db_path:
if wx_db_path == "None":
return True
KEY_SIZE = 32
DEFAULT_PAGESIZE = 4096
@ -287,6 +287,8 @@ def read_info(version_list, is_logging=False):
print("=" * 32)
return result
import os
import sys
@ -297,7 +299,6 @@ def resource_path(relative_path):
return os.path.join(base_path, relative_path)
def get_info(VERSION_LIST):
result = read_info(VERSION_LIST, True) # 读取微信信息
return result

View File

@ -19,6 +19,7 @@
<file>version_list.json</file>
<file>icons/logo.ico</file>
<file>icons/logo.png</file>
<file>icons/logo99.png</file>
<file>icons/tool.svg</file>
<file>icons/home.svg</file>
<file>icons/help.svg</file>

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,8 @@ var = resource_rc.qt_resource_name
class Icon:
Default_avatar_path = ':/icons/icons/default_avatar.svg'
Default_image_path = ':/icons/icons/404.png'
logo_path = ':/icons/icons/logo99.png'
logo_ico_path = ':/icons/icons/logo.ico'
MainWindow_Icon = QIcon(':/icons/icons/logo.svg')
Default_avatar = QIcon(Default_avatar_path)
Output = QIcon(':/icons/icons/output.svg')
@ -30,3 +32,4 @@ class Icon:
Folder_Icon = QIcon(':/icons/icons/folder.svg')
Start_Icon = QIcon(':/icons/icons/start.svg')
Decrypt_Icon = QIcon(':/icons/icons/decrypt.svg')
# Logo_Icon = QIcon(':/icons/icons/logo.png')

View File

@ -11,11 +11,11 @@ import json
import os.path
from PyQt5.QtCore import pyqtSignal, QUrl, Qt, QThread, QSize
from PyQt5.QtGui import QPixmap, QFont, QDesktopServices
from PyQt5.QtGui import QPixmap, QFont, QDesktopServices, QIcon
from PyQt5.QtWidgets import QMainWindow, QLabel, QListWidgetItem, QMessageBox
from app import config
from app.DataBase import msg_db, misc_db, micro_msg_db, hard_link_db
from app.DataBase import msg_db, misc_db, micro_msg_db, hard_link_db, close_db
from app.ui.Icon import Icon
from . import mainwindow
from .chat import ChatWindow
@ -79,7 +79,10 @@ class MainWinController(QMainWindow, mainwindow.Ui_MainWindow):
self.outputThread0 = None
self.outputThread = None
self.setupUi(self)
self.setWindowIcon(Icon.MainWindow_Icon)
# self.setWindowIcon(Icon.MainWindow_Icon)
pixmap = QPixmap(Icon.logo_ico_path)
icon = QIcon(pixmap)
self.setWindowIcon(icon)
self.setStyleSheet(Stylesheet)
self.listWidget.clear()
self.resize(QSize(800, 600))
@ -231,12 +234,19 @@ class MainWinController(QMainWindow, mainwindow.Ui_MainWindow):
QMessageBox.about(self, "解密成功", "请重新启动")
self.close()
def closeEvent(self, event):
reply = QMessageBox.question(self, '确认退出', '确定要退出吗?',
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No)
if reply == QMessageBox.Yes:
close_db()
event.accept()
else:
event.ignore()
def close(self) -> bool:
close_db()
super().close()
misc_db.close()
msg_db.close()
micro_msg_db.close()
hard_link_db.close()
self.contact_window.close()
self.exitSignal.emit(True)

View File

@ -22,6 +22,7 @@ class Ui_MainWindow(object):
font.setWeight(50)
MainWindow.setFont(font)
MainWindow.setStyleSheet("")
MainWindow.setIconSize(QtCore.QSize(50, 24))
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
@ -69,7 +70,7 @@ class Ui_MainWindow(object):
self.horizontalLayout.addWidget(self.stackedWidget)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1280, 28))
self.menubar.setGeometry(QtCore.QRect(0, 0, 1280, 27))
font = QtGui.QFont()
font.setFamily("微软雅黑")
font.setPointSize(12)

View File

@ -7,7 +7,7 @@ from PyQt5.QtCore import pyqtSignal, QThread, QUrl, QFile, QIODevice, QTextStrea
from PyQt5.QtGui import QDesktopServices
from PyQt5.QtWidgets import QWidget, QMessageBox, QFileDialog
from app.DataBase import msg_db, misc_db
from app.DataBase import msg_db, misc_db, media_msg_db, close_db
from app.DataBase.merge import merge_databases, merge_MediaMSG_databases
from app.decrypt import get_wx_info, decrypt
from app.log import logger
@ -131,6 +131,7 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog):
return
if self.info.get('key') == 'none':
QMessageBox.critical(self, "错误", "密钥错误\n请检查微信版本是否为最新和微信路径是否正确")
close_db()
self.label_tip.setVisible(True)
self.label_tip.setText('点我之后没有反应那就多等儿吧,不要再点了')
self.thread2 = DecryptThread(db_dir, self.info['key'])
@ -171,6 +172,7 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog):
'name': self.info['name'],
'mobile': self.info['mobile']
}
try:
os.makedirs('./app/data', exist_ok=True)
with open('./app/data/info.json', 'w', encoding='utf-8') as f:
@ -183,6 +185,8 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog):
# 源数据库文件列表
source_databases = [f"app/DataBase/Msg/MSG{i}.db" for i in range(1, 200)]
import shutil
if os.path.exists(target_database):
os.remove(target_database)
shutil.copy2("app/DataBase/Msg/MSG0.db", target_database) # 使用一个数据库文件作为模板
# 合并数据库
try:
@ -193,6 +197,8 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog):
# 音频数据库文件
target_database = "app/DataBase/Msg/MediaMSG.db"
# 源数据库文件列表
if os.path.exists(target_database):
os.remove(target_database)
source_databases = [f"app/DataBase/Msg/MediaMSG{i}.db" for i in range(1, 200)]
shutil.copy2("app/DataBase/Msg/MediaMSG0.db", target_database) # 使用一个数据库文件作为模板
# 合并数据库

View File

@ -35,23 +35,47 @@ def mkdir(path):
def wx_path():
try:
## 获取当前用户名
user_home = os.environ.get("USERPROFILE")
## 找到3ebffe94.ini配置文件
f = open(user_home + '\\AppData\\Roaming\\Tencent\\WeChat\\All Users\\config\\3ebffe94.ini', encoding='utf-8')
txt = f.read()
f.close()
# 打开Windows注册表
reg_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders")
# 获取“我的文档”路径的注册表键值
documents_path_value = winreg.QueryValueEx(reg_key, "Personal")
# 输出路径
##读取文件将路径放到wx_location变量里
if txt == 'MyDocument:':
wx_location = documents_path_value[0] + '\WeChat Files'
is_w_dir = False
try:
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Tencent\WeChat", 0, winreg.KEY_READ)
value, _ = winreg.QueryValueEx(key, "FileSavePath")
winreg.CloseKey(key)
w_dir = value
is_w_dir = True
except Exception as e:
w_dir = "MyDocument:"
if not is_w_dir:
try:
user_profile = os.environ.get("USERPROFILE")
path_3ebffe94 = os.path.join(user_profile, "AppData", "Roaming", "Tencent", "WeChat", "All Users",
"config",
"3ebffe94.ini")
with open(path_3ebffe94, "r", encoding="utf-8") as f:
w_dir = f.read()
is_w_dir = True
except Exception as e:
w_dir = "MyDocument:"
if w_dir == "MyDocument:":
try:
# 打开注册表路径
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
r"Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders")
documents_path = winreg.QueryValueEx(key, "Personal")[0] # 读取文档实际目录路径
winreg.CloseKey(key) # 关闭注册表
documents_paths = os.path.split(documents_path)
if "%" in documents_paths[0]:
w_dir = os.environ.get(documents_paths[0].replace("%", ""))
w_dir = os.path.join(w_dir, os.path.join(*documents_paths[1:]))
# print(1, w_dir)
else:
wx_location = txt + "\WeChat Files"
return wx_location
w_dir = documents_path
except Exception as e:
profile = os.environ.get("USERPROFILE")
w_dir = os.path.join(profile, "Documents")
msg_dir = os.path.join(w_dir, "WeChat Files")
return msg_dir
except FileNotFoundError:
return '.'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 96 KiB

View File

@ -6,6 +6,7 @@ import traceback
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import *
from app.DataBase import close_db
from app.log import logger
from app.ui import mainview
from app.ui.tool.pc_decrypt import pc_decrypt
@ -51,7 +52,9 @@ class ViewController(QWidget):
def show_success(self):
QMessageBox.about(self, "解密成功", "数据库文件存储在\napp/DataBase/Msg\n文件夹下")
def close(self) -> bool:
close_db()
super().close()
if __name__ == '__main__':
app = QApplication(sys.argv)
font = QFont('微软雅黑', 12) # 使用 Times New Roman 字体,字体大小为 14

View File

@ -16,7 +16,7 @@
</div>
<div align="center">
<a><img src="./doc/images/logo.png" height="240"/></a>
<a><img src="https://blog.lc044.love/static/img/91cadaae3a6f7ee133dafd4f9b5d8680.logo.webp" height="240"/></a>
</div>
<blockquote>
<div style="background-color: #eaf7ea; border-radius: 10px; padding: 20px; position: relative;">
@ -24,6 +24,7 @@
<div style="position: absolute;top: 0;bottom: 0;left: 0;width: 2px;background-color: #000000;"></div>
<h2>前言</h2>
<div style="text-indent: 2em;">
<div align="center"><img src="https://blog.lc044.love/static/img/7393a67ac602f7761e3c7b806165c892.logo99.webp"/></div>
<p style="text-indent:2em;">我深信有意义的不是微信,而是隐藏在对话框背后的一个个<strong>深刻故事</strong>。未来每个人都能拥有AI的陪伴而你的数据能够赋予它有关与你过去的珍贵记忆。我希望每个人都有将自己的生活痕迹👨👩👦👚🥗🏠🚴🧋⛹🛌🛀留存的权利而不是将之遗忘💀。</p>
<p style="text-indent:2em;">AI的发展不仅仅是技术的提升更是情感💞的延续。每一个对话、每一个互动都是生活中独一无二的片段是真实而动人的情感交流。因此我希望AI工作者们能够<strong>善用这些自己的数据</strong>,用于培训独特的、属于个体的人工智能。让<strong>个人AI成为生活中的朋友</strong>,能够理解、记录并分享我们的欢笑、泪水和成长。</p>
<p style="text-indent:2em;">那天AI不再是高不可攀的存在而是融入寻常百姓家的一部分。因为<strong>每个人能拥有自己的AI</strong>,将科技的力量融入生活的方方面面。这是一场关于真情实感的革命,一场让技术变得更加人性化的探索,让我们共同见证未来的美好。</p>
@ -58,21 +59,21 @@
<details>
<img alt="聊天界面" src="doc/images/chat_.png"/>
<img alt="聊天界面" src="https://blog.lc044.love/static/img/b355c16c4a4037deec96fd87efecb74b.image.webp"/>
![image-20230520235220104](doc/images/image-20230520235220104.png)
![image-20230520235351749](https://blog.lc044.love/static/img/beb1500f349ce289406e0e8accac63c0.clipboard-2023-12-12.webp)
![image-20230520235338305](doc/images/image-20230520235338305.png)
![image-20230520235351749](https://blog.lc044.love/static/img/c0e17f989978f843147fa9b8d32949b8.clipboard-2023-12-12.webp)
![image-20230520235351749](doc/images/image-20230520235351749.png)
![image-20230520235351749](https://blog.lc044.love/static/img/47b4476a522f79286b71438bc78a1304.clipboard-2023-12-12.webp)
![image-20230520235400772](doc/images/image-20230520235400772.png)
![image-20230520235400772](https://blog.lc044.love/static/img/526e20e1b8cbcd9a20ea48e242433f0a.clipboard-2023-12-12.webp)
![image-20230520235409112](doc/images/image-20230520235409112.png)
![image-20230520235409112](https://blog.lc044.love/static/img/e74f9747bf9e686ae5bccc678fe0c7ad.clipboard-2023-12-12.webp)
![image-20230520235422128](doc/images/image-20230520235422128.png)
![image-20230520235422128](https://blog.lc044.love/static/img/22edd25fc32a7a11c13d1f36f6421997.clipboard-2023-12-12.webp)
![image-20230520235431091](doc/images/image-20230520235431091.png)
![image-20230520235431091](https://blog.lc044.love/static/img/f3f88205d4596c2d098996d86e5ede7e.clipboard-2023-12-12.webp)
</details>
@ -220,7 +221,8 @@ python main.py
* 我的得力助手:[ChatGPT](https://chat.openai.com/)
---
> \[!IMPORTANT]
>
> 声明:该项目有且仅有一个目的:“留痕”——我的数据我做主,前提是“我的数据”其次才是“我做主”,禁止任何人以任何形式将其用于任何非法用途,对于使用该程序所造成的任何后果,所有创作者不承担任何责任🙄<br>
> 该软件不会对您使用的微信造成任何影响,更不会对他人的微信造成任何影响,不能找回删除的聊天记录,任何企图篡改微信聊天数据的想法都是无稽之谈。本项目所有功能均建立在”前言“的基础之上,基于该项目的所有开发者均不能接受任何有悖于”前言“的功能需求,违者后果自负。
> <br>该软件不存在任何收费,谨防上当受骗
@ -250,6 +252,7 @@ python main.py
感谢以下赞助者的慷慨支持:
- [STDquantum](https://github.com/STDquantum)
- [xuanli](https://github.com/xuanli)
如果您提供赞助并希望出现在赞助者名单中,请在提交赞助时提供您的 GitHub 用户名或其他相关信息。