增加聊天记录导出为html
This commit is contained in:
parent
447dec78c9
commit
21fe2bbcbd
31
README.md
31
README.md
@ -16,6 +16,7 @@
|
|||||||
<details>
|
<details>
|
||||||
<summary><strong>更新日志(点击展开):</strong></summary>
|
<summary><strong>更新日志(点击展开):</strong></summary>
|
||||||
|
|
||||||
|
* 2023.11.16 增加聊天记录导出为html
|
||||||
* 2023.11.15 添加test文件,添加自动构建可执行文件的脚本,添加版本描述
|
* 2023.11.15 添加test文件,添加自动构建可执行文件的脚本,添加版本描述
|
||||||
* 2023.11.15 [v2.2.5变化较大]重构解密脚本的返回值,重构命令行参数
|
* 2023.11.15 [v2.2.5变化较大]重构解密脚本的返回值,重构命令行参数
|
||||||
* 2023.11.15 修复无法获取wxid的bug
|
* 2023.11.15 修复无法获取wxid的bug
|
||||||
@ -48,7 +49,7 @@
|
|||||||
|
|
||||||
## 1. 项目简介
|
## 1. 项目简介
|
||||||
|
|
||||||
PyWxDump可用于:获取用户个人信息(昵称/账号/手机/邮箱/数据库密钥(用来解密聊天记录));数据库读取、解密脚本;聊天记录查看工具。
|
PyWxDump可用于:获取用户个人信息(昵称/账号/手机/邮箱/数据库密钥(用来解密聊天记录));数据库读取、解密脚本;聊天记录查看导出工具。
|
||||||
|
|
||||||
支持多账户信息获取,支持所有微信版本。
|
支持多账户信息获取,支持所有微信版本。
|
||||||
|
|
||||||
@ -66,6 +67,7 @@ PyWxDump可用于:获取用户个人信息(昵称/账号/手机/邮箱/数据
|
|||||||
* (6)提供数据库部分字段说明
|
* (6)提供数据库部分字段说明
|
||||||
* (7)支持微信多开场景,获取多用户信息等
|
* (7)支持微信多开场景,获取多用户信息等
|
||||||
* (8)微信需要登录状态才能获取数据库密钥
|
* (8)微信需要登录状态才能获取数据库密钥
|
||||||
|
* (9)支持导出聊天记录为html
|
||||||
|
|
||||||
**版本差异**
|
**版本差异**
|
||||||
|
|
||||||
@ -119,6 +121,7 @@ PyWxDump
|
|||||||
[PyWxDump](https://github.com/xaoyaoo/PyWxDump)是[SharpWxDump](https://github.com/AdminTest0/SharpWxDump)
|
[PyWxDump](https://github.com/xaoyaoo/PyWxDump)是[SharpWxDump](https://github.com/AdminTest0/SharpWxDump)
|
||||||
的经过重构python语言版本,同时添加了一些新的功能。
|
的经过重构python语言版本,同时添加了一些新的功能。
|
||||||
|
|
||||||
|
* 目前只在windows下测试过,linux下可能会存在问题。
|
||||||
* 如发现[version_list.json](pywxdump/version_list.json)缺失或错误,
|
* 如发现[version_list.json](pywxdump/version_list.json)缺失或错误,
|
||||||
请提交[issues](https://github.com/xaoyaoo/PyWxDump/issues).
|
请提交[issues](https://github.com/xaoyaoo/PyWxDump/issues).
|
||||||
* 如发现bug或有改进意见, 请提交[issues](https://github.com/xaoyaoo/PyWxDump/issues).
|
* 如发现bug或有改进意见, 请提交[issues](https://github.com/xaoyaoo/PyWxDump/issues).
|
||||||
@ -148,6 +151,9 @@ pip install pywxdump
|
|||||||
|
|
||||||
### 1.2 从源码安装
|
### 1.2 从源码安装
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>点击展开</summary>
|
||||||
|
|
||||||
```shell script
|
```shell script
|
||||||
pip install git+git://github.com/xaoyaoo/PyWxDump.git
|
pip install git+git://github.com/xaoyaoo/PyWxDump.git
|
||||||
```
|
```
|
||||||
@ -159,6 +165,7 @@ git clone https://github.com/xaoyaoo/PyWxDump.git
|
|||||||
cd PyWxDump
|
cd PyWxDump
|
||||||
python -m pip install -U .
|
python -m pip install -U .
|
||||||
```
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
## 2. 使用
|
## 2. 使用
|
||||||
|
|
||||||
@ -174,11 +181,15 @@ wxdump 模式 [参数]
|
|||||||
# db_path 获取微信文件夹路径
|
# db_path 获取微信文件夹路径
|
||||||
# decrypt 解密微信数据库
|
# decrypt 解密微信数据库
|
||||||
# dbshow 聊天记录查看[需要安装flask]
|
# dbshow 聊天记录查看[需要安装flask]
|
||||||
|
# export 聊天记录导出为html[需要安装flask]
|
||||||
# all 获取微信信息,解密微信数据库,查看聊天记录
|
# all 获取微信信息,解密微信数据库,查看聊天记录
|
||||||
```
|
```
|
||||||
|
|
||||||
*示例*
|
*示例*
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>点击展开示例</summary>
|
||||||
|
|
||||||
以下是示例命令:
|
以下是示例命令:
|
||||||
|
|
||||||
```shell script
|
```shell script
|
||||||
@ -228,12 +239,28 @@ wxdump dbshow -h
|
|||||||
# -fs , --filestorage_path
|
# -fs , --filestorage_path
|
||||||
# (可选)文件夹FileStorage的路径(用于显示图片)
|
# (可选)文件夹FileStorage的路径(用于显示图片)
|
||||||
|
|
||||||
|
wxdump export -h
|
||||||
|
#usage: wxdump export [-h] -u -o -msg -micro -media [-fs]
|
||||||
|
#options:
|
||||||
|
# -h, --help show this help message and exit
|
||||||
|
# -u , --username 微信账号
|
||||||
|
# -o , --outpath 导出路径
|
||||||
|
# -msg , --msg_path 解密后的 MSG.db 的路径
|
||||||
|
# -micro , --micro_path
|
||||||
|
# 解密后的 MicroMsg.db 的路径
|
||||||
|
# -media , --media_path
|
||||||
|
# 解密后的 MediaMSG.db 的路径
|
||||||
|
# -fs , --filestorage_path
|
||||||
|
# (可选)文件夹FileStorage的路径(用于显示图片)
|
||||||
|
|
||||||
wxdump all -h
|
wxdump all -h
|
||||||
#usage: main.py all [-h]
|
#usage: main.py all [-h]
|
||||||
#options:
|
#options:
|
||||||
# -h, --help show this help message and exit
|
# -h, --help show this help message and exit
|
||||||
```
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
### 2.2 python API
|
### 2.2 python API
|
||||||
|
|
||||||
更多使用方法参考[tests](./tests)文件夹下的[test_*.py](./tests/)文件
|
更多使用方法参考[tests](./tests)文件夹下的[test_*.py](./tests/)文件
|
||||||
@ -302,6 +329,8 @@ else:
|
|||||||
|
|
||||||
# 三、免责声明(非常重要!!!!!!!)
|
# 三、免责声明(非常重要!!!!!!!)
|
||||||
|
|
||||||
|
本项目仅供学习交流使用,请勿用于非法用途,否则后果自负。
|
||||||
|
|
||||||
本项目仅允许在授权情况下对数据库进行备份,严禁用于非法目的,否则自行承担所有相关责任。使用该工具则代表默认同意该条款;
|
本项目仅允许在授权情况下对数据库进行备份,严禁用于非法目的,否则自行承担所有相关责任。使用该工具则代表默认同意该条款;
|
||||||
|
|
||||||
请勿利用本项目的相关技术从事非法测试,如因此产生的一切不良后果与项目作者无关。
|
请勿利用本项目的相关技术从事非法测试,如因此产生的一切不良后果与项目作者无关。
|
||||||
|
@ -169,6 +169,49 @@ class MainShowChatRecords():
|
|||||||
app.run(debug=False)
|
app.run(debug=False)
|
||||||
|
|
||||||
|
|
||||||
|
class MainExportChatRecords():
|
||||||
|
def init_parses(self, parser):
|
||||||
|
self.mode = "export"
|
||||||
|
# 添加 'decrypt' 子命令解析器
|
||||||
|
sb_decrypt = parser.add_parser(self.mode, help="聊天记录导出为html")
|
||||||
|
sb_decrypt.add_argument("-u", "--username", type=str, help="微信账号", required=True, metavar="")
|
||||||
|
sb_decrypt.add_argument("-o", "--outpath", type=str, help="导出路径", required=True, metavar="")
|
||||||
|
sb_decrypt.add_argument("-msg", "--msg_path", type=str, help="解密后的 MSG.db 的路径", required=True,
|
||||||
|
metavar="")
|
||||||
|
sb_decrypt.add_argument("-micro", "--micro_path", type=str, help="解密后的 MicroMsg.db 的路径", required=True,
|
||||||
|
metavar="")
|
||||||
|
sb_decrypt.add_argument("-media", "--media_path", type=str, help="解密后的 MediaMSG.db 的路径", required=True,
|
||||||
|
metavar="")
|
||||||
|
sb_decrypt.add_argument("-fs", "--filestorage_path", type=str,
|
||||||
|
help="(可选)文件夹FileStorage的路径(用于显示图片)", required=False,
|
||||||
|
metavar="")
|
||||||
|
return sb_decrypt
|
||||||
|
|
||||||
|
def run(self, args):
|
||||||
|
# 从命令行参数获取值
|
||||||
|
try:
|
||||||
|
from flask import Flask, request, jsonify, render_template, g
|
||||||
|
import logging
|
||||||
|
from .show_chat.main_window import app_show_chat, get_user_list, export
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
print("[-] 请安装flask( pip install flask )")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not os.path.exists(args.msg_path) or not os.path.exists(args.micro_path) or not os.path.exists(
|
||||||
|
args.media_path):
|
||||||
|
print(os.path.exists(args.msg_path), os.path.exists(args.micro_path), os.path.exists(args.media_path))
|
||||||
|
print("[-] 输入数据库路径不存在")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not os.path.exists(args.outpath):
|
||||||
|
os.makedirs(args.outpath)
|
||||||
|
print(f"[+] 创建输出文件夹:{args.outpath}")
|
||||||
|
|
||||||
|
export(args.username, args.outpath, args.msg_path, args.micro_path, args.media_path, args.filestorage_path)
|
||||||
|
print(f"[+] 导出成功{args.outpath}")
|
||||||
|
|
||||||
|
|
||||||
class MainAll():
|
class MainAll():
|
||||||
def init_parses(self, parser):
|
def init_parses(self, parser):
|
||||||
self.mode = "all"
|
self.mode = "all"
|
||||||
@ -294,6 +337,11 @@ def console_run():
|
|||||||
sb_dbshow = main_show_chat_records.init_parses(subparsers)
|
sb_dbshow = main_show_chat_records.init_parses(subparsers)
|
||||||
modes[main_show_chat_records.mode] = main_show_chat_records
|
modes[main_show_chat_records.mode] = main_show_chat_records
|
||||||
|
|
||||||
|
# 添加 'export' 子命令解析器
|
||||||
|
main_export_chat_records = MainExportChatRecords()
|
||||||
|
sb_export = main_export_chat_records.init_parses(subparsers)
|
||||||
|
modes[main_export_chat_records.mode] = main_export_chat_records
|
||||||
|
|
||||||
# 添加 'all' 子命令解析器
|
# 添加 'all' 子命令解析器
|
||||||
main_all = MainAll()
|
main_all = MainAll()
|
||||||
sb_all = main_all.init_parses(subparsers)
|
sb_all = main_all.init_parses(subparsers)
|
||||||
|
@ -13,6 +13,8 @@ import time
|
|||||||
import hashlib
|
import hashlib
|
||||||
from pywxdump.analyse import read_img_dat, decompress_CompressContent, read_audio, parse_xml_string
|
from pywxdump.analyse import read_img_dat, decompress_CompressContent, read_audio, parse_xml_string
|
||||||
|
|
||||||
|
from flask import Flask, request, render_template, g, Blueprint
|
||||||
|
|
||||||
|
|
||||||
def get_md5(s):
|
def get_md5(s):
|
||||||
m = hashlib.md5()
|
m = hashlib.md5()
|
||||||
@ -177,18 +179,49 @@ def load_chat_records(selected_talker, start_index, page_size, user_list, MSG_AL
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
from flask import Flask, request, render_template, g, Blueprint
|
def export_html(user, outpath, MSG_ALL_db_path, MediaMSG_all_db_path, FileStorage_path, page_size=500):
|
||||||
|
name_save = user.get("remark", user.get("nickname", user.get("username", "")))
|
||||||
|
username = user.get("username", "")
|
||||||
|
|
||||||
|
chatCount = user.get("chat_count", 0)
|
||||||
|
|
||||||
|
for i in range(0, chatCount, page_size):
|
||||||
|
start_index = i
|
||||||
|
data = load_chat_records(username, start_index, page_size, user, MSG_ALL_db_path, MediaMSG_all_db_path,
|
||||||
|
FileStorage_path)
|
||||||
|
if len(data) == 0:
|
||||||
|
break
|
||||||
|
with open(f"{outpath}/{name_save}_{int(i / page_size)}.html", "w", encoding="utf-8") as f:
|
||||||
|
f.write(render_template("chat.html", msgs=data))
|
||||||
|
return True, f"导出成功{outpath}"
|
||||||
|
|
||||||
|
|
||||||
|
def export(username, outpath, MSG_ALL_db_path, MicroMsg_db_path, MediaMSG_all_db_path, FileStorage_path):
|
||||||
|
if not os.path.exists(outpath):
|
||||||
|
outpath = os.path.join(os.getcwd(), "export" + os.sep + username)
|
||||||
|
if not os.path.exists(outpath):
|
||||||
|
os.makedirs(outpath)
|
||||||
|
|
||||||
|
USER_LIST = get_user_list(MSG_ALL_db_path, MicroMsg_db_path)
|
||||||
|
user = list(filter(lambda x: x["username"] == username, USER_LIST))
|
||||||
|
|
||||||
|
if username and len(user) > 0:
|
||||||
|
user = user[0]
|
||||||
|
return export_html(user, outpath, MSG_ALL_db_path, MediaMSG_all_db_path, FileStorage_path)
|
||||||
|
|
||||||
|
|
||||||
app_show_chat = Blueprint('show_chat_main', __name__, template_folder='templates')
|
app_show_chat = Blueprint('show_chat_main', __name__, template_folder='templates')
|
||||||
app_show_chat.debug = False
|
app_show_chat.debug = False
|
||||||
|
|
||||||
|
|
||||||
|
# 主页 - 显示用户列表
|
||||||
@app_show_chat.route('/')
|
@app_show_chat.route('/')
|
||||||
def index():
|
def index():
|
||||||
g.USER_LIST = get_user_list(g.MSG_ALL_db_path, g.MicroMsg_db_path)
|
g.USER_LIST = get_user_list(g.MSG_ALL_db_path, g.MicroMsg_db_path)
|
||||||
return render_template("index.html", users=g.USER_LIST)
|
return render_template("index.html", users=g.USER_LIST)
|
||||||
|
|
||||||
|
|
||||||
|
# 获取聊天记录
|
||||||
@app_show_chat.route('/get_chat_data', methods=["GET", 'POST'])
|
@app_show_chat.route('/get_chat_data', methods=["GET", 'POST'])
|
||||||
def get_chat_data():
|
def get_chat_data():
|
||||||
username = request.args.get("username", "")
|
username = request.args.get("username", "")
|
||||||
@ -208,3 +241,26 @@ def get_chat_data():
|
|||||||
return render_template("chat.html", msgs=data)
|
return render_template("chat.html", msgs=data)
|
||||||
else:
|
else:
|
||||||
return "error"
|
return "error"
|
||||||
|
|
||||||
|
|
||||||
|
# 聊天记录导出为html
|
||||||
|
@app_show_chat.route('/export_chat_data', methods=["GET", 'POST'])
|
||||||
|
def get_export():
|
||||||
|
username = request.args.get("username", "")
|
||||||
|
|
||||||
|
user = list(filter(lambda x: x["username"] == username, g.USER_LIST))
|
||||||
|
|
||||||
|
if username and len(user) > 0:
|
||||||
|
user = user[0]
|
||||||
|
n = f"{user.get('username', '')}_{user.get('nickname', '')}_{user.get('remark', '')}"
|
||||||
|
outpath = os.path.join(os.getcwd(), "export" + os.sep + n)
|
||||||
|
if not os.path.exists(outpath):
|
||||||
|
os.makedirs(outpath)
|
||||||
|
|
||||||
|
ret = export_html(user, outpath, g.MSG_ALL_db_path, g.MediaMSG_all_db_path, g.FileStorage_path, page_size=200)
|
||||||
|
if ret[0]:
|
||||||
|
return ret[1]
|
||||||
|
else:
|
||||||
|
return ret[1]
|
||||||
|
else:
|
||||||
|
return "error"
|
||||||
|
@ -4,6 +4,11 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>chat</title>
|
<title>chat</title>
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
|
||||||
|
<style>
|
||||||
|
img {
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -79,7 +79,7 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-3" style="display: flex; justify-content: flex-end;">
|
<div class="col-3" style="display: flex; justify-content: flex-end;">
|
||||||
<button type="button" class="btn btn-primary">导出</button>
|
<button id="btn_export" type="button" class="btn btn-primary">导出</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -184,6 +184,13 @@
|
|||||||
request_function(requestUrl);
|
request_function(requestUrl);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 导出按钮点击事件
|
||||||
|
document.getElementById('btn_export').addEventListener('click', function () {
|
||||||
|
var requestUrl = '/export_chat_data?username=' + encodeURIComponent(globalUsername);
|
||||||
|
window.open(requestUrl);
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
27
setup.py
27
setup.py
@ -3,7 +3,20 @@ from setuptools import setup, find_packages
|
|||||||
with open("README.md", "r", encoding="utf-8") as fh:
|
with open("README.md", "r", encoding="utf-8") as fh:
|
||||||
long_description = fh.read()
|
long_description = fh.read()
|
||||||
|
|
||||||
version = "2.2.6"
|
version = "2.2.7"
|
||||||
|
|
||||||
|
install_requires = [
|
||||||
|
"psutil",
|
||||||
|
"pycryptodomex",
|
||||||
|
"pywin32",
|
||||||
|
"pymem",
|
||||||
|
"silk-python",
|
||||||
|
"pyaudio",
|
||||||
|
"requests",
|
||||||
|
"pillow",
|
||||||
|
"pyahocorasick"
|
||||||
|
]
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="pywxdump",
|
name="pywxdump",
|
||||||
author="xaoyaoo",
|
author="xaoyaoo",
|
||||||
@ -33,17 +46,7 @@ setup(
|
|||||||
"Operating System :: OS Independent",
|
"Operating System :: OS Independent",
|
||||||
],
|
],
|
||||||
python_requires='>=3.6, <4',
|
python_requires='>=3.6, <4',
|
||||||
install_requires=[
|
install_requires=install_requires,
|
||||||
"psutil",
|
|
||||||
"pycryptodomex",
|
|
||||||
"pywin32",
|
|
||||||
"pymem",
|
|
||||||
"silk-python",
|
|
||||||
"pyaudio",
|
|
||||||
"requests",
|
|
||||||
"pillow",
|
|
||||||
"pyahocorasick"
|
|
||||||
],
|
|
||||||
entry_points={
|
entry_points={
|
||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
'wxdump = pywxdump.command:console_run',
|
'wxdump = pywxdump.command:console_run',
|
||||||
|
Loading…
Reference in New Issue
Block a user