2023-12-13 22:12:50 +08:00
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
"""
|
|
|
|
|
@File : mainview.py
|
|
|
|
|
@Author : Shuaikang Zhou
|
|
|
|
|
@Time : 2022/12/13 15:07
|
|
|
|
|
@IDE : Pycharm
|
|
|
|
|
@Version : Python3.10
|
|
|
|
|
@comment : 主窗口
|
|
|
|
|
"""
|
|
|
|
|
import json
|
|
|
|
|
import os.path
|
2024-03-02 20:08:04 +08:00
|
|
|
|
import subprocess
|
|
|
|
|
import sys
|
2024-01-15 20:09:31 +08:00
|
|
|
|
import traceback
|
2024-03-04 11:19:08 +08:00
|
|
|
|
import requests
|
|
|
|
|
import time
|
2024-03-02 20:08:04 +08:00
|
|
|
|
from urllib.parse import urljoin
|
2023-12-13 22:12:50 +08:00
|
|
|
|
|
2024-02-14 19:07:44 +08:00
|
|
|
|
from PyQt5.QtCore import pyqtSignal, QThread, QSize, QUrl, Qt
|
2024-01-22 22:21:55 +08:00
|
|
|
|
from PyQt5.QtGui import QPixmap, QIcon, QDesktopServices
|
2024-03-04 11:19:08 +08:00
|
|
|
|
from PyQt5.QtWidgets import QMainWindow, QLabel, QMessageBox
|
|
|
|
|
|
|
|
|
|
from Custom_Widgets.Widgets import *
|
2023-12-13 22:12:50 +08:00
|
|
|
|
|
2024-01-05 21:58:37 +08:00
|
|
|
|
from app.DataBase import misc_db, micro_msg_db, close_db
|
2023-12-13 22:12:50 +08:00
|
|
|
|
from app.ui.Icon import Icon
|
2024-03-04 11:19:08 +08:00
|
|
|
|
from .interface import *
|
2024-02-09 18:00:06 +08:00
|
|
|
|
from app.ui.chat import ChatWindow
|
|
|
|
|
from app.ui.contact import ContactWindow
|
|
|
|
|
from app.ui.tool.tool_window import ToolWindow
|
2024-03-04 11:19:08 +08:00
|
|
|
|
from app.ui.home.home_window import HomeWindow, ReportThread
|
|
|
|
|
from app.ui.tool.setting.setting import ErrorThread
|
|
|
|
|
from app.ui.tool.pc_decrypt.pc_decrypt import DecryptThread, InfoThread
|
|
|
|
|
from app.ui.tool.get_bias_addr.get_bias_addr import BiasThread
|
|
|
|
|
from app.ui.chat.chat_info import ShowChatThread
|
|
|
|
|
from app.ui.chat.chat_window import ShowContactThread, ShowThread
|
|
|
|
|
from app.ui.menu.export import ShowContactExportThread, export_types, export_file_format
|
|
|
|
|
from app.util import path
|
|
|
|
|
from app.config import SERVER_API_URL
|
|
|
|
|
from app.decrypt.get_bias_addr import BiasAddr
|
2024-01-06 13:04:55 +08:00
|
|
|
|
from .menu.export import ExportDialog
|
2024-02-08 20:37:44 +08:00
|
|
|
|
from app.util.exporter.output import Output
|
2023-12-23 19:07:15 +08:00
|
|
|
|
from ..components.QCursorGif import QCursorGif
|
2024-03-04 11:19:08 +08:00
|
|
|
|
from ..config import INFO_FILE_PATH, DB_DIR, about, SERVER_API_URL, version
|
2024-01-15 20:09:31 +08:00
|
|
|
|
from ..log import logger
|
2023-12-30 19:26:24 +08:00
|
|
|
|
from ..person import Me
|
2023-12-13 22:12:50 +08:00
|
|
|
|
|
2024-03-04 11:19:08 +08:00
|
|
|
|
from app.DataBase import micro_msg_db, misc_db
|
|
|
|
|
from app.util.exporter.output import Output
|
|
|
|
|
from app.components import ScrollBar
|
|
|
|
|
from app.components.export_contact_item import ContactQListWidgetItem
|
|
|
|
|
from app.config import OUTPUT_DIR
|
|
|
|
|
from app.person import Contact
|
|
|
|
|
from app.ui.menu.exportUi import Ui_Dialog
|
|
|
|
|
from app.ui.menu.export_time_range import TimeRangeDialog
|
|
|
|
|
|
2024-01-16 21:28:38 +08:00
|
|
|
|
try:
|
2024-03-04 11:19:08 +08:00
|
|
|
|
from app.ui.menu.about_dialog import version, UpdateThread, AboutDialog
|
2024-01-16 21:28:38 +08:00
|
|
|
|
except ModuleNotFoundError:
|
|
|
|
|
logger.error(f'Python版本错误:Python>=3.10,仅支持3.10、3.11、3.12')
|
2024-01-17 21:49:47 +08:00
|
|
|
|
raise ValueError('Python版本错误:Python>=3.10,仅支持3.10、3.11、3.12')
|
2024-01-24 20:02:01 +08:00
|
|
|
|
|
2023-12-13 22:12:50 +08:00
|
|
|
|
|
2024-02-01 21:12:10 +08:00
|
|
|
|
class Avatar(QLabel):
|
|
|
|
|
def __init__(self, parent=None):
|
|
|
|
|
super().__init__(parent)
|
|
|
|
|
|
2024-03-04 11:19:08 +08:00
|
|
|
|
def mouseDoubleClickEvent(self, e):
|
2024-02-01 21:12:10 +08:00
|
|
|
|
super().mouseDoubleClickEvent()
|
|
|
|
|
QDesktopServices.openUrl(QUrl("https://blog.lc044.love/post/7"))
|
|
|
|
|
|
2024-03-04 11:19:08 +08:00
|
|
|
|
def mousePressEvent(self, e):
|
2024-02-01 21:12:10 +08:00
|
|
|
|
super().mousePressEvent(e)
|
|
|
|
|
QDesktopServices.openUrl(QUrl("https://blog.lc044.love/post/7"))
|
|
|
|
|
|
2024-02-21 22:41:11 +08:00
|
|
|
|
|
2024-03-04 11:19:08 +08:00
|
|
|
|
class MainWinController(QMainWindow, Ui_MainWindow):
|
2023-12-13 22:12:50 +08:00
|
|
|
|
exitSignal = pyqtSignal(bool)
|
|
|
|
|
okSignal = pyqtSignal(bool)
|
|
|
|
|
|
2024-03-04 11:19:08 +08:00
|
|
|
|
DecryptSignal = pyqtSignal(bool)
|
|
|
|
|
get_wxidSignal = pyqtSignal(str)
|
|
|
|
|
versionErrorSignal = pyqtSignal(str)
|
|
|
|
|
|
|
|
|
|
def __init__(self, parent=None):
|
2023-12-13 22:12:50 +08:00
|
|
|
|
super(MainWinController, self).__init__(parent)
|
|
|
|
|
self.outputThread0 = None
|
|
|
|
|
self.outputThread = None
|
2024-03-04 11:19:08 +08:00
|
|
|
|
|
2023-12-13 22:12:50 +08:00
|
|
|
|
self.setupUi(self)
|
2024-03-04 11:19:08 +08:00
|
|
|
|
|
2023-12-13 22:12:50 +08:00
|
|
|
|
self.load_flag = False
|
|
|
|
|
self.load_data()
|
|
|
|
|
self.load_num = 0
|
2024-03-04 11:19:08 +08:00
|
|
|
|
|
|
|
|
|
loadJsonStyle(self, self, jsonFiles=["app/ui/style.json"])
|
|
|
|
|
|
|
|
|
|
self.toolBtn.clicked.connect(lambda: self.centerMenuContainer.expandMenu())
|
|
|
|
|
# self.msgBtn.clicked.connect(lambda: self.centerMenuContainer.expandMenu())
|
|
|
|
|
# self.friendBtn.clicked.connect(lambda: self.centerMenuContainer.expandMenu())
|
|
|
|
|
# self.userBtn.clicked.connect(lambda: self.centerMenuContainer.expandMenu())
|
|
|
|
|
|
|
|
|
|
self.msgBtn.clicked.connect(lambda: self.centerMenuContainer.collapseMenu())
|
|
|
|
|
self.friendBtn.clicked.connect(lambda: self.centerMenuContainer.collapseMenu())
|
|
|
|
|
self.userBtn.clicked.connect(lambda: self.centerMenuContainer.collapseMenu())
|
|
|
|
|
self.aboutBtn.clicked.connect(lambda: self.centerMenuContainer.collapseMenu())
|
|
|
|
|
self.settingBtn.clicked.connect(lambda: self.centerMenuContainer.collapseMenu())
|
|
|
|
|
self.helpBtn.clicked.connect(lambda: self.centerMenuContainer.collapseMenu())
|
|
|
|
|
|
|
|
|
|
self.prepare_about()
|
|
|
|
|
|
|
|
|
|
self.version_list = None
|
|
|
|
|
self.versionText.setText(f"留痕-V{version}")
|
|
|
|
|
|
|
|
|
|
self.closeCenterMenuBtn.clicked.connect(lambda: self.centerMenuContainer.collapseMenu())
|
|
|
|
|
self.closeNotificationBtn.clicked.connect(lambda: self.popupNotificationsContainer.collapseMenu())
|
|
|
|
|
|
|
|
|
|
self.exportAllBtn.clicked.connect(self.output)
|
|
|
|
|
self.exportFriendBtn.clicked.connect(self.output)
|
|
|
|
|
|
|
|
|
|
self.updateBtn.clicked.connect(self.update)
|
|
|
|
|
|
|
|
|
|
self.questionBtn.clicked.connect(
|
|
|
|
|
lambda: QDesktopServices.openUrl(QUrl("https://blog.lc044.love/post/7")))
|
|
|
|
|
self.toolQuestionBtn.clicked.connect(
|
|
|
|
|
lambda: QDesktopServices.openUrl(QUrl("https://blog.lc044.love/post/4")))
|
|
|
|
|
self.friendQuestionBtn.clicked.connect(
|
|
|
|
|
lambda: QDesktopServices.openUrl(QUrl("https://blog.lc044.love/post/7")))
|
|
|
|
|
self.msgQuestionBtn.clicked.connect(
|
|
|
|
|
lambda: QDesktopServices.openUrl(QUrl("https://blog.lc044.love/post/7")))
|
|
|
|
|
|
|
|
|
|
self.mainContentsPages.setCurrentWidget(self.aboutMainPage)
|
|
|
|
|
|
|
|
|
|
self.reportBtn.clicked.connect(self.report)
|
|
|
|
|
|
|
|
|
|
self.stopWordBtn.clicked.connect(self.add_stopwords)
|
|
|
|
|
self.newWordBtn.clicked.connect(self.add_new_words)
|
|
|
|
|
self.read_data()
|
|
|
|
|
self.sendErrorBtn.clicked.connect(self.send_error_log)
|
|
|
|
|
|
|
|
|
|
self.setBtnVisible(0)
|
|
|
|
|
self.toolBtn.clicked.connect(lambda: self.setBtnVisible(1))
|
|
|
|
|
self.decryptBtn.clicked.connect(lambda: self.setBtnVisible(1))
|
|
|
|
|
self.decryptBtn2.clicked.connect(lambda: self.setBtnVisible(2))
|
|
|
|
|
self.dataBtn.clicked.connect(lambda: self.setBtnVisible(3))
|
|
|
|
|
self.msgBtn.clicked.connect(lambda: self.setBtnVisible(4))
|
|
|
|
|
self.friendBtn.clicked.connect(lambda: self.setBtnVisible(5))
|
|
|
|
|
self.userBtn.clicked.connect(lambda: self.setBtnVisible(6))
|
|
|
|
|
self.aboutBtn.clicked.connect(lambda: self.setBtnVisible(7))
|
|
|
|
|
self.settingBtn.clicked.connect(lambda: self.setBtnVisible(8))
|
|
|
|
|
self.helpBtn.clicked.connect(lambda: self.setBtnVisible(9))
|
|
|
|
|
|
|
|
|
|
self.startDecryptBtn.clicked.connect(self.decrypt)
|
|
|
|
|
self.getInfoBtn.clicked.connect(self.get_info)
|
|
|
|
|
self.dbDirBtn.clicked.connect(self.select_db_dir)
|
|
|
|
|
self.lineEdit.returnPressed.connect(self.set_wxid)
|
|
|
|
|
self.lineEdit.textChanged.connect(self.set_wxid_)
|
|
|
|
|
|
|
|
|
|
self.startDecryptBiasBtn.clicked.connect(self.get_bias_addr)
|
|
|
|
|
|
|
|
|
|
self.friendView = ContactWindow(parent=self.friendMainPage)
|
|
|
|
|
self.msgView = ChatWindow(parent=self.msgMainPage)
|
|
|
|
|
|
|
|
|
|
self.statisticBtn.clicked.connect(self.statistic)
|
|
|
|
|
self.moodBtn.clicked.connect(self.mood)
|
|
|
|
|
self.userReportBtn.clicked.connect(self.user_report)
|
|
|
|
|
|
|
|
|
|
self.contacts = []
|
|
|
|
|
self.btn_select_all.clicked.connect(self.select_all)
|
|
|
|
|
self.select_all_flag = False
|
|
|
|
|
self.exportBatchBtn.clicked.connect(self.export_data)
|
|
|
|
|
self.comboBox_time.activated.connect(self.set_export_date)
|
|
|
|
|
self.export_choices = {"文本": True, "图片": True, "语音": False, "视频": False, "表情包": False,
|
|
|
|
|
'音乐与音频': False, '分享卡片': False, '文件': False,
|
|
|
|
|
'转账': False, '音视频通话': False,'拍一拍等系统消息': True}
|
|
|
|
|
self.checkBox_word.setEnabled(False)
|
|
|
|
|
self.checkBox_word.setText('Docx(暂时不可用)')
|
|
|
|
|
self.worker = None
|
|
|
|
|
for export_type, default_state in self.export_choices.items():
|
|
|
|
|
checkbox = QCheckBox(export_type)
|
|
|
|
|
checkbox.setObjectName('message_type')
|
|
|
|
|
checkbox.setChecked(default_state)
|
|
|
|
|
self.messageTypeVerticalLayout.addWidget(checkbox)
|
|
|
|
|
spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
|
|
|
|
self.messageTypeVerticalLayout.addItem(spacerItem1)
|
|
|
|
|
self.timer = QTimer(self)
|
|
|
|
|
self.time = 0
|
|
|
|
|
self.total_msg_num = 99999
|
|
|
|
|
self.num = 0
|
|
|
|
|
self.timer.timeout.connect(self.update_elapsed_time)
|
|
|
|
|
self.show_thread = ShowContactExportThread()
|
|
|
|
|
self.show_thread.showSingal.connect(self.show_contact)
|
|
|
|
|
self.show_thread.start()
|
|
|
|
|
self.listWidget.setVerticalScrollBar(ScrollBar())
|
|
|
|
|
self.listWidget.itemClicked.connect(self.setCurrentIndex)
|
|
|
|
|
self.visited = set()
|
|
|
|
|
self.now_index = 0
|
|
|
|
|
self.time_range = None
|
|
|
|
|
|
|
|
|
|
def setBtnVisible(self, index=0):
|
|
|
|
|
self.getInfoBtn.setVisible(False)
|
|
|
|
|
self.dbDirBtn.setVisible(False)
|
|
|
|
|
self.startDecryptBtn.setVisible(False)
|
|
|
|
|
self.startDecryptBiasBtn.setVisible(False)
|
|
|
|
|
self.statisticBtn.setVisible(False)
|
|
|
|
|
self.moodBtn.setVisible(False)
|
|
|
|
|
self.reportBtn.setVisible(False)
|
|
|
|
|
self.userReportBtn.setVisible(False)
|
|
|
|
|
self.exportAllBtn.setVisible(False)
|
|
|
|
|
self.exportBatchBtn.setVisible(False)
|
|
|
|
|
self.exportFriendBtn.setVisible(False)
|
|
|
|
|
self.stopWordBtn.setVisible(False)
|
|
|
|
|
self.newWordBtn.setVisible(False)
|
|
|
|
|
if index == 1:
|
|
|
|
|
self.getInfoBtn.setVisible(True)
|
|
|
|
|
self.dbDirBtn.setVisible(True)
|
|
|
|
|
self.startDecryptBtn.setVisible(True)
|
|
|
|
|
if index == 2:
|
|
|
|
|
self.startDecryptBiasBtn.setVisible(True)
|
|
|
|
|
if index == 3:
|
|
|
|
|
self.exportAllBtn.setVisible(True)
|
|
|
|
|
self.exportBatchBtn.setVisible(True)
|
|
|
|
|
self.exportFriendBtn.setVisible(True)
|
|
|
|
|
if index == 5:
|
|
|
|
|
self.statisticBtn.setVisible(True)
|
|
|
|
|
self.moodBtn.setVisible(True)
|
|
|
|
|
self.userReportBtn.setVisible(True)
|
|
|
|
|
if index == 6:
|
|
|
|
|
self.reportBtn.setVisible(True)
|
|
|
|
|
if index == 8:
|
|
|
|
|
self.stopWordBtn.setVisible(True)
|
|
|
|
|
self.newWordBtn.setVisible(True)
|
|
|
|
|
|
|
|
|
|
def load_data(self):
|
2024-02-17 21:34:46 +08:00
|
|
|
|
if os.path.exists(INFO_FILE_PATH):
|
|
|
|
|
with open(INFO_FILE_PATH, 'r', encoding='utf-8') as f:
|
2023-12-13 22:12:50 +08:00
|
|
|
|
dic = json.loads(f.read())
|
|
|
|
|
wxid = dic.get('wxid')
|
|
|
|
|
if wxid:
|
2023-12-30 19:26:24 +08:00
|
|
|
|
me = Me()
|
2023-12-13 22:12:50 +08:00
|
|
|
|
me.wxid = dic.get('wxid')
|
|
|
|
|
me.name = dic.get('name')
|
2024-02-19 21:29:06 +08:00
|
|
|
|
me.nickName = dic.get('name')
|
|
|
|
|
me.remark = dic.get('name')
|
2023-12-13 22:12:50 +08:00
|
|
|
|
me.mobile = dic.get('mobile')
|
|
|
|
|
me.wx_dir = dic.get('wx_dir')
|
2024-03-01 01:17:47 +08:00
|
|
|
|
me.token = dic.get('token')
|
2023-12-13 22:12:50 +08:00
|
|
|
|
self.set_my_info(wxid)
|
|
|
|
|
self.load_flag = True
|
|
|
|
|
else:
|
2024-01-17 20:04:59 +08:00
|
|
|
|
pass
|
2023-12-13 22:12:50 +08:00
|
|
|
|
|
|
|
|
|
def set_my_info(self, wxid):
|
|
|
|
|
self.avatar = QPixmap()
|
2024-02-29 16:36:32 +08:00
|
|
|
|
img_bytes = misc_db.get_avatar_buffer(wxid)
|
2023-12-13 22:12:50 +08:00
|
|
|
|
if not img_bytes:
|
|
|
|
|
return
|
|
|
|
|
if img_bytes[:4] == b'\x89PNG':
|
|
|
|
|
self.avatar.loadFromData(img_bytes, format='PNG')
|
|
|
|
|
else:
|
|
|
|
|
self.avatar.loadFromData(img_bytes, format='jfif')
|
|
|
|
|
self.avatar.scaled(60, 60)
|
2023-12-29 21:50:17 +08:00
|
|
|
|
contact_info_list = micro_msg_db.get_contact_by_username(wxid)
|
2024-01-30 21:45:28 +08:00
|
|
|
|
if not contact_info_list:
|
|
|
|
|
close_db()
|
|
|
|
|
import shutil
|
|
|
|
|
try:
|
2024-02-17 21:34:46 +08:00
|
|
|
|
shutil.rmtree(DB_DIR)
|
2024-01-30 21:45:28 +08:00
|
|
|
|
except:
|
|
|
|
|
pass
|
2024-03-04 11:19:08 +08:00
|
|
|
|
self.message_error("数据库错误,请删除app文件夹后重启电脑再运行软件")
|
2024-01-30 21:45:28 +08:00
|
|
|
|
return
|
2023-12-30 19:26:24 +08:00
|
|
|
|
me = Me()
|
2023-12-13 22:12:50 +08:00
|
|
|
|
me.set_avatar(img_bytes)
|
2023-12-29 21:50:17 +08:00
|
|
|
|
me.smallHeadImgUrl = contact_info_list[7]
|
2023-12-13 22:12:50 +08:00
|
|
|
|
self.myavatar.setScaledContents(True)
|
|
|
|
|
self.myavatar.setPixmap(self.avatar)
|
|
|
|
|
|
|
|
|
|
def output(self):
|
2024-03-04 11:19:08 +08:00
|
|
|
|
if self.sender() == self.exportAllBtn:
|
2023-12-13 22:12:50 +08:00
|
|
|
|
self.outputThread = Output(None, type_=Output.CSV_ALL)
|
|
|
|
|
self.outputThread.okSignal.connect(
|
2024-03-04 11:19:08 +08:00
|
|
|
|
lambda x: self.message_notification('聊天记录导出成功'))
|
2023-12-13 22:12:50 +08:00
|
|
|
|
self.outputThread.start()
|
2024-03-04 11:19:08 +08:00
|
|
|
|
elif self.sender() == self.exportFriendBtn:
|
2023-12-13 22:12:50 +08:00
|
|
|
|
self.outputThread = Output(None, type_=Output.CONTACT_CSV)
|
|
|
|
|
self.outputThread.okSignal.connect(
|
2024-03-04 11:19:08 +08:00
|
|
|
|
lambda x: self.message_notification('联系人导出成功'))
|
2023-12-13 22:12:50 +08:00
|
|
|
|
self.outputThread.start()
|
2024-03-04 11:19:08 +08:00
|
|
|
|
|
|
|
|
|
def countLine(self, text):
|
|
|
|
|
text = text.replace('<br>', '\n')
|
|
|
|
|
lines = len(text.split('\n'))
|
|
|
|
|
return lines - 1
|
|
|
|
|
|
|
|
|
|
def message_notification(self, msg):
|
|
|
|
|
self.notificationType.setText("通知")
|
|
|
|
|
self.notificationContent.setText(msg)
|
|
|
|
|
self.popupNotificationsContainer.setFixedHeight(104 + 22 * self.countLine(msg))
|
|
|
|
|
self.popupNotificationsContainer.expandMenu()
|
|
|
|
|
|
|
|
|
|
def message_error(self, msg):
|
|
|
|
|
self.notificationType.setText("错误")
|
|
|
|
|
self.notificationContent.setText(msg)
|
|
|
|
|
self.popupNotificationsContainer.setFixedHeight(104 + 22 * self.countLine(msg))
|
|
|
|
|
self.popupNotificationsContainer.expandMenu()
|
|
|
|
|
|
|
|
|
|
def prepare_about(self):
|
|
|
|
|
self.aboutText.setText(about)
|
|
|
|
|
self.aboutText.adjustSize()
|
2023-12-13 22:12:50 +08:00
|
|
|
|
|
2024-01-17 20:20:32 +08:00
|
|
|
|
def update(self):
|
|
|
|
|
self.update_thread = UpdateThread()
|
|
|
|
|
self.update_thread.updateSignal.connect(self.show_update)
|
|
|
|
|
self.update_thread.start()
|
|
|
|
|
|
|
|
|
|
def show_update(self, update_info):
|
|
|
|
|
if not update_info.get('update_available'):
|
2024-03-04 11:19:08 +08:00
|
|
|
|
self.message_notification("当前已是最新版本")
|
|
|
|
|
# return
|
|
|
|
|
detail = f'''当前版本:{version},最新版本:{update_info.get('latest_version')}
|
|
|
|
|
更新内容:{update_info.get('description')}
|
|
|
|
|
<a href='{update_info.get('download_url')}'>点击下载</a>
|
2024-01-17 20:20:32 +08:00
|
|
|
|
'''
|
2024-03-04 11:19:08 +08:00
|
|
|
|
self.message_notification(detail)
|
2023-12-13 22:12:50 +08:00
|
|
|
|
|
|
|
|
|
def decrypt_success(self):
|
2024-03-04 11:19:08 +08:00
|
|
|
|
self.message_notification("解密成功,请重新启动")
|
2023-12-13 22:12:50 +08:00
|
|
|
|
self.close()
|
|
|
|
|
|
|
|
|
|
def close(self) -> bool:
|
2023-12-16 20:06:43 +08:00
|
|
|
|
close_db()
|
2024-01-15 19:51:08 +08:00
|
|
|
|
super().close()
|
2023-12-13 22:12:50 +08:00
|
|
|
|
self.exitSignal.emit(True)
|
2024-03-02 23:04:15 +08:00
|
|
|
|
|
2024-03-04 11:19:08 +08:00
|
|
|
|
# 我的年度报告相关
|
|
|
|
|
|
|
|
|
|
def report(self):
|
|
|
|
|
time_range = ['2023-01-01 00:00:00', '2024-02-10 00:00:00']
|
|
|
|
|
self.reportProgress.setText('正在生成报告\n如需查看报告请点击访问 http://127.0.0.1:21314/ 查看')
|
|
|
|
|
self.report_thread = ReportThread(Me(), time_range)
|
|
|
|
|
self.report_thread.start()
|
|
|
|
|
QDesktopServices.openUrl(QUrl(f"http://127.0.0.1:21314/"))
|
|
|
|
|
|
|
|
|
|
# 设置相关
|
|
|
|
|
|
|
|
|
|
def read_data(self):
|
|
|
|
|
os.makedirs('./app/data', exist_ok=True)
|
|
|
|
|
stopwords = ['裂开', '苦涩', '叹气', '凋谢', '让我看看', '酷', '奋斗', '疑问', '擦汗', '抠鼻', '鄙视', '勾引',
|
|
|
|
|
'奸笑', '嘿哈', '捂脸', '机智', '加油', '吃瓜', '尴尬', '炸弹', '旺柴']
|
|
|
|
|
new_words = ['YYDS', '666', '显眼包', '遥遥领先']
|
|
|
|
|
if os.path.exists('./app/data/stopwords.txt'):
|
|
|
|
|
with open('./app/data/stopwords.txt', 'r', encoding='utf-8') as f:
|
|
|
|
|
stopwords = set(f.read().splitlines())
|
|
|
|
|
self.stopWordTextEdit.setPlainText(' '.join(stopwords))
|
|
|
|
|
else:
|
|
|
|
|
self.stopWordTextEdit.setPlainText(' '.join(stopwords))
|
|
|
|
|
stopwords = '\n'.join(stopwords)
|
|
|
|
|
with open('./app/data/stopwords.txt', 'w', encoding='utf-8') as f:
|
|
|
|
|
f.write(stopwords)
|
|
|
|
|
if os.path.exists('./app/data/new_words.txt'):
|
|
|
|
|
with open('./app/data/new_words.txt', 'r', encoding='utf-8') as f:
|
|
|
|
|
stopwords = set(f.read().splitlines())
|
|
|
|
|
self.newWordTextEdit.setPlainText(' '.join(new_words))
|
|
|
|
|
else:
|
|
|
|
|
self.newWordTextEdit.setPlainText(' '.join(new_words))
|
|
|
|
|
stopwords = '\n'.join(stopwords)
|
|
|
|
|
with open('./app/data/new_words.txt', 'w', encoding='utf-8') as f:
|
|
|
|
|
f.write(stopwords)
|
|
|
|
|
|
|
|
|
|
def add_stopwords(self):
|
|
|
|
|
text = self.stopWordTextEdit.toPlainText()
|
|
|
|
|
stopwords = '\n'.join(text.split())
|
|
|
|
|
with open('./app/data/stopwords.txt', 'w', encoding='utf-8') as f:
|
|
|
|
|
f.write(stopwords)
|
|
|
|
|
self.message_notification("停用词添加成功")
|
|
|
|
|
|
|
|
|
|
def add_new_words(self):
|
|
|
|
|
text = self.newWordTextEdit.toPlainText()
|
|
|
|
|
new_words = '\n'.join(text.split())
|
|
|
|
|
with open('./app/data/new_words.txt', 'w', encoding='utf-8') as f:
|
|
|
|
|
f.write(new_words)
|
|
|
|
|
self.message_notification("自定义词添加成功")
|
|
|
|
|
|
|
|
|
|
def send_error_log(self):
|
|
|
|
|
self.send_thread = ErrorThread()
|
|
|
|
|
self.send_thread.signal.connect(self.show_resp)
|
|
|
|
|
self.send_thread.start()
|
|
|
|
|
|
|
|
|
|
def show_resp(self, msg):
|
|
|
|
|
if msg.get('code') == 200:
|
|
|
|
|
self.message_notification(f"日志发送成功\n{msg.get('message')}")
|
|
|
|
|
else:
|
|
|
|
|
self.message_notification(f"{msg.get('code')}:{msg.get('errmsg')}")
|
|
|
|
|
|
|
|
|
|
# 自动解密相关
|
|
|
|
|
|
|
|
|
|
def get_info(self):
|
|
|
|
|
self.get_info_thread = InfoThread(self.version_list)
|
|
|
|
|
self.get_info_thread.signal.connect(self.set_info)
|
|
|
|
|
self.get_info_thread.start()
|
|
|
|
|
|
|
|
|
|
def set_info(self, result):
|
|
|
|
|
if result[0] == -1:
|
|
|
|
|
self.message_error("请登录微信")
|
|
|
|
|
elif result[0] == -2:
|
|
|
|
|
self.versionErrorSignal.emit(result[1])
|
|
|
|
|
self.message_error("微信版本不匹配\n请手动填写信息")
|
|
|
|
|
|
|
|
|
|
elif result[0] == -3:
|
|
|
|
|
self.message_error("WeChat WeChatWin.dll Not Found")
|
|
|
|
|
elif result[0] == -10086:
|
|
|
|
|
self.message_error("未知错误,请收集错误信息")
|
|
|
|
|
else:
|
|
|
|
|
self.ready = True
|
|
|
|
|
self.info = result[0]
|
|
|
|
|
self.label_key.setText(self.info['key'])
|
|
|
|
|
self.lineEdit.setText(self.info['wxid'])
|
|
|
|
|
self.label_name.setText(self.info['name'])
|
|
|
|
|
self.label_phone.setText(self.info['mobile'])
|
|
|
|
|
self.label_pid.setText(str(self.info['pid']))
|
|
|
|
|
self.label_version.setText(self.info['version'])
|
|
|
|
|
self.lineEdit.setFocus()
|
|
|
|
|
self.getInfoCheckBox.setCheckable(True)
|
|
|
|
|
self.getInfoCheckBox.setChecked(True)
|
|
|
|
|
self.get_wxidSignal.emit(self.info['wxid'])
|
|
|
|
|
directory = os.path.join(path.wx_path(), self.info['wxid'])
|
|
|
|
|
if os.path.exists(directory):
|
|
|
|
|
self.label_db_dir.setText(directory)
|
|
|
|
|
self.wx_dir = directory
|
|
|
|
|
self.setPathCheckBox.setCheckable(True)
|
|
|
|
|
self.setPathCheckBox.setChecked(True)
|
|
|
|
|
self.ready = True
|
|
|
|
|
if self.ready:
|
|
|
|
|
self.label_ready.setText('已就绪')
|
|
|
|
|
if self.wx_dir and os.path.exists(os.path.join(self.wx_dir)):
|
|
|
|
|
self.label_ready.setText('已就绪')
|
|
|
|
|
|
|
|
|
|
def set_wxid_(self):
|
|
|
|
|
self.info['wxid'] = self.lineEdit.text()
|
|
|
|
|
|
|
|
|
|
def set_wxid(self):
|
|
|
|
|
self.info['wxid'] = self.lineEdit.text()
|
|
|
|
|
self.message_notification("wxid修改成功")
|
|
|
|
|
|
|
|
|
|
def select_db_dir(self):
|
|
|
|
|
directory = QFileDialog.getExistingDirectory(
|
|
|
|
|
self, "选取微信文件保存目录——能看到Msg文件夹",
|
|
|
|
|
path.wx_path()
|
|
|
|
|
)
|
|
|
|
|
db_dir = os.path.join(directory, 'Msg')
|
|
|
|
|
if not os.path.exists(db_dir):
|
|
|
|
|
self.message_error("文件夹选择错误\n一般以wxid_xxx结尾")
|
|
|
|
|
return
|
2024-03-02 23:04:15 +08:00
|
|
|
|
|
2024-03-04 11:19:08 +08:00
|
|
|
|
self.label_db_dir.setText(directory)
|
|
|
|
|
self.wx_dir = directory
|
|
|
|
|
self.setPathCheckBox.setCheckable(True)
|
|
|
|
|
self.setPathCheckBox.setChecked(True)
|
|
|
|
|
if self.ready:
|
|
|
|
|
self.label_ready.setText('已就绪')
|
2024-03-02 20:08:04 +08:00
|
|
|
|
|
2024-03-04 11:19:08 +08:00
|
|
|
|
def decrypt(self):
|
|
|
|
|
if not self.ready:
|
|
|
|
|
self.message_error("请先获取信息")
|
|
|
|
|
return
|
|
|
|
|
if not self.wx_dir:
|
|
|
|
|
self.message_error("请先选择微信安装路径")
|
|
|
|
|
return
|
|
|
|
|
if self.lineEdit.text() == 'None':
|
|
|
|
|
self.message_error("请填入wxid")
|
|
|
|
|
return
|
|
|
|
|
db_dir = os.path.join(self.wx_dir, 'Msg')
|
|
|
|
|
if self.ready:
|
|
|
|
|
if not os.path.exists(db_dir):
|
|
|
|
|
self.message_error("文件夹选择错误\n一般以wxid_xxx结尾")
|
|
|
|
|
return
|
|
|
|
|
if self.info.get('key') == 'None':
|
|
|
|
|
self.message_error("密钥错误\n请查看教程解决相关问题")
|
|
|
|
|
close_db()
|
|
|
|
|
self.thread2 = DecryptThread(db_dir, self.info['key'])
|
|
|
|
|
self.thread2.maxNumSignal.connect(self.setProgressBarMaxNum)
|
|
|
|
|
self.thread2.signal.connect(self.progressBar_view)
|
|
|
|
|
self.thread2.okSignal.connect(self.btnExitClicked)
|
|
|
|
|
self.thread2.errorSignal.connect(
|
|
|
|
|
lambda x: self.message_error("错误\n请检查微信版本是否为最新和微信路径是否正确\n或者关闭微信多开")
|
|
|
|
|
)
|
|
|
|
|
self.thread2.start()
|
|
|
|
|
|
|
|
|
|
def btnEnterClicked(self):
|
|
|
|
|
self.progressBar_view(self.max_val)
|
|
|
|
|
self.DecryptSignal.emit(True)
|
|
|
|
|
|
|
|
|
|
def setProgressBarMaxNum(self, max_val):
|
|
|
|
|
self.max_val = max_val
|
|
|
|
|
self.progressBar.setRange(0, max_val)
|
|
|
|
|
|
|
|
|
|
def progressBar_view(self, value):
|
|
|
|
|
self.progressBar.setProperty('value', value)
|
|
|
|
|
|
|
|
|
|
def btnExitClicked(self):
|
|
|
|
|
dic = {
|
|
|
|
|
'wxid': self.info['wxid'],
|
|
|
|
|
'wx_dir': self.wx_dir,
|
|
|
|
|
'name': self.info['name'],
|
|
|
|
|
'mobile': self.info['mobile']
|
|
|
|
|
}
|
2024-03-02 20:08:04 +08:00
|
|
|
|
try:
|
2024-03-04 11:19:08 +08:00
|
|
|
|
with open(INFO_FILE_PATH, "w", encoding="utf-8") as f:
|
|
|
|
|
json.dump(dic, f, ensure_ascii=False, indent=4)
|
2024-03-02 20:08:04 +08:00
|
|
|
|
except:
|
2024-03-04 11:19:08 +08:00
|
|
|
|
with open('./info.json', 'w', encoding='utf-8') as f:
|
|
|
|
|
f.write(json.dumps(dic))
|
|
|
|
|
self.progressBar_view(self.max_val)
|
|
|
|
|
self.DecryptSignal.emit(True)
|
|
|
|
|
self.close()
|
|
|
|
|
|
|
|
|
|
# 手动解密相关
|
|
|
|
|
|
|
|
|
|
def upload(self, version_data):
|
|
|
|
|
url = urljoin(SERVER_API_URL, 'wxBiasAddr')
|
2024-03-02 20:08:04 +08:00
|
|
|
|
try:
|
2024-03-04 11:19:08 +08:00
|
|
|
|
requests.post(url, json=version_data)
|
|
|
|
|
print('版本信息上传成功')
|
2024-03-02 20:08:04 +08:00
|
|
|
|
except:
|
2024-03-04 11:19:08 +08:00
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def get_bias_addr(self):
|
|
|
|
|
account = self.lineEdit_wx_alias.text()
|
|
|
|
|
mobile = self.lineEdit_tel.text()
|
|
|
|
|
name = self.lineEdit_wx_name.text()
|
|
|
|
|
if not all([account, mobile, name]):
|
|
|
|
|
self.message_error("请把所有信息填写完整")
|
|
|
|
|
return
|
|
|
|
|
key = None
|
|
|
|
|
db_path = "test"
|
|
|
|
|
self.thread = BiasThread(account, mobile, name, key, db_path)
|
|
|
|
|
self.thread.signal.connect(self.set_bias_addr)
|
|
|
|
|
self.thread.start()
|
|
|
|
|
|
|
|
|
|
def set_bias_addr(self, data):
|
|
|
|
|
self.upload(data)
|
|
|
|
|
self.biasAddrSignal.emit(data)
|
|
|
|
|
|
|
|
|
|
# 好友相关
|
|
|
|
|
|
|
|
|
|
def statistic(self):
|
|
|
|
|
self.friendView.stackedWidget.currentWidget().analysis()
|
|
|
|
|
|
|
|
|
|
def mood(self):
|
|
|
|
|
self.friendView.stackedWidget.currentWidget().emotionale_Analysis()
|
|
|
|
|
|
|
|
|
|
def user_report(self):
|
|
|
|
|
self.friendView.stackedWidget.currentWidget().annual_report()
|
|
|
|
|
|
|
|
|
|
# 数据导出相关
|
|
|
|
|
|
|
|
|
|
def set_export_date(self):
|
|
|
|
|
date_range = self.comboBox_time.currentText()
|
|
|
|
|
if date_range == '全部时间':
|
|
|
|
|
pass
|
|
|
|
|
elif date_range == '最近三个月':
|
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
|
today = datetime.now()
|
|
|
|
|
today_date = today.date()
|
|
|
|
|
today_midnight = datetime.combine(today_date, datetime.min.time()) + timedelta(days=1)
|
|
|
|
|
today_midnight_timestamp = int(today_midnight.timestamp())
|
|
|
|
|
three_months_ago = today - timedelta(days=90)
|
|
|
|
|
three_months_ago_date = three_months_ago.date()
|
|
|
|
|
three_months_ago_midnight = datetime.combine(three_months_ago_date, datetime.min.time())
|
|
|
|
|
three_months_ago_midnight_timestamp = int(three_months_ago_midnight.timestamp())
|
|
|
|
|
self.time_range = (three_months_ago_midnight_timestamp, today_midnight_timestamp)
|
|
|
|
|
|
|
|
|
|
elif date_range == '自定义时间':
|
|
|
|
|
self.time_range_view = TimeRangeDialog(parent=self)
|
|
|
|
|
self.time_range_view.date_range_signal.connect(self.set_time_range)
|
|
|
|
|
self.time_range_view.show()
|
|
|
|
|
self.comboBox_time.setCurrentIndex(0)
|
|
|
|
|
|
|
|
|
|
def set_time_range(self, time_range):
|
|
|
|
|
self.time_range = time_range
|
|
|
|
|
self.comboBox_time.setCurrentIndex(2)
|
|
|
|
|
|
|
|
|
|
def export_data(self):
|
|
|
|
|
self.exportBatchBtn.setEnabled(False)
|
|
|
|
|
selected_types = {export_types[export_type]: checkbox.isChecked() for export_type, checkbox in
|
|
|
|
|
zip(self.export_choices.keys(), self.findChildren(QCheckBox, 'message_type'))}
|
|
|
|
|
print("选择的数据类型:", selected_types)
|
|
|
|
|
|
|
|
|
|
file_types = []
|
|
|
|
|
for checkbox in [self.checkBox_txt, self.checkBox_csv, self.checkBox_html, self.checkBox_word]:
|
|
|
|
|
if checkbox.isChecked():
|
|
|
|
|
file_types.append(export_file_format[checkbox.text()])
|
|
|
|
|
select_contacts = []
|
|
|
|
|
count = self.listWidget.count()
|
|
|
|
|
for i in range(count):
|
|
|
|
|
item = self.listWidget.item(i)
|
|
|
|
|
if item.is_select:
|
|
|
|
|
select_contacts.append(self.contacts[i])
|
|
|
|
|
print("选择的文件格式:", file_types)
|
|
|
|
|
self.worker = Output(select_contacts, type_=Output.Batch, message_types=selected_types, sub_type=file_types,
|
|
|
|
|
time_range=self.time_range)
|
|
|
|
|
self.worker.okSignal.connect(self.export_finished)
|
|
|
|
|
self.worker.rangeSignal.connect(self.set_total_msg_num)
|
|
|
|
|
self.worker.nowContact.connect(self.update_progress)
|
|
|
|
|
self.worker.start()
|
|
|
|
|
self.timer.start(1000)
|
|
|
|
|
self.start_time = time.time()
|
|
|
|
|
|
|
|
|
|
def set_total_msg_num(self, num):
|
|
|
|
|
self.total_msg_num = num
|
|
|
|
|
|
|
|
|
|
def setCurrentIndex(self, item):
|
|
|
|
|
item.select()
|
|
|
|
|
item.dis_select()
|
|
|
|
|
|
|
|
|
|
def select_all(self):
|
|
|
|
|
self.select_all_flag = not self.select_all_flag
|
|
|
|
|
print('全选', self.select_all_flag)
|
|
|
|
|
if self.select_all_flag:
|
|
|
|
|
count = self.listWidget.count()
|
|
|
|
|
for i in range(count):
|
|
|
|
|
item = self.listWidget.item(i)
|
|
|
|
|
item.force_select()
|
|
|
|
|
self.btn_select_all.setText('全不选')
|
|
|
|
|
else:
|
|
|
|
|
count = self.listWidget.count()
|
|
|
|
|
for i in range(count):
|
|
|
|
|
item = self.listWidget.item(i)
|
|
|
|
|
item.force_dis_select()
|
|
|
|
|
self.btn_select_all.setText('全选')
|
|
|
|
|
|
|
|
|
|
def export_finished(self):
|
|
|
|
|
self.exportBatchBtn.setEnabled(True)
|
|
|
|
|
self.time = 0
|
|
|
|
|
end_time = time.time()
|
|
|
|
|
print(f'总耗时:{end_time - self.start_time}s')
|
|
|
|
|
self.message_notification(f"导出聊天记录成功\n在{OUTPUT_DIR}目录下(跟exe文件在一起)\n{os.path.normpath(os.path.join(os.getcwd(), OUTPUT_DIR))}")
|
|
|
|
|
|
|
|
|
|
def show_contact(self, contact):
|
|
|
|
|
contact_item = ContactQListWidgetItem(contact.remark, contact.smallHeadImgUrl, contact.smallHeadImgBLOG)
|
|
|
|
|
self.listWidget.addItem(contact_item)
|
|
|
|
|
self.listWidget.setItemWidget(contact_item, contact_item.widget)
|
|
|
|
|
self.contacts.append(contact)
|
|
|
|
|
|
|
|
|
|
def update_elapsed_time(self):
|
|
|
|
|
self.time += 1
|
|
|
|
|
self.label_time.setText(f"耗时: {self.time}s")
|
|
|
|
|
|
|
|
|
|
def update_progress(self, remark):
|
|
|
|
|
self.num += 1
|
|
|
|
|
progress_percentage = int((self.num) / self.total_msg_num * 100)
|
|
|
|
|
self.exportProgressBar.setValue(progress_percentage)
|
|
|
|
|
self.label_process.setText(f"导出进度: {progress_percentage}% {remark}")
|