新增导出所有聊天记录到CSV

This commit is contained in:
shuaikangzhou 2023-11-27 21:23:26 +08:00
parent 252b7fa145
commit 5f11483d1a
9 changed files with 129 additions and 51 deletions

View File

@ -1,4 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (WeChatMsg)" project-jdk-type="Python SDK" /> <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (WeChatMsg)" project-jdk-type="Python SDK" />
<component name="PyCharmProfessionalAdvertiser">
<option name="shown" value="true" />
</component>
</project> </project>

View File

@ -67,6 +67,26 @@ def get_messages(username_):
return result return result
def get_messages_all():
sql = '''
select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime
from MSG
order by CreateTime
'''
result = []
for cur in cursor:
try:
lock.acquire(True)
cur.execute(sql)
result_ = cur.fetchall()
# print(len(result))
result += result_
finally:
lock.release()
result.sort(key=lambda x: x[5])
return result
def get_message_by_num(username_, local_id): def get_message_by_num(username_, local_id):
sql = ''' sql = '''
select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime

View File

@ -4,7 +4,6 @@ import os
from PyQt5.QtCore import pyqtSignal, QThread from PyQt5.QtCore import pyqtSignal, QThread
from . import msg from . import msg
from ..log import log
from ..person_pc import MePC from ..person_pc import MePC
if not os.path.exists('./data/聊天记录'): if not os.path.exists('./data/聊天记录'):
@ -22,55 +21,55 @@ class Output(QThread):
CSV = 0 CSV = 0
DOCX = 1 DOCX = 1
HTML = 2 HTML = 2
CSV_ALL = 3
def __init__(self, contact, parent=None, type_=DOCX): def __init__(self, contact, parent=None, type_=DOCX):
super().__init__(parent) super().__init__(parent)
self.Child0 = None
self.last_timestamp = 0 self.last_timestamp = 0
self.sec = 2 # 默认1000秒 self.sec = 2 # 默认1000秒
self.contact = contact self.contact = contact
self.ta_username = contact.wxid self.ta_username = contact.wxid if contact else ''
self.msg_id = 0 self.msg_id = 0
self.output_type = type_ self.output_type = type_
self.total_num = 0 self.total_num = 0
self.num = 0 self.num = 0
@log
def to_csv(self, conRemark, path):
self.okSignal.emit('ok')
def to_html(self):
self.okSignal.emit('ok')
def is_5_min(self, timestamp):
if abs(timestamp - self.last_timestamp) > 300:
self.last_timestamp = timestamp
return True
return False
def progress(self, value): def progress(self, value):
self.progressSignal.emit(value) self.progressSignal.emit(value)
def to_csv_all(self):
origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/"
if not os.path.exists(origin_docx_path):
os.mkdir(origin_docx_path)
filename = f"{os.path.abspath('.')}/data/聊天记录/messages.csv"
# columns = ["用户名", "消息内容", "发送时间", "发送状态", "消息类型", "isSend", "msgId"]
columns = ['localId', 'TalkerId', 'Type', 'SubType',
'IsSender', 'CreateTime', 'Status', 'StrContent',
'StrTime']
messages = msg.get_messages_all()
# 写入CSV文件
with open(filename, mode='w', newline='', encoding='utf-8') as file:
writer = csv.writer(file)
writer.writerow(columns)
# 写入数据
writer.writerows(messages)
self.okSignal.emit(1)
def run(self): def run(self):
if self.output_type == self.DOCX: if self.output_type == self.DOCX:
return return
elif self.output_type == self.CSV: elif self.output_type == self.CSV_ALL:
# print("线程导出csv") self.to_csv_all()
self.Child0 = ChildThread(self.contact, type_=ChildThread.CSV) else:
self.Child0 = ChildThread(self.contact, type_=self.output_type)
self.Child0.progressSignal.connect(self.progress) self.Child0.progressSignal.connect(self.progress)
self.Child0.rangeSignal.connect(self.rangeSignal) self.Child0.rangeSignal.connect(self.rangeSignal)
self.Child0.okSignal.connect(self.okSignal) self.Child0.okSignal.connect(self.okSignal)
self.Child0.run() self.Child0.start()
elif self.output_type == self.HTML:
# self.to_html() def cancel(self):
self.Child0 = ChildThread(self.contact, type_=ChildThread.HTML) self.requestInterruption()
self.Child0.progressSignal.connect(self.progress)
self.Child0.rangeSignal.connect(self.rangeSignal)
self.Child0.okSignal.connect(self.okSignal)
self.Child0.run()
# self.okSignal.emit(1)
class ChildThread(QThread): class ChildThread(QThread):
@ -96,7 +95,6 @@ class ChildThread(QThread):
def is_5_min(self, timestamp): def is_5_min(self, timestamp):
if abs(timestamp - self.last_timestamp) > 300: if abs(timestamp - self.last_timestamp) > 300:
self.last_timestamp = timestamp self.last_timestamp = timestamp
return True return True
return False return False
@ -142,6 +140,24 @@ class ChildThread(QThread):
writer.writerows(messages) writer.writerows(messages)
self.okSignal.emit('ok') self.okSignal.emit('ok')
def to_csv_all(self):
origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/"
if not os.path.exists(origin_docx_path):
os.mkdir(origin_docx_path)
filename = f"{os.path.abspath('.')}/data/聊天记录/messages.csv"
# columns = ["用户名", "消息内容", "发送时间", "发送状态", "消息类型", "isSend", "msgId"]
columns = ['localId', 'TalkerId', 'Type', 'SubType',
'IsSender', 'CreateTime', 'Status', 'StrContent',
'StrTime']
messages = msg.get_messages_all()
# 写入CSV文件
with open(filename, mode='w', newline='', encoding='utf-8') as file:
writer = csv.writer(file)
writer.writerow(columns)
# 写入数据
writer.writerows(messages)
self.okSignal.emit(1)
def to_html(self): def to_html(self):
origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}" origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}"
if not os.path.exists(origin_docx_path): if not os.path.exists(origin_docx_path):
@ -734,9 +750,14 @@ const chatMessages = [
self.okSignal.emit(1) self.okSignal.emit(1)
def run(self): def run(self):
if self.output_type == self.DOCX: if self.output_type == Output.DOCX:
return return
elif self.output_type == self.CSV: elif self.output_type == Output.CSV:
self.to_csv() self.to_csv()
elif self.output_type == self.HTML: elif self.output_type == Output.HTML:
self.to_html_() self.to_html_()
elif self.output_type == Output.CSV_ALL:
self.to_csv_all()
def cancel(self):
self.requestInterruption()

View File

@ -43,6 +43,7 @@ class ChatInfo(QWidget):
def show_finish(self, ok): def show_finish(self, ok):
self.setScrollBarPos() self.setScrollBarPos()
self.show_chat_thread.quit()
def verticalScrollBar(self, pos): def verticalScrollBar(self, pos):
""" """

View File

@ -21,6 +21,7 @@ from . import mainwindow
from .chat import ChatWindow from .chat import ChatWindow
from .contact import ContactWindow from .contact import ContactWindow
from .tool.tool_window import ToolWindow from .tool.tool_window import ToolWindow
from ..DataBase.output_pc import Output
from ..person_pc import MePC from ..person_pc import MePC
# 美化样式表 # 美化样式表
@ -68,6 +69,8 @@ class MainWinController(QMainWindow, mainwindow.Ui_MainWindow):
# username = '' # username = ''
def __init__(self, username, parent=None): def __init__(self, username, parent=None):
super(MainWinController, self).__init__(parent) super(MainWinController, self).__init__(parent)
self.outputThread0 = None
self.outputThread = None
self.setupUi(self) self.setupUi(self)
self.setWindowIcon(Icon.MainWindow_Icon) self.setWindowIcon(Icon.MainWindow_Icon)
self.setStyleSheet(Stylesheet) self.setStyleSheet(Stylesheet)
@ -101,12 +104,15 @@ class MainWinController(QMainWindow, mainwindow.Ui_MainWindow):
) )
def init_ui(self): def init_ui(self):
self.menu_output.setIcon(Icon.Output)
self.action_output_CSV.setIcon(Icon.ToCSV)
self.action_output_CSV.triggered.connect(self.output)
self.action_help_contact.triggered.connect( self.action_help_contact.triggered.connect(
lambda: QDesktopServices.openUrl(QUrl("http://8.146.206.114/post/4"))) lambda: QDesktopServices.openUrl(QUrl("https://blog.lc044.love/post/4")))
self.action_help_chat.triggered.connect( self.action_help_chat.triggered.connect(
lambda: QDesktopServices.openUrl(QUrl("http://8.146.206.114/post/4"))) lambda: QDesktopServices.openUrl(QUrl("https://blog.lc044.love/post/4")))
self.action_help_decrypt.triggered.connect( self.action_help_decrypt.triggered.connect(
lambda: QDesktopServices.openUrl(QUrl("http://8.146.206.114/post/4"))) lambda: QDesktopServices.openUrl(QUrl("https://blog.lc044.love/post/4")))
self.listWidget.setVisible(False) self.listWidget.setVisible(False)
self.stackedWidget.setVisible(False) self.stackedWidget.setVisible(False)
self.listWidget.currentRowChanged.connect(self.setCurrentIndex) self.listWidget.currentRowChanged.connect(self.setCurrentIndex)
@ -180,6 +186,16 @@ class MainWinController(QMainWindow, mainwindow.Ui_MainWindow):
self.listWidget.setVisible(True) self.listWidget.setVisible(True)
self.stackedWidget.setVisible(True) self.stackedWidget.setVisible(True)
def output(self):
if self.sender() == self.action_output_CSV:
self.outputThread = Output(None, type_=Output.CSV_ALL)
self.outputThread.okSignal.connect(
lambda x: self.message('聊天记录导出成功\n./data/聊天记录/messages.csv'))
self.outputThread.start()
def message(self, msg):
QMessageBox.about(self, "提醒", msg)
def about(self): def about(self):
""" """
关于 关于

View File

@ -90,11 +90,13 @@ class Ui_MainWindow(object):
self.menubar.setObjectName("menubar") self.menubar.setObjectName("menubar")
self.menu_F = QtWidgets.QMenu(self.menubar) self.menu_F = QtWidgets.QMenu(self.menubar)
self.menu_F.setObjectName("menu_F") self.menu_F.setObjectName("menu_F")
self.menu = QtWidgets.QMenu(self.menubar) self.menu_data = QtWidgets.QMenu(self.menubar)
font = QtGui.QFont() font = QtGui.QFont()
font.setFamily("微软雅黑") font.setFamily("微软雅黑")
self.menu.setFont(font) self.menu_data.setFont(font)
self.menu.setObjectName("menu") self.menu_data.setObjectName("menu_data")
self.menu_output = QtWidgets.QMenu(self.menu_data)
self.menu_output.setObjectName("menu_output")
self.menu_2 = QtWidgets.QMenu(self.menubar) self.menu_2 = QtWidgets.QMenu(self.menubar)
self.menu_2.setObjectName("menu_2") self.menu_2.setObjectName("menu_2")
self.menu_about = QtWidgets.QMenu(self.menubar) self.menu_about = QtWidgets.QMenu(self.menubar)
@ -120,16 +122,20 @@ class Ui_MainWindow(object):
self.action_help_chat.setObjectName("action_help_chat") self.action_help_chat.setObjectName("action_help_chat")
self.action_help_contact = QtWidgets.QAction(MainWindow) self.action_help_contact = QtWidgets.QAction(MainWindow)
self.action_help_contact.setObjectName("action_help_contact") self.action_help_contact.setObjectName("action_help_contact")
self.action_output_CSV = QtWidgets.QAction(MainWindow)
self.action_output_CSV.setObjectName("action_output_CSV")
self.menu_F.addSeparator() self.menu_F.addSeparator()
self.menu_F.addSeparator() self.menu_F.addSeparator()
self.menu_F.addAction(self.action_3) self.menu_F.addAction(self.action_3)
self.menu_F.addAction(self.action_4) self.menu_F.addAction(self.action_4)
self.menu_output.addAction(self.action_output_CSV)
self.menu_data.addAction(self.menu_output.menuAction())
self.menu_2.addAction(self.action_help_decrypt) self.menu_2.addAction(self.action_help_decrypt)
self.menu_2.addAction(self.action_help_chat) self.menu_2.addAction(self.action_help_chat)
self.menu_2.addAction(self.action_help_contact) self.menu_2.addAction(self.action_help_contact)
self.menu_about.addAction(self.action_desc) self.menu_about.addAction(self.action_desc)
self.menubar.addAction(self.menu_F.menuAction()) self.menubar.addAction(self.menu_F.menuAction())
self.menubar.addAction(self.menu.menuAction()) self.menubar.addAction(self.menu_data.menuAction())
self.menubar.addAction(self.menu_2.menuAction()) self.menubar.addAction(self.menu_2.menuAction())
self.menubar.addAction(self.menu_about.menuAction()) self.menubar.addAction(self.menu_about.menuAction())
self.menubar.addAction(self.menu_3.menuAction()) self.menubar.addAction(self.menu_3.menuAction())
@ -155,7 +161,8 @@ class Ui_MainWindow(object):
item.setText(_translate("MainWindow", "新建项目")) item.setText(_translate("MainWindow", "新建项目"))
self.listWidget.setSortingEnabled(__sortingEnabled) self.listWidget.setSortingEnabled(__sortingEnabled)
self.menu_F.setTitle(_translate("MainWindow", "文件(F)")) self.menu_F.setTitle(_translate("MainWindow", "文件(F)"))
self.menu.setTitle(_translate("MainWindow", "编辑")) self.menu_data.setTitle(_translate("MainWindow", "数据"))
self.menu_output.setTitle(_translate("MainWindow", "导出聊天记录(全部)"))
self.menu_2.setTitle(_translate("MainWindow", "帮助")) self.menu_2.setTitle(_translate("MainWindow", "帮助"))
self.menu_about.setTitle(_translate("MainWindow", "关于")) self.menu_about.setTitle(_translate("MainWindow", "关于"))
self.menu_3.setTitle(_translate("MainWindow", "不显示或者显示异常请重启应用")) self.menu_3.setTitle(_translate("MainWindow", "不显示或者显示异常请重启应用"))
@ -165,3 +172,4 @@ class Ui_MainWindow(object):
self.action_desc.setText(_translate("MainWindow", "说明")) self.action_desc.setText(_translate("MainWindow", "说明"))
self.action_help_chat.setText(_translate("MainWindow", "聊天相关")) self.action_help_chat.setText(_translate("MainWindow", "聊天相关"))
self.action_help_contact.setText(_translate("MainWindow", "好友相关")) self.action_help_contact.setText(_translate("MainWindow", "好友相关"))
self.action_output_CSV.setText(_translate("MainWindow", "CSV"))

View File

@ -183,15 +183,22 @@
<addaction name="action_3"/> <addaction name="action_3"/>
<addaction name="action_4"/> <addaction name="action_4"/>
</widget> </widget>
<widget class="QMenu" name="menu"> <widget class="QMenu" name="menu_data">
<property name="font"> <property name="font">
<font> <font>
<family>微软雅黑</family> <family>微软雅黑</family>
</font> </font>
</property> </property>
<property name="title"> <property name="title">
<string>编辑</string> <string>数据</string>
</property> </property>
<widget class="QMenu" name="menu_output">
<property name="title">
<string>导出聊天记录(全部)</string>
</property>
<addaction name="action_output_CSV"/>
</widget>
<addaction name="menu_output"/>
</widget> </widget>
<widget class="QMenu" name="menu_2"> <widget class="QMenu" name="menu_2">
<property name="title"> <property name="title">
@ -213,7 +220,7 @@
</property> </property>
</widget> </widget>
<addaction name="menu_F"/> <addaction name="menu_F"/>
<addaction name="menu"/> <addaction name="menu_data"/>
<addaction name="menu_2"/> <addaction name="menu_2"/>
<addaction name="menu_about"/> <addaction name="menu_about"/>
<addaction name="menu_3"/> <addaction name="menu_3"/>
@ -254,6 +261,11 @@
<string>好友相关</string> <string>好友相关</string>
</property> </property>
</action> </action>
<action name="action_output_CSV">
<property name="text">
<string>CSV</string>
</property>
</action>
</widget> </widget>
<resources/> <resources/>
<connections/> <connections/>

View File

@ -52,6 +52,8 @@ def parser_xml(xml_string):
def download(url, output_dir, name, thumb=False): def download(url, output_dir, name, thumb=False):
if not url:
return ':/icons/icons/404.png'
resp = requests.get(url) resp = requests.get(url)
byte = resp.content byte = resp.content
image_format = get_image_format(byte[:8]) image_format = get_image_format(byte[:8])

View File

@ -2,7 +2,7 @@ import ctypes
import sys import sys
import time import time
from PyQt5.QtGui import QIcon, QMovie from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import * from PyQt5.QtWidgets import *
from app.ui_pc import mainview from app.ui_pc import mainview
@ -18,11 +18,6 @@ class ViewController(QWidget):
self.setWindowIcon(QIcon(':/icons/icons/logo.png')) self.setWindowIcon(QIcon(':/icons/icons/logo.png'))
self.viewMainWIndow = None self.viewMainWIndow = None
self.viewDecrypt = None self.viewDecrypt = None
# 创建加载动画
loading_label = QLabel()
movie = QMovie("./app/data/loading.gif") # 替换为你的加载动画文件路径
loading_label.setMovie(movie)
movie.start()
def loadPCDecryptView(self): def loadPCDecryptView(self):
""" """