From a977359d87414bc061a9243b4de4500e6e8cbbc1 Mon Sep 17 00:00:00 2001 From: STDquantum <405720329@qq.com> Date: Thu, 18 Jan 2024 13:51:14 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E5=AE=8C=E6=88=90=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=E8=BD=AC=E8=B4=A6=E5=92=8C=E9=9F=B3=E8=A7=86=E9=A2=91=E9=80=9A?= =?UTF-8?q?=E8=AF=9D=E7=B1=BB=E5=9E=8B=E4=BF=A1=E6=81=AF=E7=9A=84=E4=B8=A4?= =?UTF-8?q?=E4=B8=AA=E5=90=8E=E7=AB=AF=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/DataBase/exporter_csv.py | 2 +- app/DataBase/exporter_docx.py | 4 +- app/DataBase/exporter_html.py | 271 ++++++++++++++++------- app/DataBase/msg.py | 7 +- app/DataBase/output.py | 6 +- app/DataBase/package_msg.py | 3 +- app/ui/chat/chat_info.py | 8 +- app/ui/contact/export/export_dialog.py | 6 +- app/ui/menu/export.py | 6 +- app/util/compress_content.py | 295 +++++++++++++++---------- 10 files changed, 389 insertions(+), 219 deletions(-) diff --git a/app/DataBase/exporter_csv.py b/app/DataBase/exporter_csv.py index 5536110..941d89f 100644 --- a/app/DataBase/exporter_csv.py +++ b/app/DataBase/exporter_csv.py @@ -22,7 +22,7 @@ class CSVExporter(ExporterBase): # 写入数据 # writer.writerows(messages) for msg in messages: - other_data = [msg[12].remark, msg[12].nickName, msg[12].wxid] if self.contact.is_chatroom else [] + other_data = [msg[13].remark, msg[13].nickName, msg[13].wxid] if self.contact.is_chatroom else [] writer.writerow([*msg[:9], *other_data]) print(f"【完成导出 CSV {self.contact.remark}】") self.okSignal.emit(1) diff --git a/app/DataBase/exporter_docx.py b/app/DataBase/exporter_docx.py index 2fe4e51..bd81d1f 100644 --- a/app/DataBase/exporter_docx.py +++ b/app/DataBase/exporter_docx.py @@ -320,8 +320,8 @@ class DocxExporter(ExporterBase): if message[4]: # is_send continue try: - chatroom_avatar_path = f"{origin_docx_path}/avatar/{message[12].wxid}.png" - message[12].save_avatar(chatroom_avatar_path) + chatroom_avatar_path = f"{origin_docx_path}/avatar/{message[13].wxid}.png" + message[13].save_avatar(chatroom_avatar_path) except: print(message) pass diff --git a/app/DataBase/exporter_html.py b/app/DataBase/exporter_html.py index a657246..bd8cb98 100644 --- a/app/DataBase/exporter_html.py +++ b/app/DataBase/exporter_html.py @@ -11,19 +11,26 @@ from app.DataBase.output import ExporterBase, escape_js_and_html from app.log import logger from app.person import Me from app.util import path -from app.util.compress_content import parser_reply, share_card, music_share, file +from app.util.compress_content import ( + parser_reply, + share_card, + music_share, + file, + transfer_decompress, + call_decompress, +) from app.util.emoji import get_emoji_url from app.util.image import get_image_path, get_image from app.util.music import get_music_path icon_files = { - './icon/word.png': ['doc', 'docx'], - './icon/excel.png': ['xls', 'xlsx'], - './icon/csv.png': ['csv'], - './icon/txt.png': ['txt'], - './icon/zip.png': ['zip', '7z', 'rar'], - './icon/ppt.png': ['ppt', 'pptx'], - './icon/pdf.png': ['pdf'], + "./icon/word.png": ["doc", "docx"], + "./icon/excel.png": ["xls", "xlsx"], + "./icon/csv.png": ["csv"], + "./icon/txt.png": ["txt"], + "./icon/zip.png": ["zip", "7z", "rar"], + "./icon/ppt.png": ["ppt", "pptx"], + "./icon/pdf.png": ["pdf"], } @@ -40,7 +47,7 @@ class HtmlExporter(ExporterBase): avatar = self.get_avatar_path(is_send, message) str_content = escape_js_and_html(str_content) doc.write( - f'''{{ type:{1}, text: '{str_content}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},''' + f"""{{ type:{1}, text: '{str_content}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},""" ) def image(self, doc, message): @@ -57,13 +64,17 @@ class HtmlExporter(ExporterBase): str_content = escape_js_and_html(str_content) image_path = hard_link_db.get_image(str_content, BytesExtra, thumb=False) if not os.path.exists(os.path.join(Me().wx_dir, image_path)): - image_thumb_path = hard_link_db.get_image(str_content, BytesExtra, thumb=True) + image_thumb_path = hard_link_db.get_image( + str_content, BytesExtra, thumb=True + ) if not os.path.exists(os.path.join(Me().wx_dir, image_thumb_path)): return image_path = image_thumb_path - image_path = get_image_path(image_path, base_path=f'/data/聊天记录/{self.contact.remark}/image') + image_path = get_image_path( + image_path, base_path=f"/data/聊天记录/{self.contact.remark}/image" + ) doc.write( - f'''{{ type:{type_}, text: '{image_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},''' + f"""{{ type:{type_}, text: '{image_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},""" ) def audio(self, doc, message): @@ -77,7 +88,9 @@ class HtmlExporter(ExporterBase): avatar = self.get_avatar_path(is_send, message) display_name = self.get_display_name(is_send, message) try: - audio_path = media_msg_db.get_audio_path(msgSvrId, output_path=origin_docx_path + "/voice") + audio_path = media_msg_db.get_audio_path( + msgSvrId, output_path=origin_docx_path + "/voice" + ) audio_path = "./voice/" + os.path.basename(audio_path) except: logger.error(traceback.format_exc()) @@ -86,7 +99,7 @@ class HtmlExporter(ExporterBase): if voice_to_text and voice_to_text != "": voice_to_text = escape_js_and_html(voice_to_text) doc.write( - f'''{{ type:34, text:'{audio_path}',is_send:{is_send},avatar_path:'{avatar}',voice_to_text:'{voice_to_text}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},''' + f"""{{ type:34, text:'{audio_path}',is_send:{is_send},avatar_path:'{avatar}',voice_to_text:'{voice_to_text}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},""" ) def emoji(self, doc, message): @@ -99,7 +112,7 @@ class HtmlExporter(ExporterBase): display_name = self.get_display_name(is_send, message) emoji_path = get_emoji_url(str_content, thumb=True) doc.write( - f'''{{ type:{3}, text: '{emoji_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},''' + f"""{{ type:{3}, text: '{emoji_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},""" ) def file(self, doc, message): @@ -112,25 +125,27 @@ class HtmlExporter(ExporterBase): is_chatroom = 1 if self.contact.is_chatroom else 0 avatar = self.get_avatar_path(is_send, message) display_name = self.get_display_name(is_send, message) - file_info = file(bytesExtra, compress_content, output_path=origin_docx_path + '/file') - if file_info.get('is_error') == False: + file_info = file( + bytesExtra, compress_content, output_path=origin_docx_path + "/file" + ) + if file_info.get("is_error") == False: icon_path = None for icon, extensions in icon_files.items(): - if file_info.get('file_ext') in extensions: + if file_info.get("file_ext") in extensions: icon_path = icon break # 如果没有与文件后缀匹配的图标,则使用默认图标 if icon_path is None: - default_icon = './icon/file.png' + default_icon = "./icon/file.png" icon_path = default_icon - file_path = file_info.get('file_path') + file_path = file_info.get("file_path") if file_path != "": - file_path = './file/' + file_info.get('file_name') + file_path = "./file/" + file_info.get("file_name") doc.write( - f'''{{ type:49, text: '{file_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp} + f"""{{ type:49, text: '{file_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp} ,is_chatroom:{is_chatroom},displayname:'{display_name}',icon_path: '{icon_path}' ,sub_type:6,file_name: '{file_info.get('file_name')}',file_size: '{file_info.get('file_len')}' - ,app_name: '{file_info.get('app_name')}'}},''' + ,app_name: '{file_info.get('app_name')}'}},""" ) def refermsg(self, doc, message): @@ -143,20 +158,20 @@ class HtmlExporter(ExporterBase): str_time = message[8] is_send = message[4] content = parser_reply(message[11]) - refer_msg = content.get('refer') + refer_msg = content.get("refer") timestamp = message[5] is_chatroom = 1 if self.contact.is_chatroom else 0 avatar = self.get_avatar_path(is_send, message) display_name = self.get_display_name(is_send, message) - contentText = escape_js_and_html(content.get('title')) + contentText = escape_js_and_html(content.get("title")) if refer_msg: referText = f"{escape_js_and_html(refer_msg.get('displayname'))}:{escape_js_and_html(refer_msg.get('content'))}" doc.write( - f'''{{ type:49, text: '{contentText}',is_send:{is_send},sub_type:{content.get('type')},refer_text: '{referText}',avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},''' + f"""{{ type:49, text: '{contentText}',is_send:{is_send},sub_type:{content.get('type')},refer_text: '{referText}',avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},""" ) else: doc.write( - f'''{{ type:49, text: '{contentText}',is_send:{is_send},sub_type:{content.get('type')},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},''' + f"""{{ type:49, text: '{contentText}',is_send:{is_send},sub_type:{content.get('type')},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},""" ) def system_msg(self, doc, message): @@ -166,14 +181,15 @@ class HtmlExporter(ExporterBase): timestamp = message[5] is_chatroom = 1 if self.contact.is_chatroom else 0 - str_content = str_content.replace('重新编辑]]>', "") - res = findall('()', str_content) + str_content = str_content.replace("重新编辑]]>', "" + ) + res = findall("()", str_content) for xmlstr, b in res: str_content = str_content.replace(xmlstr, "") str_content = escape_js_and_html(str_content) doc.write( - f'''{{ type:0, text: '{str_content}',is_send:{is_send},avatar_path:'',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:''}},''' + f"""{{ type:0, text: '{str_content}',is_send:{is_send},avatar_path:'',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:''}},""" ) def video(self, doc, message): @@ -190,30 +206,32 @@ class HtmlExporter(ExporterBase): video_path = hard_link_db.get_video(str_content, BytesExtra, thumb=False) image_path = hard_link_db.get_video(str_content, BytesExtra, thumb=True) 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 = path.get_relative_path( + image_path, base_path=f"/data/聊天记录/{self.contact.remark}/image" + ) try: # todo 网络图片问题 print(origin_docx_path + image_path[1:]) os.utime(origin_docx_path + image_path[1:], (timestamp, timestamp)) doc.write( - f'''{{ type:3, text: '{image_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},''' + f"""{{ type:3, text: '{image_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},""" ) except: doc.write( - f'''{{ type:1, text: '视频丢失',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},''' + f"""{{ type:1, text: '视频丢失',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},""" ) return if video_path is None and image_path is None: return - video_path = f'{Me().wx_dir}/{video_path}' + video_path = f"{Me().wx_dir}/{video_path}" if os.path.exists(video_path): - new_path = origin_docx_path + '/video/' + os.path.basename(video_path) + new_path = origin_docx_path + "/video/" + os.path.basename(video_path) if not os.path.exists(new_path): - shutil.copy(video_path, os.path.join(origin_docx_path, 'video')) + shutil.copy(video_path, os.path.join(origin_docx_path, "video")) os.utime(new_path, (timestamp, timestamp)) - video_path = f'./video/{os.path.basename(video_path)}' + video_path = f"./video/{os.path.basename(video_path)}" doc.write( - f'''{{ type:{type_}, text: '{video_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},''' + f"""{{ type:{type_}, text: '{video_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},""" ) def music_share(self, doc, message): @@ -221,21 +239,24 @@ class HtmlExporter(ExporterBase): is_send = message[4] timestamp = message[5] content = music_share(message[11]) - music_path = '' - if content.get('is_error') == False: - if content.get('audio_url') != '': - music_path = get_music_path(content.get('audio_url'), content.get('title'), - output_path=origin_docx_path + '/music') - if music_path != '': - music_path = f'./music/{os.path.basename(music_path)}' - music_path = music_path.replace('\\', '/') + music_path = "" + if content.get("is_error") == False: + if content.get("audio_url") != "": + music_path = get_music_path( + content.get("audio_url"), + content.get("title"), + output_path=origin_docx_path + "/music", + ) + if music_path != "": + music_path = f"./music/{os.path.basename(music_path)}" + music_path = music_path.replace("\\", "/") is_chatroom = 1 if self.contact.is_chatroom else 0 avatar = self.get_avatar_path(is_send, message) display_name = self.get_display_name(is_send, message) doc.write( - f'''{{ type:49, text:'{music_path}',is_send:{is_send},avatar_path:'{avatar}',link_url:'{content.get('link_url')}', + f"""{{ type:49, text:'{music_path}',is_send:{is_send},avatar_path:'{avatar}',link_url:'{content.get('link_url')}', timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}',sub_type:3,title:'{content.get('title')}', - artist:'{content.get('artist')}', website_name:'{content.get('website_name')}'}},''' + artist:'{content.get('artist')}', website_name:'{content.get('website_name')}'}},""" ) def share_card(self, doc, message): @@ -248,53 +269,116 @@ class HtmlExporter(ExporterBase): is_chatroom = 1 if self.contact.is_chatroom else 0 avatar = self.get_avatar_path(is_send, message) display_name = self.get_display_name(is_send, message) - thumbnail = '' - if card_data.get('thumbnail'): - thumbnail = os.path.join(Me().wx_dir, card_data.get('thumbnail')) + thumbnail = "" + if card_data.get("thumbnail"): + thumbnail = os.path.join(Me().wx_dir, card_data.get("thumbnail")) if os.path.exists(thumbnail): - shutil.copy(thumbnail, os.path.join(origin_docx_path, 'image', os.path.basename(thumbnail))) - thumbnail = './image/' + os.path.basename(thumbnail) + shutil.copy( + thumbnail, + os.path.join( + origin_docx_path, "image", os.path.basename(thumbnail) + ), + ) + thumbnail = "./image/" + os.path.basename(thumbnail) else: - thumbnail = '' - app_logo = '' - if card_data.get('app_logo'): - app_logo = os.path.join(Me().wx_dir, card_data.get('app_logo')) + thumbnail = "" + app_logo = "" + if card_data.get("app_logo"): + app_logo = os.path.join(Me().wx_dir, card_data.get("app_logo")) if os.path.exists(app_logo): - shutil.copy(app_logo, os.path.join(origin_docx_path, 'image', os.path.basename(app_logo))) - app_logo = './image/' + os.path.basename(app_logo) + shutil.copy( + app_logo, + os.path.join(origin_docx_path, "image", os.path.basename(app_logo)), + ) + app_logo = "./image/" + os.path.basename(app_logo) else: - app_logo = card_data.get('app_logo') + app_logo = card_data.get("app_logo") doc.write( - f'''{{ type:49,sub_type:5, text:'',is_send:{is_send},avatar_path:'{avatar}',url:'{card_data.get('url')}', + f"""{{ type:49,sub_type:5, text:'',is_send:{is_send},avatar_path:'{avatar}',url:'{card_data.get('url')}', timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}',title:'{card_data.get('title')}', description:'{card_data.get('description')}',thumbnail:'{thumbnail}',app_logo:'{app_logo}', app_name:'{card_data.get('app_name')}' - }},\n''' + }},\n""" + ) + + def transfer(self, doc, message): + is_send = message[4] + timestamp = message[5] + compress_content_ = message[11] + open("test.bin", "wb").write(compress_content_) + transfer_detail = transfer_decompress(compress_content_) + is_chatroom = 1 if self.contact.is_chatroom else 0 + avatar = self.get_avatar_path(is_send, message) + display_name = self.get_display_name(is_send, message) + text_info_map = { + 1: transfer_detail["pay_memo"] or "转账", + 3: "已收款", + 4: "已退还", + } + doc.write( + f"""{{ type:49, sub_type:2000, + text:'{text_info_map[transfer_detail["paysubtype"]]}', + is_send:{is_send},avatar_path:'{avatar}', + timestamp:{timestamp},is_chatroom:{is_chatroom}, + displayname:'{display_name}',paysubtype:{transfer_detail["paysubtype"]}, + pay_memo:'{transfer_detail["pay_memo"]}',feedesc:'{transfer_detail["feedesc"]}', + }},\n""" + ) + + def call(self, doc, message): + is_send = message[4] + timestamp = message[5] + str_content = message[7] + bytes_extra = message[10] + display_content = message[12] + call_detail = call_decompress( + is_send, bytes_extra, display_content, str_content + ) + is_chatroom = 1 if self.contact.is_chatroom else 0 + avatar = self.get_avatar_path(is_send, message) + display_name = self.get_display_name(is_send, message) + doc.write( + f"""{{ type:50, text:'{call_detail["display_content"]}', + call_type:{call_detail["call_type"]},avatar_path:'{avatar}', + timestamp:{timestamp},is_chatroom:{is_chatroom}, + displayname:'{display_name}', + }},\n""" ) def export(self): print(f"【开始导出 HTML {self.contact.remark}】") messages = msg_db.get_messages(self.contact.wxid, time_range=self.time_range) filename = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}/{self.contact.remark}.html" - file_path = './app/resources/data/template.html' + file_path = "./app/resources/data/template.html" if not os.path.exists(file_path): - resource_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__))) - file_path = os.path.join(resource_dir, 'app', 'resources', 'data', 'template.html') + resource_dir = getattr( + sys, "_MEIPASS", os.path.abspath(os.path.dirname(__file__)) + ) + file_path = os.path.join( + resource_dir, "app", "resources", "data", "template.html" + ) with open(file_path, "r", encoding="utf-8") as f: content = f.read() - html_head, html_end = content.split('/*注意看这是分割线*/') - f = open(filename, 'w', encoding='utf-8') - html_head = html_head.replace("出错了", f"{self.contact.remark}") - html_head = html_head.replace("

出错了

", f"

{self.contact.remark}

") + html_head, html_end = content.split("/*注意看这是分割线*/") + f = open(filename, "w", encoding="utf-8") + html_head = html_head.replace( + "出错了", f"{self.contact.remark}" + ) + html_head = html_head.replace( + '

出错了

', f'

{self.contact.remark}

' + ) f.write(html_head) self.rangeSignal.emit(len(messages)) for index, message in enumerate(messages): type_ = message[2] sub_type = message[3] timestamp = message[5] - if (type_ == 3 and self.message_types.get(3)) or (type_ == 34 and self.message_types.get(34)) or ( - type_ == 47 and self.message_types.get(47)): + if ( + (type_ == 3 and self.message_types.get(3)) + or (type_ == 34 and self.message_types.get(34)) + or (type_ == 47 and self.message_types.get(47)) + ): pass else: self.progressSignal.emit(1) @@ -319,6 +403,10 @@ class HtmlExporter(ExporterBase): self.music_share(f, message) elif type_ == 49 and sub_type == 5 and self.message_types.get(4905): self.share_card(f, message) + elif type_ == 49 and sub_type == 2000 and self.message_types.get(492000): + self.transfer(f, message) + elif type_ == 50 and self.message_types.get(50): + self.call(f, message) if index % 2000 == 0: print(f"【导出 HTML {self.contact.remark}】{index}/{len(messages)}") f.write(html_end) @@ -333,7 +421,7 @@ class HtmlExporter(ExporterBase): @return: """ self.num += 1 - print('子线程完成', self.num, '/', self.total_num) + print("子线程完成", self.num, "/", self.total_num) if self.num == self.total_num: # 所有子线程都完成之后就发送完成信号 self.okSignal.emit(1) @@ -343,6 +431,7 @@ class OutputMedia(QThread): """ 导出语音消息 """ + okSingal = pyqtSignal(int) progressSignal = pyqtSignal(int) @@ -357,7 +446,9 @@ class OutputMedia(QThread): is_send = message[4] msgSvrId = message[9] try: - audio_path = media_msg_db.get_audio(msgSvrId, output_path=origin_docx_path + "/voice") + audio_path = media_msg_db.get_audio( + msgSvrId, output_path=origin_docx_path + "/voice" + ) except: logger.error(traceback.format_exc()) finally: @@ -369,6 +460,7 @@ class OutputEmoji(QThread): """ 导出表情包 """ + okSingal = pyqtSignal(int) progressSignal = pyqtSignal(int) @@ -395,6 +487,7 @@ class OutputImage(QThread): """ 导出图片 """ + okSingal = pyqtSignal(int) progressSignal = pyqtSignal(int) @@ -407,10 +500,10 @@ class OutputImage(QThread): def count1(self, num): self.num += 1 - print('图片导出完成一个') + print("图片导出完成一个") if self.num == self.child_thread_num: self.okSingal.emit(47) - print('图片导出完成') + print("图片导出完成") def run(self): origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}" @@ -420,13 +513,19 @@ class OutputImage(QThread): BytesExtra = message[10] timestamp = message[5] try: - image_path = hard_link_db.get_image(str_content, BytesExtra, thumb=False) + image_path = hard_link_db.get_image( + str_content, BytesExtra, thumb=False + ) if not os.path.exists(os.path.join(Me().wx_dir, image_path)): - image_thumb_path = hard_link_db.get_image(str_content, BytesExtra, thumb=True) + image_thumb_path = hard_link_db.get_image( + str_content, BytesExtra, thumb=True + ) if not os.path.exists(os.path.join(Me().wx_dir, image_thumb_path)): continue image_path = image_thumb_path - image_path = get_image(image_path, base_path=f'/data/聊天记录/{self.contact.remark}/image') + image_path = get_image( + image_path, base_path=f"/data/聊天记录/{self.contact.remark}/image" + ) try: os.utime(origin_docx_path + image_path[1:], (timestamp, timestamp)) except: @@ -464,13 +563,19 @@ class OutputImageChild(QThread): BytesExtra = message[10] timestamp = message[5] try: - image_path = hard_link_db.get_image(str_content, BytesExtra, thumb=False) + image_path = hard_link_db.get_image( + str_content, BytesExtra, thumb=False + ) if not os.path.exists(os.path.join(Me().wx_dir, image_path)): - image_thumb_path = hard_link_db.get_image(str_content, BytesExtra, thumb=True) + image_thumb_path = hard_link_db.get_image( + str_content, BytesExtra, thumb=True + ) if not os.path.exists(os.path.join(Me().wx_dir, image_thumb_path)): continue image_path = image_thumb_path - image_path = get_image(image_path, base_path=f'/data/聊天记录/{self.contact.remark}/image') + image_path = get_image( + image_path, base_path=f"/data/聊天记录/{self.contact.remark}/image" + ) try: os.utime(origin_docx_path + image_path[1:], (timestamp, timestamp)) except: @@ -480,4 +585,4 @@ class OutputImageChild(QThread): finally: self.progressSignal.emit(1) self.okSingal.emit(47) - print('图片子线程完成') + print("图片子线程完成") diff --git a/app/DataBase/msg.py b/app/DataBase/msg.py index 8a0f205..5b25a4f 100644 --- a/app/DataBase/msg.py +++ b/app/DataBase/msg.py @@ -35,7 +35,8 @@ def parser_chatroom_message(messages): a[9]: msgSvrId, a[10]: BytesExtra, a[11]: CompressContent, - a[12]: msg_sender, (ContactPC 或 ContactDefault 类型,这个才是群聊里的信息发送人,不是群聊或者自己是发送者没有这个字段) + a[12]: DisplayContent, + a[13]: msg_sender, (ContactPC 或 ContactDefault 类型,这个才是群聊里的信息发送人,不是群聊或者自己是发送者没有这个字段) ''' updated_messages = [] # 用于存储修改后的消息列表 for row in messages: @@ -157,13 +158,15 @@ class Msg: a[9]: msgSvrId, a[10]: BytesExtra, a[11]: CompressContent, + a[12]: DisplayContent, + a[13]: 联系人的类(如果是群聊就有,不是的话没有这个字段) """ if not self.open_flag: return None if time_range: start_time, end_time = time_range sql = f''' - select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime,MsgSvrID,BytesExtra,CompressContent + select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime,MsgSvrID,BytesExtra,CompressContent,DisplayContent from MSG where StrTalker=? {'AND CreateTime>' + str(start_time) + ' AND CreateTime<' + str(end_time) if time_range else ''} diff --git a/app/DataBase/output.py b/app/DataBase/output.py index 6aa91c7..86104c7 100644 --- a/app/DataBase/output.py +++ b/app/DataBase/output.py @@ -118,12 +118,12 @@ class ExporterBase(QThread): def get_avatar_path(self, is_send, message, is_absolute_path=False) -> str: if is_absolute_path: if self.contact.is_chatroom: - avatar = message[12].avatar_path + avatar = message[13].avatar_path else: avatar = Me().avatar_path if is_send else self.contact.avatar_path else: if self.contact.is_chatroom: - avatar = message[12].smallHeadImgUrl + avatar = message[13].smallHeadImgUrl else: avatar = Me().smallHeadImgUrl if is_send else self.contact.smallHeadImgUrl return avatar @@ -133,7 +133,7 @@ class ExporterBase(QThread): if is_send: display_name = Me().name else: - display_name = message[12].remark + display_name = message[13].remark else: display_name = Me().name if is_send else self.contact.remark return escape_js_and_html(display_name) diff --git a/app/DataBase/package_msg.py b/app/DataBase/package_msg.py index a371d89..bd3ed99 100644 --- a/app/DataBase/package_msg.py +++ b/app/DataBase/package_msg.py @@ -107,7 +107,8 @@ class PackageMsg: a[9]: msgSvrId, a[10]: BytesExtra, a[11]: CompressContent, - a[12]: msg_sender, (ContactPC 或 ContactDefault 类型,这个才是群聊里的信息发送人,不是群聊或者自己是发送者没有这个字段) + a[12]: DisplayContent, + a[13]: msg_sender, (ContactPC 或 ContactDefault 类型,这个才是群聊里的信息发送人,不是群聊或者自己是发送者没有这个字段) ''' updated_messages = [] # 用于存储修改后的消息列表 messages = msg_db.get_messages(chatroom_wxid) diff --git a/app/ui/chat/chat_info.py b/app/ui/chat/chat_info.py index f71d5c9..a88232e 100644 --- a/app/ui/chat/chat_info.py +++ b/app/ui/chat/chat_info.py @@ -83,13 +83,13 @@ class ChatInfo(QWidget): def get_avatar_path(self, is_send, message, is_absolute_path=False) -> str: if self.contact.is_chatroom: - avatar = message[12].smallHeadImgUrl + avatar = message[13].smallHeadImgUrl else: avatar = Me().smallHeadImgUrl if is_send else self.contact.smallHeadImgUrl if is_absolute_path: if self.contact.is_chatroom: - # message[12].save_avatar() - avatar = message[12].avatar + # message[13].save_avatar() + avatar = message[13].avatar else: avatar = Me().avatar if is_send else self.contact.avatar return avatar @@ -99,7 +99,7 @@ class ChatInfo(QWidget): if is_send: display_name = Me().name else: - display_name = message[12].remark + display_name = message[13].remark else: display_name = None return display_name diff --git a/app/ui/contact/export/export_dialog.py b/app/ui/contact/export/export_dialog.py index df56700..bb354f8 100644 --- a/app/ui/contact/export/export_dialog.py +++ b/app/ui/contact/export/export_dialog.py @@ -22,7 +22,9 @@ types = { '音乐与音频': 4903, '文件': 4906, '分享卡片': 4905, - '拍一拍等系统消息': 10000 + '转账': 492000, + '音视频通话': 50, + '拍一拍等系统消息': 10000, } Stylesheet = """ QPushButton{ @@ -53,7 +55,7 @@ class ExportDialog(QDialog, Ui_Dialog): if file_type == 'html': self.export_type = Output.HTML self.export_choices = {"文本": True, "图片": True, "语音": False, "视频": False, "表情包": False, - '音乐与音频': False, '分享卡片': False, '文件': False, + '音乐与音频': False, '分享卡片': False, '文件': False, '转账': False, '音视频通话': False, '拍一拍等系统消息': True} # 定义导出的数据类型,默认全部选择 elif file_type == 'csv': self.export_type = Output.CSV diff --git a/app/ui/menu/export.py b/app/ui/menu/export.py index b168de0..788b6a1 100644 --- a/app/ui/menu/export.py +++ b/app/ui/menu/export.py @@ -26,7 +26,9 @@ types = { '音乐与音频': 4903, '文件': 4906, '分享卡片': 4905, - '拍一拍等系统消息': 10000 + '转账': 492000, + '音视频通话': 50, + '拍一拍等系统消息': 10000, } file_format = { 'Docx': Output.DOCX, @@ -69,7 +71,7 @@ class ExportDialog(QDialog, Ui_Dialog): self.textBrowser.setVerticalScrollBar(scroll_bar) self.export_choices = {"文本": True, "图片": True, "语音": False, "视频": False, "表情包": False, '音乐与音频': False, '分享卡片': False, '文件': False, - '拍一拍等系统消息': True} # 定义导出的数据类型,默认全部选择 + '转账': False, '音视频通话': False, '拍一拍等系统消息': True} # 定义导出的数据类型 self.setWindowTitle(title) self.checkBox_word.setEnabled(False) self.checkBox_word.setText('Docx(暂时不可用)') diff --git a/app/util/compress_content.py b/app/util/compress_content.py index 31a5b94..69f49c5 100644 --- a/app/util/compress_content.py +++ b/app/util/compress_content.py @@ -19,31 +19,32 @@ def decompress_CompressContent(data): :return: """ if data is None or not isinstance(data, bytes): - return '' + return "" try: dst = lz4.block.decompress(data, uncompressed_size=len(data) << 10) - decoded_string = dst.decode().replace('\x00', '') # Remove any null characters + decoded_string = dst.decode().replace("\x00", "") # Remove any null characters except: - print("Decompression failed: potentially corrupt input or insufficient buffer size.") - return '' + print( + "Decompression failed: potentially corrupt input or insufficient buffer size." + ) + return "" return decoded_string def escape_js_and_html(input_str): if not input_str: - return '' + return "" # 转义HTML特殊字符 html_escaped = html.escape(input_str, quote=False) # 手动处理JavaScript转义字符 js_escaped = ( - html_escaped - .replace("\\", "\\\\") + html_escaped.replace("\\", "\\\\") .replace("'", r"\'") - .replace('"', r'\"') - .replace("\n", r'\n') - .replace("\r", r'\r') - .replace("\t", r'\t') + .replace('"', r"\"") + .replace("\n", r"\n") + .replace("\r", r"\r") + .replace("\t", r"\t") ) return js_escaped @@ -53,168 +54,232 @@ def parser_reply(data: bytes): xml_content = decompress_CompressContent(data) if not xml_content: return { - 'type': 57, - 'title': "发生错误", - 'refer': { - 'type': '1', - 'content': '引用错误', - 'displayname': '用户名', + "type": 57, + "title": "发生错误", + "refer": { + "type": "1", + "content": "引用错误", + "displayname": "用户名", }, - "is_error": True + "is_error": True, } try: root = ET.XML(xml_content) - appmsg = root.find('appmsg') - msg_type = int(appmsg.find('type').text) - title = appmsg.find('title').text - refermsg_content = appmsg.find('refermsg').find('content').text - refermsg_type = int(appmsg.find('refermsg').find('type').text) - refermsg_displayname = appmsg.find('refermsg').find('displayname').text + appmsg = root.find("appmsg") + msg_type = int(appmsg.find("type").text) + title = appmsg.find("title").text + refermsg_content = appmsg.find("refermsg").find("content").text + refermsg_type = int(appmsg.find("refermsg").find("type").text) + refermsg_displayname = appmsg.find("refermsg").find("displayname").text return { - 'type': msg_type, - 'title': title, - 'refer': None if refermsg_type != 1 else { - 'type': refermsg_type, - 'content': refermsg_content.lstrip("\n"), - 'displayname': refermsg_displayname, + "type": msg_type, + "title": title, + "refer": None + if refermsg_type != 1 + else { + "type": refermsg_type, + "content": refermsg_content.lstrip("\n"), + "displayname": refermsg_displayname, }, - "is_error": False + "is_error": False, } except: return { - 'type': 57, - 'title': "发生错误", - 'refer': { - 'type': '1', - 'content': '引用错误', - 'displayname': '用户名', + "type": 57, + "title": "发生错误", + "refer": { + "type": "1", + "content": "引用错误", + "displayname": "用户名", }, - "is_error": True + "is_error": True, } def music_share(data: bytes): xml_content = decompress_CompressContent(data) if not xml_content: - return { - 'type': 3, - 'title': "发生错误", - "is_error": True - } + return {"type": 3, "title": "发生错误", "is_error": True} try: root = ET.XML(xml_content) - appmsg = root.find('appmsg') - msg_type = int(appmsg.find('type').text) - title = appmsg.find('title').text + appmsg = root.find("appmsg") + msg_type = int(appmsg.find("type").text) + title = appmsg.find("title").text if len(title) >= 39: - title = title[:38] + '...' - artist = appmsg.find('des').text - link_url = appmsg.find('url').text # 链接地址 - audio_url = get_audio_url(appmsg.find('dataurl').text) # 播放地址 + title = title[:38] + "..." + artist = appmsg.find("des").text + link_url = appmsg.find("url").text # 链接地址 + audio_url = get_audio_url(appmsg.find("dataurl").text) # 播放地址 website_name = get_website_name(link_url) return { - 'type': msg_type, - 'title': escape_js_and_html(title), - 'artist': escape_js_and_html(artist), - 'link_url': link_url, - 'audio_url': audio_url, - 'website_name': escape_js_and_html(website_name), - "is_error": False + "type": msg_type, + "title": escape_js_and_html(title), + "artist": escape_js_and_html(artist), + "link_url": link_url, + "audio_url": audio_url, + "website_name": escape_js_and_html(website_name), + "is_error": False, } except Exception as e: print(f"Music Share Error: {e}") - return { - 'type': 3, - 'title': "发生错误", - "is_error": True - } + return {"type": 3, "title": "发生错误", "is_error": True} def share_card(bytesExtra, compress_content_): - title, des, url, show_display_name, thumbnail, app_logo = '', '', '', '', '', '' + title, des, url, show_display_name, thumbnail, app_logo = "", "", "", "", "", "" try: xml = decompress_CompressContent(compress_content_) root = ET.XML(xml) - appmsg = root.find('appmsg') - title = appmsg.find('title').text + appmsg = root.find("appmsg") + title = appmsg.find("title").text try: - des = appmsg.find('des').text + des = appmsg.find("des").text except: - des = '' - url = appmsg.find('url').text - appinfo = root.find('appinfo') - show_display_name = appmsg.find('sourcedisplayname') - sourceusername = appmsg.find('sourceusername') + des = "" + url = appmsg.find("url").text + appinfo = root.find("appinfo") + show_display_name = appmsg.find("sourcedisplayname") + sourceusername = appmsg.find("sourceusername") if show_display_name is not None: show_display_name = show_display_name.text else: if appinfo is not None: - show_display_name = appinfo.find('appname').text + show_display_name = appinfo.find("appname").text msg_bytes = MessageBytesExtra() msg_bytes.ParseFromString(bytesExtra) - app_logo = '' - thumbnail = '' + app_logo = "" + thumbnail = "" for tmp in msg_bytes.message2: if tmp.field1 == 3: thumbnail = tmp.field2 - thumbnail = "\\".join(thumbnail.split('\\')[1:]) + thumbnail = "\\".join(thumbnail.split("\\")[1:]) if tmp.field2 == 4: app_logo = tmp.field2 - app_logo = "\\".join(app_logo.split('\\')[1:]) + app_logo = "\\".join(app_logo.split("\\")[1:]) if sourceusername is not None: from app.DataBase import micro_msg_db # 放上面会导致循环依赖 + contact = micro_msg_db.get_contact_by_username(sourceusername.text) if contact: app_logo = contact[7] finally: return { - 'title': escape_js_and_html(title), - 'description': escape_js_and_html(des), - 'url': escape_js_and_html(url), - 'app_name': escape_js_and_html(show_display_name), - 'thumbnail': thumbnail, - 'app_logo': app_logo + "title": escape_js_and_html(title), + "description": escape_js_and_html(des), + "url": escape_js_and_html(url), + "app_name": escape_js_and_html(show_display_name), + "thumbnail": thumbnail, + "app_logo": app_logo, } +def transfer_decompress(compress_content_): + """ + return dict + feedesc: 钱数,str类型,包含一个前缀币种符号(除人民币¥之外未测试); + pay_memo: 转账备注; + receiver_username: 接受转账人的 wxid; (因为电脑上只有私聊页面会显示收款所以这个字段没有也罢,不要轻易使用,因为可能为空) + paysubtype: int 类型,1 为发出转账,3 为接受转账,4 为退还转账; + """ + feedesc, pay_memo, receiver_username, paysubtype = "", "", "", "" + try: + xml = decompress_CompressContent(compress_content_) + root = ET.XML(xml) + appmsg = root.find("appmsg") + wcpayinfo = appmsg.find("wcpayinfo") + paysubtype = int(wcpayinfo.find("paysubtype").text) + feedesc = wcpayinfo.find("feedesc").text + pay_memo = wcpayinfo.find("pay_memo").text + receiver_username = wcpayinfo.find("receiver_username").text + finally: + return { + "feedesc": feedesc, + "pay_memo": escape_js_and_html(pay_memo), + "receiver_username": receiver_username, + "paysubtype": paysubtype, + } + + +def call_decompress(is_send, bytes_extra, display_content, str_content): # 音视频通话 + """ + return dict + call_type: int 类型,0 为视频,1为语音; (返回为 2 是未知错误) + display_content: str 类型,页面显示的话; + """ + call_type = 2 + call_length = 0 + msg_bytes = MessageBytesExtra() + msg_bytes.ParseFromString(bytes_extra) + # message2 字段 1: 发送人wxid; 字段 3: "1"是语音,"0"是视频; 字段 4: 通话时长 + for i in msg_bytes.message2: + if i.field1 == 3: + call_type = int(i.field2) + elif i.field1 == 4: + call_length = int(i.field2) + + try: + if display_content == "": + if str_content == "11": + h, m, s = ( + call_length // 3600, + (call_length % 3600) // 60, + call_length % 60, + ) + display_content = f"通话时长 {f'{h}:' if h else ''}{m:02d}:{s:02d}" + else: + display_content = { + "5": ("" if is_send else "对方") + "已取消", + "8": ("对方" if is_send else "") + "已拒绝", + "7": "已在其他设备接听", + "12": "已在其他设备拒绝", + }[str_content] + except KeyError: + display_content = "未知类型,您可以把这条消息对应的微信界面消息反馈给我们" + + return { + "call_type": call_type, + "display_content": display_content, + } + + def get_website_name(url): parsed_url = urlparse(url) domain = f"{parsed_url.scheme}://{parsed_url.netloc}" - website_name = '' + website_name = "" try: response = requests.get(domain, allow_redirects=False) if response.status_code == 200: - soup = BeautifulSoup(response.content, 'html.parser') + soup = BeautifulSoup(response.content, "html.parser") website_name = soup.title.string.strip() elif response.status_code == 302: - domain = response.headers['Location'] + domain = response.headers["Location"] response = requests.get(domain, allow_redirects=False) - soup = BeautifulSoup(response.content, 'html.parser') + soup = BeautifulSoup(response.content, "html.parser") website_name = soup.title.string.strip() else: response = requests.get(url, allow_redirects=False) if response.status_code == 200: - soup = BeautifulSoup(response.content, 'html.parser') + soup = BeautifulSoup(response.content, "html.parser") website_name = soup.title.string.strip() index = website_name.find("-") if index != -1: # 如果找到了 "-" - website_name = website_name[index + 1:].strip() + website_name = website_name[index + 1 :].strip() except Exception as e: print(f"Get Website Info Error: {e}") return website_name def get_audio_url(url): - path = '' + path = "" try: response = requests.get(url, allow_redirects=False) # 检查响应状态码 if response.status_code == 302: - path = response.headers['Location'] + path = response.headers["Location"] elif response.status_code == 200: - print('音乐文件已失效,url:' + url) + print("音乐文件已失效,url:" + url) else: - print('音乐文件地址获取失败,url:' + url + ',状态码' + str(response.status_code)) + print("音乐文件地址获取失败,url:" + url + ",状态码" + str(response.status_code)) except Exception as e: print(f"Get Audio Url Error: {e}") return path @@ -223,45 +288,37 @@ def get_audio_url(url): def file(bytes_extra, compress_content, output_path): xml_content = decompress_CompressContent(compress_content) if not xml_content: - return { - 'type': 6, - 'title': "发生错误", - "is_error": True - } + return {"type": 6, "title": "发生错误", "is_error": True} try: root = ET.XML(xml_content) - appmsg = root.find('appmsg') - msg_type = int(appmsg.find('type').text) - file_name = appmsg.find('title').text + appmsg = root.find("appmsg") + msg_type = int(appmsg.find("type").text) + file_name = appmsg.find("title").text pattern = r'[\\/:*?"<>|\r\n]+' file_name = re.sub(pattern, "_", file_name) - appattach = appmsg.find('appattach') - file_len = int(appattach.find('totallen').text) - app_name = '' + appattach = appmsg.find("appattach") + file_len = int(appattach.find("totallen").text) + app_name = "" file_len = format_bytes(file_len) - file_ext = appattach.find('fileext').text + file_ext = appattach.find("fileext").text if root.find("appinfo") is not None: - app_info = root.find('appinfo') - app_name = app_info.find('appname').text + app_info = root.find("appinfo") + app_name = app_info.find("appname").text if app_name is None: - app_name = '' + app_name = "" file_path = get_file(bytes_extra, file_name, output_path) return { - 'type': msg_type, - 'file_name': escape_js_and_html(file_name), - 'file_len': file_len, - 'file_ext': file_ext, - 'file_path': file_path, - 'app_name': escape_js_and_html(app_name), - "is_error": False + "type": msg_type, + "file_name": escape_js_and_html(file_name), + "file_len": file_len, + "file_ext": file_ext, + "file_path": file_path, + "app_name": escape_js_and_html(app_name), + "is_error": False, } except Exception as e: print(f"File Get Info Error: {e}") - return { - 'type': 6, - 'title': "发生错误", - "is_error": True - } + return {"type": 6, "title": "发生错误", "is_error": True} def format_bytes(size): From a440aee16867e4e98b50a5cd037f228b79c16ca3 Mon Sep 17 00:00:00 2001 From: STDquantum <405720329@qq.com> Date: Thu, 18 Jan 2024 13:57:43 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E5=B0=8F=E6=97=B6=E4=B9=9F=E8=B5=B7?= =?UTF-8?q?=E7=A0=81=E4=B8=A4=E4=BD=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/util/compress_content.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/util/compress_content.py b/app/util/compress_content.py index 69f49c5..f21af90 100644 --- a/app/util/compress_content.py +++ b/app/util/compress_content.py @@ -225,7 +225,7 @@ def call_decompress(is_send, bytes_extra, display_content, str_content): # 音 (call_length % 3600) // 60, call_length % 60, ) - display_content = f"通话时长 {f'{h}:' if h else ''}{m:02d}:{s:02d}" + display_content = f"通话时长 {f'{h:02d}:' if h else ''}{m:02d}:{s:02d}" else: display_content = { "5": ("" if is_send else "对方") + "已取消", From 9d69f0e4cb6f8293927f1f5447f33b9e1d925163 Mon Sep 17 00:00:00 2001 From: STDquantum <405720329@qq.com> Date: Fri, 19 Jan 2024 18:37:56 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/DataBase/exporter_docx.py | 94 ++++++++++++++++-- app/DataBase/exporter_html.py | 177 ++++++++++++---------------------- app/DataBase/output.py | 8 +- app/DataBase/output_pc.py | 6 +- 4 files changed, 160 insertions(+), 125 deletions(-) diff --git a/app/DataBase/exporter_docx.py b/app/DataBase/exporter_docx.py index bd81d1f..48ebfdb 100644 --- a/app/DataBase/exporter_docx.py +++ b/app/DataBase/exporter_docx.py @@ -25,6 +25,7 @@ encoded_chars = b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0e\x0f\x10\x11\x # 创建一个字典,将要删除的字符映射为 None char_mapping = {char: None for char in encoded_chars} + def filter_control_characters(input_string): """ 过滤掉不可打印字符 @@ -39,6 +40,84 @@ def filter_control_characters(input_string): class DocxExporter(ExporterBase): + def merge_docx(self, n): + self.process_num += 1 + conRemark = self.contact.remark + origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{conRemark}" + filename = f"{origin_docx_path}/{conRemark}_{n}.docx" + # print(all_file_path) + doc = docx.Document(filename) + if self.merged_doc_index == [-1, -1]: + self.document.append(doc) + self.merged_doc_index = [n, n] + else: + if n == self.merged_doc_index[0] - 1: + self.document.insert(0, doc) + self.merged_doc_index[0] -= 1 + elif n == self.merged_doc_index[1] + 1: + self.document.append(doc) + self.merged_doc_index[1] += 1 + else: + self.docs.append([doc, n]) + self.docs_set.add(n) + new_docx = [] + new_set = set() + # print(self.docs) + while new_set!=self.docs_set: + self.docs.sort(key=lambda x: x[1]) + for doc_, index in self.docs: + if index == self.merged_doc_index[0] - 1: + self.document.insert(0, doc_) + self.merged_doc_index[0] -= 1 + elif index == self.merged_doc_index[1] + 1: + self.document.append(doc_) + self.merged_doc_index[1] += 1 + else: + new_docx.append([doc_, index]) + new_set.add(index) + self.docs = new_docx + self.docs_set = new_set + os.remove(filename) + if self.process_num == self.child_thread_num: + # self.document.append(self.document) + file = os.path.join(origin_docx_path, f'{conRemark}.docx') + try: + self.document.save(file) + except PermissionError: + file = file[:-5] + f'{time.time()}' + '.docx' + self.document.save(file) + self.okSignal.emit(1) + + def export(self): + self.child_threads = [] + messages = msg_db.get_messages(self.contact.wxid, time_range=self.time_range) + # 计算每个子列表的长度 + num = 1 + # num = len(messages) // 500 +1 + sublist_length = len(messages) // num + + # 使用列表切片将列表分成n个子列表 + divided_list = [messages[i:i + sublist_length] for i in range(0, len(messages), sublist_length)] + self.child_thread_num = len(divided_list) + self.process_num = 0 + doc = docx.Document() + doc.styles["Normal"].font.name = "Cambria" + doc.styles["Normal"]._element.rPr.rFonts.set(qn("w:eastAsia"), "宋体") + self.document = Composer(doc) + self.merged_doc_index = [-1, -1] + self.docs = [] + self.docs_set = set() + # self.document.append(self.document) + for i in range(self.child_thread_num): + child_thread = DocxExporterChildThread(self.contact, type_=self.DOCX, message_types=self.message_types, + time_range=self.time_range, messages=divided_list[i], index=i) + self.child_threads.append(child_thread) + child_thread.okSignal.connect(self.merge_docx) + child_thread.progressSignal.connect(self.progressSignal) + child_thread.start() + + +class DocxExporterChildThread(ExporterBase): def text(self, doc, message): type_ = message[2] str_content = message[7] @@ -313,15 +392,15 @@ class DocxExporter(ExporterBase): def export(self): print(f"【开始导出 DOCX {self.contact.remark}】") origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}" - messages = msg_db.get_messages(self.contact.wxid, time_range=self.time_range) + messages = self.messages Me().save_avatar(os.path.join(f"{origin_docx_path}/avatar/{Me().wxid}.png")) if self.contact.is_chatroom: for message in messages: if message[4]: # is_send continue try: - chatroom_avatar_path = f"{origin_docx_path}/avatar/{message[13].wxid}.png" - message[13].save_avatar(chatroom_avatar_path) + chatroom_avatar_path = f"{origin_docx_path}/avatar/{message[12].wxid}.png" + message[12].save_avatar(chatroom_avatar_path) except: print(message) pass @@ -329,6 +408,8 @@ class DocxExporter(ExporterBase): self.contact.save_avatar(os.path.join(f"{origin_docx_path}/avatar/{self.contact.wxid}.png")) self.rangeSignal.emit(len(messages)) + index = 0 + def newdoc(): nonlocal n, doc doc = docx.Document() @@ -347,8 +428,8 @@ class DocxExporter(ExporterBase): for index, message in enumerate(messages): if index % 200 == 0 and index: # doc = document.add_paragraph() - filename = os.path.join(origin_docx_path, f"{self.contact.remark}{n}.docx") - doc.save(filename) + # filename = os.path.join(origin_docx_path, f"{self.contact.remark}{n}.docx") + # doc.save(filename) newdoc() type_ = message[2] @@ -391,6 +472,7 @@ class DocxExporter(ExporterBase): for index, dx in enumerate(docs[::-1]): print(f"【合并 DOCX {self.contact.remark}】{index + 1}/{len(docs)}") doc.insert(0, dx) + filename = os.path.join(origin_docx_path, f"{self.contact.remark}_{self.index}.docx") try: # document.save(filename) doc.save(filename) @@ -399,4 +481,4 @@ class DocxExporter(ExporterBase): # document.save(filename) doc.save(filename) print(f"【完成导出 DOCX {self.contact.remark}】") - self.okSignal.emit(1) + self.okSignal.emit(self.index) diff --git a/app/DataBase/exporter_html.py b/app/DataBase/exporter_html.py index bd8cb98..2d799f7 100644 --- a/app/DataBase/exporter_html.py +++ b/app/DataBase/exporter_html.py @@ -11,26 +11,19 @@ from app.DataBase.output import ExporterBase, escape_js_and_html from app.log import logger from app.person import Me from app.util import path -from app.util.compress_content import ( - parser_reply, - share_card, - music_share, - file, - transfer_decompress, - call_decompress, -) +from app.util.compress_content import parser_reply, share_card, music_share, file, transfer_decompress, call_decompress from app.util.emoji import get_emoji_url from app.util.image import get_image_path, get_image from app.util.music import get_music_path icon_files = { - "./icon/word.png": ["doc", "docx"], - "./icon/excel.png": ["xls", "xlsx"], - "./icon/csv.png": ["csv"], - "./icon/txt.png": ["txt"], - "./icon/zip.png": ["zip", "7z", "rar"], - "./icon/ppt.png": ["ppt", "pptx"], - "./icon/pdf.png": ["pdf"], + './icon/word.png': ['doc', 'docx'], + './icon/excel.png': ['xls', 'xlsx'], + './icon/csv.png': ['csv'], + './icon/txt.png': ['txt'], + './icon/zip.png': ['zip', '7z', 'rar'], + './icon/ppt.png': ['ppt', 'pptx'], + './icon/pdf.png': ['pdf'], } @@ -47,7 +40,7 @@ class HtmlExporter(ExporterBase): avatar = self.get_avatar_path(is_send, message) str_content = escape_js_and_html(str_content) doc.write( - f"""{{ type:{1}, text: '{str_content}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},""" + f'''{{ type:{1}, text: '{str_content}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},''' ) def image(self, doc, message): @@ -64,17 +57,13 @@ class HtmlExporter(ExporterBase): str_content = escape_js_and_html(str_content) image_path = hard_link_db.get_image(str_content, BytesExtra, thumb=False) if not os.path.exists(os.path.join(Me().wx_dir, image_path)): - image_thumb_path = hard_link_db.get_image( - str_content, BytesExtra, thumb=True - ) + image_thumb_path = hard_link_db.get_image(str_content, BytesExtra, thumb=True) if not os.path.exists(os.path.join(Me().wx_dir, image_thumb_path)): return image_path = image_thumb_path - image_path = get_image_path( - image_path, base_path=f"/data/聊天记录/{self.contact.remark}/image" - ) + image_path = get_image_path(image_path, base_path=f'/data/聊天记录/{self.contact.remark}/image') doc.write( - f"""{{ type:{type_}, text: '{image_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},""" + f'''{{ type:{type_}, text: '{image_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},''' ) def audio(self, doc, message): @@ -88,9 +77,7 @@ class HtmlExporter(ExporterBase): avatar = self.get_avatar_path(is_send, message) display_name = self.get_display_name(is_send, message) try: - audio_path = media_msg_db.get_audio_path( - msgSvrId, output_path=origin_docx_path + "/voice" - ) + audio_path = media_msg_db.get_audio_path(msgSvrId, output_path=origin_docx_path + "/voice") audio_path = "./voice/" + os.path.basename(audio_path) except: logger.error(traceback.format_exc()) @@ -99,7 +86,7 @@ class HtmlExporter(ExporterBase): if voice_to_text and voice_to_text != "": voice_to_text = escape_js_and_html(voice_to_text) doc.write( - f"""{{ type:34, text:'{audio_path}',is_send:{is_send},avatar_path:'{avatar}',voice_to_text:'{voice_to_text}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},""" + f'''{{ type:34, text:'{audio_path}',is_send:{is_send},avatar_path:'{avatar}',voice_to_text:'{voice_to_text}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},''' ) def emoji(self, doc, message): @@ -112,7 +99,7 @@ class HtmlExporter(ExporterBase): display_name = self.get_display_name(is_send, message) emoji_path = get_emoji_url(str_content, thumb=True) doc.write( - f"""{{ type:{3}, text: '{emoji_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},""" + f'''{{ type:{3}, text: '{emoji_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},''' ) def file(self, doc, message): @@ -125,27 +112,22 @@ class HtmlExporter(ExporterBase): is_chatroom = 1 if self.contact.is_chatroom else 0 avatar = self.get_avatar_path(is_send, message) display_name = self.get_display_name(is_send, message) - file_info = file( - bytesExtra, compress_content, output_path=origin_docx_path + "/file" - ) - if file_info.get("is_error") == False: + file_info = file(bytesExtra, compress_content, output_path=origin_docx_path + '/file') + if file_info.get('is_error') == False: icon_path = None for icon, extensions in icon_files.items(): - if file_info.get("file_ext") in extensions: + if file_info.get('file_ext') in extensions: icon_path = icon break # 如果没有与文件后缀匹配的图标,则使用默认图标 if icon_path is None: - default_icon = "./icon/file.png" + default_icon = './icon/file.png' icon_path = default_icon - file_path = file_info.get("file_path") + file_path = file_info.get('file_path') if file_path != "": - file_path = "./file/" + file_info.get("file_name") + file_path = './file/' + file_info.get('file_name') doc.write( - f"""{{ type:49, text: '{file_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp} - ,is_chatroom:{is_chatroom},displayname:'{display_name}',icon_path: '{icon_path}' - ,sub_type:6,file_name: '{file_info.get('file_name')}',file_size: '{file_info.get('file_len')}' - ,app_name: '{file_info.get('app_name')}'}},""" + f'''{{ type:49, text: '{file_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}',icon_path: '{icon_path}',sub_type:6,file_name: '{file_info.get('file_name')}',file_size: '{file_info.get('file_len')}',app_name: '{file_info.get('app_name')}'}},''' ) def refermsg(self, doc, message): @@ -158,20 +140,20 @@ class HtmlExporter(ExporterBase): str_time = message[8] is_send = message[4] content = parser_reply(message[11]) - refer_msg = content.get("refer") + refer_msg = content.get('refer') timestamp = message[5] is_chatroom = 1 if self.contact.is_chatroom else 0 avatar = self.get_avatar_path(is_send, message) display_name = self.get_display_name(is_send, message) - contentText = escape_js_and_html(content.get("title")) + contentText = escape_js_and_html(content.get('title')) if refer_msg: referText = f"{escape_js_and_html(refer_msg.get('displayname'))}:{escape_js_and_html(refer_msg.get('content'))}" doc.write( - f"""{{ type:49, text: '{contentText}',is_send:{is_send},sub_type:{content.get('type')},refer_text: '{referText}',avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},""" + f'''{{ type:49, text: '{contentText}',is_send:{is_send},sub_type:{content.get('type')},refer_text: '{referText}',avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},''' ) else: doc.write( - f"""{{ type:49, text: '{contentText}',is_send:{is_send},sub_type:{content.get('type')},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},""" + f'''{{ type:49, text: '{contentText}',is_send:{is_send},sub_type:{content.get('type')},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},''' ) def system_msg(self, doc, message): @@ -181,15 +163,14 @@ class HtmlExporter(ExporterBase): timestamp = message[5] is_chatroom = 1 if self.contact.is_chatroom else 0 - str_content = str_content.replace("重新编辑]]>', "" - ) - res = findall("()", str_content) + str_content = str_content.replace('重新编辑]]>', "") + res = findall('()', str_content) for xmlstr, b in res: str_content = str_content.replace(xmlstr, "") str_content = escape_js_and_html(str_content) doc.write( - f"""{{ type:0, text: '{str_content}',is_send:{is_send},avatar_path:'',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:''}},""" + f'''{{ type:0, text: '{str_content}',is_send:{is_send},avatar_path:'',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:''}},''' ) def video(self, doc, message): @@ -206,32 +187,30 @@ class HtmlExporter(ExporterBase): video_path = hard_link_db.get_video(str_content, BytesExtra, thumb=False) image_path = hard_link_db.get_video(str_content, BytesExtra, thumb=True) 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 = path.get_relative_path(image_path, base_path=f'/data/聊天记录/{self.contact.remark}/image') try: # todo 网络图片问题 print(origin_docx_path + image_path[1:]) os.utime(origin_docx_path + image_path[1:], (timestamp, timestamp)) doc.write( - f"""{{ type:3, text: '{image_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},""" + f'''{{ type:3, text: '{image_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},''' ) except: doc.write( - f"""{{ type:1, text: '视频丢失',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},""" + f'''{{ type:1, text: '视频丢失',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},''' ) return if video_path is None and image_path is None: return - video_path = f"{Me().wx_dir}/{video_path}" + video_path = f'{Me().wx_dir}/{video_path}' if os.path.exists(video_path): - new_path = origin_docx_path + "/video/" + os.path.basename(video_path) + new_path = origin_docx_path + '/video/' + os.path.basename(video_path) if not os.path.exists(new_path): - shutil.copy(video_path, os.path.join(origin_docx_path, "video")) + shutil.copy(video_path, os.path.join(origin_docx_path, 'video')) os.utime(new_path, (timestamp, timestamp)) - video_path = f"./video/{os.path.basename(video_path)}" + video_path = f'./video/{os.path.basename(video_path)}' doc.write( - f"""{{ type:{type_}, text: '{video_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},""" + f'''{{ type:{type_}, text: '{video_path}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}'}},''' ) def music_share(self, doc, message): @@ -239,24 +218,20 @@ class HtmlExporter(ExporterBase): is_send = message[4] timestamp = message[5] content = music_share(message[11]) - music_path = "" - if content.get("is_error") == False: - if content.get("audio_url") != "": - music_path = get_music_path( - content.get("audio_url"), - content.get("title"), - output_path=origin_docx_path + "/music", - ) - if music_path != "": - music_path = f"./music/{os.path.basename(music_path)}" - music_path = music_path.replace("\\", "/") + music_path = '' + if content.get('is_error') == False: + if content.get('audio_url') != '': + music_path = get_music_path(content.get('audio_url'), content.get('title'), + output_path=origin_docx_path + '/music') + if music_path != '': + music_path = f'./music/{os.path.basename(music_path)}' + music_path = music_path.replace('\\', '/') is_chatroom = 1 if self.contact.is_chatroom else 0 avatar = self.get_avatar_path(is_send, message) display_name = self.get_display_name(is_send, message) + music_path = escape_js_and_html(music_path) doc.write( - f"""{{ type:49, text:'{music_path}',is_send:{is_send},avatar_path:'{avatar}',link_url:'{content.get('link_url')}', - timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}',sub_type:3,title:'{content.get('title')}', - artist:'{content.get('artist')}', website_name:'{content.get('website_name')}'}},""" + f'''{{ type:49, text:'{music_path}',is_send:{is_send},avatar_path:'{avatar}',link_url:'{content.get('link_url')}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}',sub_type:3,title:'{content.get('title')}',artist:'{content.get('artist')}', website_name:'{content.get('website_name')}'}},''' ) def share_card(self, doc, message): @@ -269,36 +244,24 @@ class HtmlExporter(ExporterBase): is_chatroom = 1 if self.contact.is_chatroom else 0 avatar = self.get_avatar_path(is_send, message) display_name = self.get_display_name(is_send, message) - thumbnail = "" - if card_data.get("thumbnail"): - thumbnail = os.path.join(Me().wx_dir, card_data.get("thumbnail")) + thumbnail = '' + if card_data.get('thumbnail'): + thumbnail = os.path.join(Me().wx_dir, card_data.get('thumbnail')) if os.path.exists(thumbnail): - shutil.copy( - thumbnail, - os.path.join( - origin_docx_path, "image", os.path.basename(thumbnail) - ), - ) - thumbnail = "./image/" + os.path.basename(thumbnail) + shutil.copy(thumbnail, os.path.join(origin_docx_path, 'image', os.path.basename(thumbnail))) + thumbnail = './image/' + os.path.basename(thumbnail) else: - thumbnail = "" - app_logo = "" - if card_data.get("app_logo"): - app_logo = os.path.join(Me().wx_dir, card_data.get("app_logo")) + thumbnail = '' + app_logo = '' + if card_data.get('app_logo'): + app_logo = os.path.join(Me().wx_dir, card_data.get('app_logo')) if os.path.exists(app_logo): - shutil.copy( - app_logo, - os.path.join(origin_docx_path, "image", os.path.basename(app_logo)), - ) - app_logo = "./image/" + os.path.basename(app_logo) + shutil.copy(app_logo, os.path.join(origin_docx_path, 'image', os.path.basename(app_logo))) + app_logo = './image/' + os.path.basename(app_logo) else: - app_logo = card_data.get("app_logo") + app_logo = card_data.get('app_logo') doc.write( - f"""{{ type:49,sub_type:5, text:'',is_send:{is_send},avatar_path:'{avatar}',url:'{card_data.get('url')}', - timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}',title:'{card_data.get('title')}', - description:'{card_data.get('description')}',thumbnail:'{thumbnail}',app_logo:'{app_logo}', - app_name:'{card_data.get('app_name')}' - }},\n""" + f'''{{ type:49,sub_type:5, text:'',is_send:{is_send},avatar_path:'{avatar}',url:'{card_data.get('url')}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}',title:'{card_data.get('title')}',description:'{card_data.get('description')}',thumbnail:'{thumbnail}',app_logo:'{app_logo}',app_name:'{card_data.get('app_name')}'}},\n''' ) def transfer(self, doc, message): @@ -315,15 +278,7 @@ class HtmlExporter(ExporterBase): 3: "已收款", 4: "已退还", } - doc.write( - f"""{{ type:49, sub_type:2000, - text:'{text_info_map[transfer_detail["paysubtype"]]}', - is_send:{is_send},avatar_path:'{avatar}', - timestamp:{timestamp},is_chatroom:{is_chatroom}, - displayname:'{display_name}',paysubtype:{transfer_detail["paysubtype"]}, - pay_memo:'{transfer_detail["pay_memo"]}',feedesc:'{transfer_detail["feedesc"]}', - }},\n""" - ) + doc.write(f"""{{ type:49, sub_type:2000,text:'{text_info_map[transfer_detail["paysubtype"]]}',is_send:{is_send},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}',paysubtype:{transfer_detail["paysubtype"]},pay_memo:'{transfer_detail["pay_memo"]}',feedesc:'{transfer_detail["feedesc"]}',}},\n""") def call(self, doc, message): is_send = message[4] @@ -337,13 +292,7 @@ class HtmlExporter(ExporterBase): is_chatroom = 1 if self.contact.is_chatroom else 0 avatar = self.get_avatar_path(is_send, message) display_name = self.get_display_name(is_send, message) - doc.write( - f"""{{ type:50, text:'{call_detail["display_content"]}', - call_type:{call_detail["call_type"]},avatar_path:'{avatar}', - timestamp:{timestamp},is_chatroom:{is_chatroom}, - displayname:'{display_name}', - }},\n""" - ) + doc.write(f"""{{ type:50, text:'{call_detail["display_content"]}',call_type:{call_detail["call_type"]},avatar_path:'{avatar}',timestamp:{timestamp},is_chatroom:{is_chatroom},displayname:'{display_name}',}},\n""") def export(self): print(f"【开始导出 HTML {self.contact.remark}】") diff --git a/app/DataBase/output.py b/app/DataBase/output.py index 86104c7..359ee4a 100644 --- a/app/DataBase/output.py +++ b/app/DataBase/output.py @@ -90,19 +90,23 @@ class ExporterBase(QThread): CONTACT_CSV = 4 TXT = 5 - def __init__(self, contact, type_=DOCX, message_types={},time_range=None, parent=None): + def __init__(self, contact, type_=DOCX, message_types={}, time_range=None, messages=None,index=0, parent=None): super().__init__(parent) self.message_types = message_types # 导出的消息类型 self.contact: Contact = contact # 联系人 self.output_type = type_ # 导出文件类型 self.total_num = 1 # 总的消息数量 self.num = 0 # 当前处理的消息数量 + self.index = index # self.last_timestamp = 0 self.time_range = time_range + self.messages = messages origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}" makedirs(origin_docx_path) + def run(self): self.export() + def export(self): raise NotImplementedError("export method must be implemented in subclasses") @@ -166,4 +170,4 @@ class ExporterBase(QThread): return def share_card(self, doc, message): - return \ No newline at end of file + return diff --git a/app/DataBase/output_pc.py b/app/DataBase/output_pc.py index d7dcfed..6905b45 100644 --- a/app/DataBase/output_pc.py +++ b/app/DataBase/output_pc.py @@ -3,7 +3,7 @@ import os import traceback from typing import List -from PyQt5.QtCore import pyqtSignal, QThread +from PyQt5.QtCore import pyqtSignal, QThread, QObject from PyQt5.QtWidgets import QFileDialog from app.DataBase.exporter_csv import CSVExporter @@ -20,7 +20,7 @@ from ..util.image import get_image os.makedirs('./data/聊天记录', exist_ok=True) -class Output(QThread): +class Output(QObject): """ 发送信息线程 """ @@ -222,7 +222,7 @@ class Output(QThread): Child.okSignal.connect(self.okSignal if not is_batch else self.batch_finish_one) Child.start() - def run(self): + def start(self): if self.output_type == self.DOCX: self.to_docx(self.contact, self.message_types) elif self.output_type == self.CSV_ALL: From 63460e0caadcb9d6c15b112bf17c315b77aa0d86 Mon Sep 17 00:00:00 2001 From: STDquantum <405720329@qq.com> Date: Fri, 19 Jan 2024 18:40:16 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/DataBase/exporter_html.py | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/app/DataBase/exporter_html.py b/app/DataBase/exporter_html.py index 2d799f7..c6c40c9 100644 --- a/app/DataBase/exporter_html.py +++ b/app/DataBase/exporter_html.py @@ -298,36 +298,25 @@ class HtmlExporter(ExporterBase): print(f"【开始导出 HTML {self.contact.remark}】") messages = msg_db.get_messages(self.contact.wxid, time_range=self.time_range) filename = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}/{self.contact.remark}.html" - file_path = "./app/resources/data/template.html" + file_path = './app/resources/data/template.html' if not os.path.exists(file_path): - resource_dir = getattr( - sys, "_MEIPASS", os.path.abspath(os.path.dirname(__file__)) - ) - file_path = os.path.join( - resource_dir, "app", "resources", "data", "template.html" - ) + resource_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__))) + file_path = os.path.join(resource_dir, 'app', 'resources', 'data', 'template.html') with open(file_path, "r", encoding="utf-8") as f: content = f.read() - html_head, html_end = content.split("/*注意看这是分割线*/") - f = open(filename, "w", encoding="utf-8") - html_head = html_head.replace( - "出错了", f"{self.contact.remark}" - ) - html_head = html_head.replace( - '

出错了

', f'

{self.contact.remark}

' - ) + html_head, html_end = content.split('/*注意看这是分割线*/') + f = open(filename, 'w', encoding='utf-8') + html_head = html_head.replace("出错了", f"{self.contact.remark}") + html_head = html_head.replace("

出错了

", f"

{self.contact.remark}

") f.write(html_head) self.rangeSignal.emit(len(messages)) for index, message in enumerate(messages): type_ = message[2] sub_type = message[3] timestamp = message[5] - if ( - (type_ == 3 and self.message_types.get(3)) - or (type_ == 34 and self.message_types.get(34)) - or (type_ == 47 and self.message_types.get(47)) - ): + if (type_ == 3 and self.message_types.get(3)) or (type_ == 34 and self.message_types.get(34)) or ( + type_ == 47 and self.message_types.get(47)): pass else: self.progressSignal.emit(1)