2024-01-02 22:33:46 +08:00
|
|
|
import csv
|
|
|
|
import html
|
2023-01-23 09:47:40 +08:00
|
|
|
import os
|
2024-01-02 22:33:46 +08:00
|
|
|
import shutil
|
|
|
|
import sys
|
2023-01-23 09:47:40 +08:00
|
|
|
import time
|
2024-01-02 22:33:46 +08:00
|
|
|
import traceback
|
|
|
|
from re import findall
|
2023-03-31 11:15:44 +08:00
|
|
|
|
2023-01-23 09:47:40 +08:00
|
|
|
import docx
|
2024-01-02 22:33:46 +08:00
|
|
|
from PyQt5.QtCore import pyqtSignal, QThread
|
|
|
|
from PyQt5.QtWidgets import QFileDialog
|
2023-01-23 09:47:40 +08:00
|
|
|
from docx import shared
|
|
|
|
from docx.enum.table import WD_ALIGN_VERTICAL
|
|
|
|
from docx.enum.text import WD_COLOR_INDEX, WD_PARAGRAPH_ALIGNMENT
|
2024-01-02 22:33:46 +08:00
|
|
|
from docx.oxml.ns import qn
|
|
|
|
|
|
|
|
from .package_msg import PackageMsg
|
|
|
|
from ..DataBase import media_msg_db, hard_link_db, micro_msg_db, msg_db
|
|
|
|
from ..log import logger
|
|
|
|
from ..person import Me, Contact
|
|
|
|
from ..util import path
|
|
|
|
from ..util.compress_content import parser_reply, music_share, share_card
|
|
|
|
from ..util.emoji import get_emoji_url
|
|
|
|
from ..util.file import get_file
|
|
|
|
from ..util.music import get_music_path
|
|
|
|
from ..util.image import get_image_path, get_image, get_image_abs_path
|
|
|
|
|
|
|
|
os.makedirs('./data/聊天记录', exist_ok=True)
|
|
|
|
|
|
|
|
|
|
|
|
def set_global_font(doc, font_name):
|
|
|
|
# 创建一个新样式
|
|
|
|
style = doc.styles['Normal']
|
|
|
|
|
|
|
|
# 设置字体名称
|
|
|
|
style.font.name = font_name
|
|
|
|
# 遍历文档中的所有段落,将样式应用到每个段落
|
|
|
|
for paragraph in doc.paragraphs:
|
|
|
|
for run in paragraph.runs:
|
|
|
|
run.font.name = font_name
|
|
|
|
|
|
|
|
|
|
|
|
def makedirs(path):
|
|
|
|
os.makedirs(path, exist_ok=True)
|
|
|
|
os.makedirs(os.path.join(path, 'image'), exist_ok=True)
|
|
|
|
os.makedirs(os.path.join(path, 'emoji'), exist_ok=True)
|
|
|
|
os.makedirs(os.path.join(path, 'video'), exist_ok=True)
|
|
|
|
os.makedirs(os.path.join(path, 'voice'), exist_ok=True)
|
|
|
|
os.makedirs(os.path.join(path, 'file'), exist_ok=True)
|
|
|
|
os.makedirs(os.path.join(path, 'avatar'), exist_ok=True)
|
|
|
|
os.makedirs(os.path.join(path, 'music'), exist_ok=True)
|
|
|
|
os.makedirs(os.path.join(path, 'icon'), exist_ok=True)
|
|
|
|
file = './app/resources/data/file.png'
|
|
|
|
if not os.path.exists(file):
|
|
|
|
resource_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__)))
|
|
|
|
file = os.path.join(resource_dir, 'app', 'resources', 'data', 'file.png')
|
|
|
|
shutil.copy(file, path + '/icon/file.png')
|
|
|
|
play_file = './app/resources/data/play.png'
|
|
|
|
if not os.path.exists(play_file):
|
|
|
|
resource_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__)))
|
|
|
|
play_file = os.path.join(resource_dir, 'app', 'resources', 'data', 'play.png')
|
|
|
|
shutil.copy(play_file, path + '/icon/play.png')
|
|
|
|
pause_file = './app/resources/data/pause.png'
|
|
|
|
if not os.path.exists(pause_file):
|
|
|
|
resource_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__)))
|
|
|
|
pause_file = os.path.join(resource_dir, 'app', 'resources', 'data', 'pause.png')
|
|
|
|
shutil.copy(pause_file, path + '/icon/pause.png')
|
|
|
|
|
|
|
|
|
|
|
|
def escape_js_and_html(input_str):
|
|
|
|
# 转义HTML特殊字符
|
|
|
|
html_escaped = html.escape(input_str, quote=False)
|
|
|
|
|
|
|
|
# 手动处理JavaScript转义字符
|
|
|
|
js_escaped = (
|
|
|
|
html_escaped
|
|
|
|
.replace("\\", "\\\\")
|
|
|
|
.replace("'", r"\'")
|
|
|
|
.replace('"', r'\"')
|
|
|
|
.replace("\n", r'\n')
|
|
|
|
.replace("\r", r'\r')
|
|
|
|
.replace("\t", r'\t')
|
|
|
|
)
|
|
|
|
|
|
|
|
return js_escaped
|
|
|
|
|
|
|
|
|
|
|
|
class ExporterBase(QThread):
|
2023-01-23 09:47:40 +08:00
|
|
|
progressSignal = pyqtSignal(int)
|
2023-02-02 00:26:44 +08:00
|
|
|
rangeSignal = pyqtSignal(int)
|
|
|
|
okSignal = pyqtSignal(int)
|
|
|
|
i = 1
|
2023-10-26 22:45:44 +08:00
|
|
|
CSV = 0
|
|
|
|
DOCX = 1
|
|
|
|
HTML = 2
|
2024-01-02 22:33:46 +08:00
|
|
|
CSV_ALL = 3
|
|
|
|
CONTACT_CSV = 4
|
|
|
|
TXT = 5
|
2023-01-23 09:47:40 +08:00
|
|
|
|
2024-01-02 22:33:46 +08:00
|
|
|
def __init__(self, contact, type_=DOCX, message_types={}, parent=None):
|
2023-01-23 09:47:40 +08:00
|
|
|
super().__init__(parent)
|
2024-01-02 22:33:46 +08:00
|
|
|
self.message_types = message_types # 导出的消息类型
|
|
|
|
self.contact: Contact = contact # 联系人
|
|
|
|
self.output_type = type_ # 导出文件类型
|
|
|
|
self.total_num = 1 # 总的消息数量
|
|
|
|
self.num = 0 # 当前处理的消息数量
|
|
|
|
self.last_timestamp = 0
|
|
|
|
origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}"
|
|
|
|
makedirs(origin_docx_path)
|
2023-02-02 00:26:44 +08:00
|
|
|
def run(self):
|
2024-01-02 22:33:46 +08:00
|
|
|
self.export()
|
|
|
|
def export(self):
|
|
|
|
raise NotImplementedError("export method must be implemented in subclasses")
|
|
|
|
|
|
|
|
def cancel(self):
|
|
|
|
self.requestInterruption()
|
|
|
|
|
|
|
|
def is_5_min(self, timestamp) -> bool:
|
|
|
|
if abs(timestamp - self.last_timestamp) > 300:
|
|
|
|
self.last_timestamp = timestamp
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
def get_avatar_path(self, is_send, message, is_absolute_path=False) -> str:
|
|
|
|
if self.contact.is_chatroom:
|
|
|
|
avatar = message[12].smallHeadImgUrl
|
2023-01-23 09:47:40 +08:00
|
|
|
else:
|
2024-01-02 22:33:46 +08:00
|
|
|
avatar = Me().smallHeadImgUrl if is_send else self.contact.smallHeadImgUrl
|
|
|
|
if is_absolute_path:
|
|
|
|
if self.contact.is_chatroom:
|
|
|
|
avatar = message[12].avatar_path
|
2023-01-23 09:47:40 +08:00
|
|
|
else:
|
2024-01-02 22:33:46 +08:00
|
|
|
avatar = Me().avatar_path if is_send else self.contact.avatar_path
|
|
|
|
return avatar
|
2023-01-23 09:47:40 +08:00
|
|
|
|
2024-01-02 22:33:46 +08:00
|
|
|
def get_display_name(self, is_send, message) -> str:
|
|
|
|
if self.contact.is_chatroom:
|
|
|
|
if is_send:
|
|
|
|
display_name = Me().name
|
|
|
|
else:
|
|
|
|
display_name = message[12].remark
|
|
|
|
else:
|
|
|
|
display_name = Me().name if is_send else self.contact.remark
|
|
|
|
return escape_js_and_html(display_name)
|
2023-01-23 09:47:40 +08:00
|
|
|
|
2024-01-02 22:33:46 +08:00
|
|
|
def text(self, doc, message):
|
|
|
|
return
|
2023-01-23 09:47:40 +08:00
|
|
|
|
2024-01-02 22:33:46 +08:00
|
|
|
def image(self, doc, message):
|
|
|
|
return
|
2023-01-23 09:47:40 +08:00
|
|
|
|
2024-01-02 22:33:46 +08:00
|
|
|
def audio(self, doc, message):
|
|
|
|
return
|
2023-02-02 00:26:44 +08:00
|
|
|
|
2024-01-02 22:33:46 +08:00
|
|
|
def emoji(self, doc, message):
|
|
|
|
return
|
2023-02-02 00:26:44 +08:00
|
|
|
|
2024-01-02 22:33:46 +08:00
|
|
|
def file(self, doc, message):
|
|
|
|
return
|
2023-01-23 09:47:40 +08:00
|
|
|
|
2024-01-02 22:33:46 +08:00
|
|
|
def refermsg(self, doc, message):
|
|
|
|
return
|
2023-10-26 22:45:44 +08:00
|
|
|
|
2024-01-02 22:33:46 +08:00
|
|
|
def system_msg(self, doc, message):
|
|
|
|
return
|
2023-10-26 22:45:44 +08:00
|
|
|
|
2024-01-02 22:33:46 +08:00
|
|
|
def video(self, doc, message):
|
|
|
|
return
|
2023-01-23 09:47:40 +08:00
|
|
|
|
2024-01-02 22:33:46 +08:00
|
|
|
def music_share(self, doc, message):
|
|
|
|
return
|
2023-01-23 09:47:40 +08:00
|
|
|
|
2024-01-02 22:33:46 +08:00
|
|
|
def share_card(self, doc, message):
|
|
|
|
return
|