From e30d90175244f988a6b757b0764545019548fea6 Mon Sep 17 00:00:00 2001 From: xaoyaoo Date: Sat, 29 Jun 2024 11:58:14 +0800 Subject: [PATCH 01/12] add wx 3.9.11.19 --- pywxdump/version_list.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pywxdump/version_list.json b/pywxdump/version_list.json index ce40e0c..aa6414b 100644 --- a/pywxdump/version_list.json +++ b/pywxdump/version_list.json @@ -390,5 +390,12 @@ 93550168, 0, 93551632 + ], + "3.9.11.19": [ + 93550296, + 93551632, + 93550104, + 0, + 93551568 ] } \ No newline at end of file From c80d2dae14fcacd1a24254becd20245fef051922 Mon Sep 17 00:00:00 2001 From: xaoyaoo Date: Tue, 2 Jul 2024 18:58:45 +0800 Subject: [PATCH 02/12] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index a2b4f27..6d8c2e3 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,6 @@ [![PyPI-Downloads](https://img.shields.io/pypi/dm/pywxdump)](https://pypistats.org/packages/pywxdump) [![GitHub license](https://img.shields.io/pypi/l/pywxdump)](https://github.com/xaoyaoo/PyWxDump/blob/master/LICENSE) -V3.0.* 有很多bug,如果你有问题,请使用V2.4.71版本,或者等待后续版本(发现bug请提交issue) - * Welcome to provide more ideas or code to improve this project together. ### If you are a novice, please pay attention to the Official Accounts: `逍遥之芯` (the QR code is below), and reply: `PyWxDump` to get a picture text tutorial. From fda41bd203f1453e9c218e12fac221de9f5c565c Mon Sep 17 00:00:00 2001 From: xaoyaoo Date: Tue, 2 Jul 2024 18:59:04 +0800 Subject: [PATCH 03/12] UPDATE CHANGELOG.md --- doc/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index a6b515e..3dc9aa4 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -1,5 +1,7 @@ ## v3.0.36.(待发布) +- Update README.md +- add wx 3.9.11.19 - UPDATE CHANGELOG.md ## v3.0.35 From 36f253d45fba78c70c9e63d5afec758d0a4dd920 Mon Sep 17 00:00:00 2001 From: xaoyaoo Date: Tue, 2 Jul 2024 19:02:55 +0800 Subject: [PATCH 04/12] fix --- pywxdump/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pywxdump/__init__.py b/pywxdump/__init__.py index 2c29657..ef906d0 100644 --- a/pywxdump/__init__.py +++ b/pywxdump/__init__.py @@ -28,4 +28,4 @@ except: # PYWXDUMP_ROOT_PATH = os.path.dirname(__file__) # db_init = DBPool("DBPOOL_INIT") -__version__ = "3.0.35" +__version__ = "3.0.36" From 2cdae045186c043a2c87d3513a28afafee291d9b Mon Sep 17 00:00:00 2001 From: xaoyaoo Date: Tue, 2 Jul 2024 19:20:23 +0800 Subject: [PATCH 05/12] release_new_version --- tests/release_new_version.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 tests/release_new_version.py diff --git a/tests/release_new_version.py b/tests/release_new_version.py new file mode 100644 index 0000000..fab2e11 --- /dev/null +++ b/tests/release_new_version.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*-# +# ------------------------------------------------------------------------------- +# Name: release_new_version.py +# Description: +# Author: xaoyaoo +# Date: 2024/07/02 +# ------------------------------------------------------------------------------- +import os +import sys +import time + +# 获取当前文件所在目录 +current_path = os.path.dirname(os.path.abspath(__file__)) +PyWxDump_path = os.path.dirname((current_path)) +os.chdir(PyWxDump_path) +version_path = os.path.join(PyWxDump_path, "pywxdump", "__init__.py") + +# 读取版本号 pywxdump/__init__.py 中的 __version__ +with open(version_path, "r", encoding="utf-8") as f: + for line in f.readlines(): + if line.startswith("__version__"): + version = line.split("=")[-1].strip().strip("\"'") + break + else: + raise RuntimeError("version not found") + +# print("PyWxDump_path", PyWxDump_path) +# print("version", version) +print(f"git tag -a v{version} -m 'v{version} release'") +os.system(f"git tag -a v{version} -m 'v{version}'") +time.sleep(1) +os.system(f"git push origin v{version}") From 35e93e557fee700df9760999e9b090b503a3f6bd Mon Sep 17 00:00:00 2001 From: xaoyaoo Date: Tue, 2 Jul 2024 19:42:11 +0800 Subject: [PATCH 06/12] UPDATE CHANGELOG.md --- doc/CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index 3dc9aa4..58628df 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -1,5 +1,10 @@ -## v3.0.36.(待发布) +## v3.0.37.(待发布) +- release_new_version + +## v3.0.36 + +- fix - Update README.md - add wx 3.9.11.19 - UPDATE CHANGELOG.md From ed192990bd4ef5f547eb5df56939f78539521903 Mon Sep 17 00:00:00 2001 From: xaoyaoo Date: Tue, 2 Jul 2024 19:42:25 +0800 Subject: [PATCH 07/12] UPDATE CHANGELOG.md --- doc/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index 58628df..5cc8795 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -1,5 +1,6 @@ ## v3.0.37.(待发布) +- UPDATE CHANGELOG.md - release_new_version ## v3.0.36 From 538da538a3581a2fa9a12afe3efabeb509e9ba84 Mon Sep 17 00:00:00 2001 From: xaoyaoo Date: Wed, 3 Jul 2024 13:04:55 +0800 Subject: [PATCH 08/12] =?UTF-8?q?add=20(=E5=88=86=E4=BA=AB)=E5=8D=A1?= =?UTF-8?q?=E7=89=87=E5=BC=8F=E9=93=BE=E6=8E=A5=20=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pywxdump/dbpreprocess/parsingMSG.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/pywxdump/dbpreprocess/parsingMSG.py b/pywxdump/dbpreprocess/parsingMSG.py index 0ccf456..c3fc02d 100644 --- a/pywxdump/dbpreprocess/parsingMSG.py +++ b/pywxdump/dbpreprocess/parsingMSG.py @@ -341,7 +341,8 @@ class ParsingMSG(DatabaseBase): """ 获取单条消息详情,格式化输出 """ - localId, IsSender, StrContent, StrTalker, Sequence, Type, SubType, CreateTime, MsgSvrID, DisplayContent, CompressContent, BytesExtra, id = row + (localId, IsSender, StrContent, StrTalker, Sequence, Type, SubType, CreateTime, MsgSvrID, + DisplayContent, CompressContent, BytesExtra, id) = row CreateTime = timestamp2str(CreateTime) type_id = (Type, SubType) @@ -416,6 +417,16 @@ class ParsingMSG(DatabaseBase): file_name = os.path.basename(url) content["msg"] = file_name + elif type_id == (49, 5): # (分享)卡片式链接 + CompressContent = self.decompress_CompressContent(CompressContent) + CompressContent_tmp = xml2dict(CompressContent) + appmsg = CompressContent_tmp.get("appmsg", {}) + title = appmsg.get("title", "") + des = appmsg.get("des", "") + url = appmsg.get("url", "") + content["msg"] = f"{title}\n{des}\n\n{url}" + content["src"] = url + elif type_id == (49, 19): # 合并转发的聊天记录 CompressContent = self.decompress_CompressContent(CompressContent) content_tmp = xml2dict(CompressContent) From ae38e4271252d3cf6d6eeceac5c33ce04362e594 Mon Sep 17 00:00:00 2001 From: xaoyaoo Date: Wed, 3 Jul 2024 13:21:05 +0800 Subject: [PATCH 09/12] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=85=AC=E4=BC=97?= =?UTF-8?q?=E5=8F=B7=E6=B6=88=E6=81=AF=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pywxdump/__init__.py | 2 +- pywxdump/api/api.py | 7 +- pywxdump/dbpreprocess/__init__.py | 1 + pywxdump/dbpreprocess/parsingPublicMsg.py | 94 +++++++++++++++++++++++ pywxdump/wx_info/get_wx_info.py | 2 +- pywxdump/wx_info/merge_db.py | 7 +- 6 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 pywxdump/dbpreprocess/parsingPublicMsg.py diff --git a/pywxdump/__init__.py b/pywxdump/__init__.py index ef906d0..d4ad05e 100644 --- a/pywxdump/__init__.py +++ b/pywxdump/__init__.py @@ -13,7 +13,7 @@ from .wx_info import merge_copy_db, merge_msg_db, merge_media_msg_db, merge_db, all_merge_real_time_db from .analyzer import DBPool from .dbpreprocess import get_user_list, get_recent_user_list, wxid2userinfo, ParsingMSG, ParsingMicroMsg, \ - ParsingMediaMSG, ParsingOpenIMContact, ParsingFavorite + ParsingMediaMSG, ParsingOpenIMContact, ParsingFavorite,ParsingPublicMsg from .server import start_falsk import os, json diff --git a/pywxdump/api/api.py b/pywxdump/api/api.py index df70730..b6dec7c 100644 --- a/pywxdump/api/api.py +++ b/pywxdump/api/api.py @@ -23,7 +23,7 @@ from pywxdump.api.utils import read_session, get_session_wxids, save_session, er from pywxdump import read_info, VERSION_LIST, batch_decrypt, BiasAddr, merge_db, decrypt_merge, merge_real_time_db from pywxdump.dbpreprocess import wxid2userinfo, ParsingMSG, get_user_list, get_recent_user_list, ParsingMediaMSG, \ - download_file, export_csv, export_json, ParsingMicroMsg + download_file, export_csv, export_json, ParsingMicroMsg, ParsingPublicMsg from pywxdump.dbpreprocess.utils import dat2img # app = Flask(__name__, static_folder='../ui/web/dist', static_url_path='/') @@ -311,6 +311,8 @@ def msg_count(): if not my_wxid: return ReJson(1001, body="my_wxid is required") merge_path = read_session(g.sf, my_wxid, "merge_path") chat_count = ParsingMSG(merge_path).msg_count(wxid) + if None in chat_count: + chat_count = ParsingPublicMsg(merge_path).msg_count(wxid) return ReJson(0, chat_count) @@ -407,6 +409,9 @@ def get_msgs(): parsing_msg = ParsingMSG(merge_path) msgs, wxid_list = parsing_msg.msg_list(wxid, start, limit) + if not msgs: + parsing_public_msg = ParsingPublicMsg(merge_path) + msgs, wxid_list = parsing_public_msg.msg_list(wxid, start, limit) wxid_list.append(my_wxid) user_list = wxid2userinfo(merge_path, merge_path, wxid_list) return ReJson(0, {"msg_list": msgs, "user_list": user_list}) diff --git a/pywxdump/dbpreprocess/__init__.py b/pywxdump/dbpreprocess/__init__.py index eb59741..2da6753 100644 --- a/pywxdump/dbpreprocess/__init__.py +++ b/pywxdump/dbpreprocess/__init__.py @@ -12,6 +12,7 @@ from .parsingMSG import ParsingMSG from .parsingMicroMsg import ParsingMicroMsg from .parsingMediaMSG import ParsingMediaMSG from .parsingOpenIMContact import ParsingOpenIMContact +from .parsingPublicMsg import ParsingPublicMsg from .utils import download_file from .export.exportCSV import export_csv diff --git a/pywxdump/dbpreprocess/parsingPublicMsg.py b/pywxdump/dbpreprocess/parsingPublicMsg.py new file mode 100644 index 0000000..5c8d915 --- /dev/null +++ b/pywxdump/dbpreprocess/parsingPublicMsg.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*-# +# ------------------------------------------------------------------------------- +# Name: parsingPublicMsg.py +# Description: +# Author: xaoyaoo +# Date: 2024/07/03 +# ------------------------------------------------------------------------------- + +# -*- coding: utf-8 -*-# +# ------------------------------------------------------------------------------- +# Name: parsingMSG.py +# Description: +# Author: xaoyaoo +# Date: 2024/04/15 +# ------------------------------------------------------------------------------- +import json +import os +import re +from typing import Union, Tuple + +import pandas as pd + +from .dbbase import DatabaseBase +from .parsingMSG import ParsingMSG +from .utils import get_md5, name2typeid, typeid2name, type_converter, timestamp2str, xml2dict, match_BytesExtra +import lz4.block +import blackboxprotobuf + + +class ParsingPublicMsg(ParsingMSG): + _class_name = "PublicMSG" + + def msg_count(self, wxid: str = ""): + """ + 获取聊天记录数量,根据wxid获取单个联系人的聊天记录数量,不传wxid则获取所有联系人的聊天记录数量 + :param MSG_db_path: MSG.db 文件路径 + :return: 聊天记录数量列表 {wxid: chat_count} + """ + if wxid: + sql = f"SELECT StrTalker, COUNT(*) FROM PublicMsg WHERE StrTalker='{wxid}';" + else: + sql = f"SELECT StrTalker, COUNT(*) FROM PublicMsg GROUP BY StrTalker ORDER BY COUNT(*) DESC;" + + result = self.execute_sql(sql) + if not result: + return {} + df = pd.DataFrame(result, columns=["wxid", "msg_count"]) + # # 排序 + df = df.sort_values(by="msg_count", ascending=False) + # chat_counts : {wxid: chat_count} + chat_counts = df.set_index("wxid").to_dict()["msg_count"] + return chat_counts + + def msg_count_total(self): + """ + 获取聊天记录总数 + :return: 聊天记录总数 + """ + sql = "SELECT COUNT(*) FROM PublicMsg;" + result = self.execute_sql(sql) + if result and len(result) > 0: + chat_counts = result[0][0] + return chat_counts + return 0 + + + def msg_list(self, wxid="", start_index=0, page_size=500, msg_type: str = ""): + sql = ( + "SELECT localId, IsSender, StrContent, StrTalker, Sequence, Type, SubType, CreateTime, MsgSvrID, " + "DisplayContent, CompressContent, BytesExtra, ROW_NUMBER() OVER (ORDER BY CreateTime ASC) AS id " + "FROM PublicMsg WHERE 1==1 " + "ORDER BY CreateTime ASC LIMIT ?, ?" + ) + params = [start_index, page_size] + if msg_type: + sql = sql.replace("ORDER BY CreateTime ASC LIMIT ?, ?", + f"AND Type=? ORDER BY CreateTime ASC LIMIT ?,?") + params = [msg_type] + params + + if wxid: + sql = sql.replace("WHERE 1==1", f"WHERE StrTalker=? ") + params = [wxid] + params + params = tuple(params) + result1 = self.execute_sql(sql, params) + if not result1: + return [], [] + data = [] + wxid_list = [] + for row in result1: + tmpdata = self.msg_detail(row) + wxid_list.append(tmpdata["talker"]) + data.append(tmpdata) + wxid_list = list(set(wxid_list)) + return data, wxid_list diff --git a/pywxdump/wx_info/get_wx_info.py b/pywxdump/wx_info/get_wx_info.py index 9095497..7365316 100644 --- a/pywxdump/wx_info/get_wx_info.py +++ b/pywxdump/wx_info/get_wx_info.py @@ -398,7 +398,7 @@ def get_core_db(wx_path: str, db_type: list = None) -> [str]: """ if not os.path.exists(wx_path): return False, f"[-] 目录不存在: {wx_path}" - db_type_all = ["MSG", "MediaMSG", "MicroMsg", "OpenIMContact", "OpenIMMedia", "OpenIMMsg", "Favorite"] + db_type_all = ["MSG", "MediaMSG", "MicroMsg", "OpenIMContact", "OpenIMMedia", "OpenIMMsg", "Favorite", "PublicMsg"] if not db_type: db_type = db_type_all diff --git a/pywxdump/wx_info/merge_db.py b/pywxdump/wx_info/merge_db.py index 2f17564..4da0f57 100644 --- a/pywxdump/wx_info/merge_db.py +++ b/pywxdump/wx_info/merge_db.py @@ -14,6 +14,7 @@ import subprocess import time from typing import List + def merge_copy_db(db_path, save_path): if isinstance(db_path, list) and len(db_path) == 1: db_path = db_path[0] @@ -299,7 +300,8 @@ def merge_db(db_paths, save_path="merge.db", CreateTime: int = 0, endCreateTime: return save_path -def decrypt_merge(wx_path, key, outpath="", CreateTime: int = 0, endCreateTime: int = 0, db_type: List[str] = []) -> (bool, str): +def decrypt_merge(wx_path, key, outpath="", CreateTime: int = 0, endCreateTime: int = 0, db_type: List[str] = []) -> ( +bool, str): """ 解密合并数据库 msg.db, microMsg.db, media.db,注意:会删除原数据库 :param wx_path: 微信路径 eg: C:\*******\WeChat Files\wxid_********* @@ -319,7 +321,8 @@ def decrypt_merge(wx_path, key, outpath="", CreateTime: int = 0, endCreateTime: # 分割wx_path的文件名和父目录 msg_dir = os.path.dirname(wx_path) my_wxid = os.path.basename(wx_path) - db_type_set: set[str] = {"MSG", "MediaMSG", "MicroMsg", "OpenIMContact", "OpenIMMedia", "OpenIMMsg", "Favorite"} + db_type_set: set[str] = {"MSG", "MediaMSG", "MicroMsg", "OpenIMContact", "OpenIMMedia", "OpenIMMsg", "Favorite", + "PublicMsg"} if len(db_type) == 0: db_type = list(db_type_set) else: From 94a05fef76eefaf1a2a3e36df32a7e1b03939127 Mon Sep 17 00:00:00 2001 From: xaoyaoo Date: Wed, 3 Jul 2024 13:21:23 +0800 Subject: [PATCH 10/12] fix and add --- pywxdump/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pywxdump/__init__.py b/pywxdump/__init__.py index d4ad05e..dbf5cf2 100644 --- a/pywxdump/__init__.py +++ b/pywxdump/__init__.py @@ -28,4 +28,4 @@ except: # PYWXDUMP_ROOT_PATH = os.path.dirname(__file__) # db_init = DBPool("DBPOOL_INIT") -__version__ = "3.0.36" +__version__ = "3.0.37" From c28b7db3ff8f1374c7489a2c1205019f6dd5a330 Mon Sep 17 00:00:00 2001 From: xaoyaoo Date: Wed, 3 Jul 2024 13:21:33 +0800 Subject: [PATCH 11/12] UPDATE CHANGELOG.md --- doc/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index 5cc8795..8ef6a97 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -1,5 +1,8 @@ ## v3.0.37.(待发布) +- 增加公众号消息显示 +- fix and add +- add (分享)卡片式链接 解析 - UPDATE CHANGELOG.md - release_new_version From 67898f279f0e90b583d47caceea430934d94d5e4 Mon Sep 17 00:00:00 2001 From: xaoyaoo Date: Thu, 4 Jul 2024 16:00:59 +0800 Subject: [PATCH 12/12] UPDATE CHANGELOG.md --- doc/CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index 8ef6a97..794b745 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -1,4 +1,8 @@ -## v3.0.37.(待发布) +## v3.0.38.(待发布) + +- + +## v3.0.37 - 增加公众号消息显示 - fix and add