该flask为fastapi,速度更快
This commit is contained in:
parent
81832a2a99
commit
0b937a2f11
@ -5,15 +5,8 @@
|
|||||||
# Author: xaoyaoo
|
# Author: xaoyaoo
|
||||||
# Date: 2023/10/14
|
# Date: 2023/10/14
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
# from .analyzer.db_parsing import read_img_dat, read_emoji, decompress_CompressContent, read_audio_buf, read_audio, \
|
__version__ = "3.1.18"
|
||||||
# parse_xml_string, read_BytesExtra
|
|
||||||
# from .ui import app_show_chat, get_user_list, export
|
|
||||||
from .wx_core import BiasAddr, get_wx_info, get_wx_db, batch_decrypt, decrypt, get_core_db
|
|
||||||
from .wx_core import merge_db, decrypt_merge, merge_real_time_db, all_merge_real_time_db
|
|
||||||
from .analyzer import DBPool
|
|
||||||
from .db import MsgHandler, MicroHandler, \
|
|
||||||
MediaHandler, OpenIMContactHandler, FavoriteHandler, PublicMsgHandler, DBHandler
|
|
||||||
from .server import start_falsk
|
|
||||||
import os, json
|
import os, json
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -24,7 +17,18 @@ except:
|
|||||||
WX_OFFS = {}
|
WX_OFFS = {}
|
||||||
WX_OFFS_PATH = None
|
WX_OFFS_PATH = None
|
||||||
|
|
||||||
|
from .wx_core import BiasAddr, get_wx_info, get_wx_db, batch_decrypt, decrypt, get_core_db
|
||||||
|
from .wx_core import merge_db, decrypt_merge, merge_real_time_db, all_merge_real_time_db
|
||||||
|
from .db import DBHandler, MsgHandler, MicroHandler, MediaHandler, OpenIMContactHandler, FavoriteHandler, \
|
||||||
|
PublicMsgHandler
|
||||||
|
from .api import start_server, app
|
||||||
|
from .analyzer import DBPool
|
||||||
|
|
||||||
# PYWXDUMP_ROOT_PATH = os.path.dirname(__file__)
|
# PYWXDUMP_ROOT_PATH = os.path.dirname(__file__)
|
||||||
# db_init = DBPool("DBPOOL_INIT")
|
# db_init = DBPool("DBPOOL_INIT")
|
||||||
|
|
||||||
__version__ = "3.1.18"
|
|
||||||
|
__all__ = ["BiasAddr", "get_wx_info", "get_wx_db", "batch_decrypt", "decrypt", "get_core_db",
|
||||||
|
"merge_db", "decrypt_merge", "merge_real_time_db", "all_merge_real_time_db",
|
||||||
|
"MsgHandler", "MicroHandler", "MediaHandler", "OpenIMContactHandler", "FavoriteHandler", "PublicMsgHandler",
|
||||||
|
"DBHandler", "start_server", "WX_OFFS", "WX_OFFS_PATH", "__version__", "app"]
|
||||||
|
@ -5,9 +5,132 @@
|
|||||||
# Author: xaoyaoo
|
# Author: xaoyaoo
|
||||||
# Date: 2023/12/14
|
# Date: 2023/12/14
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import uvicorn
|
||||||
|
|
||||||
|
from fastapi import FastAPI, Request, Path, Query
|
||||||
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
from fastapi.exceptions import RequestValidationError
|
||||||
|
from starlette.middleware.cors import CORSMiddleware
|
||||||
|
from starlette.responses import RedirectResponse
|
||||||
|
|
||||||
|
from .utils import gc, is_port_in_use, server_loger
|
||||||
|
from .rjson import ReJson
|
||||||
from .remote_server import rs_api
|
from .remote_server import rs_api
|
||||||
from .local_server import ls_api
|
from .local_server import ls_api
|
||||||
from .utils import get_conf, set_conf
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
from pywxdump import __version__
|
||||||
pass
|
|
||||||
|
app = FastAPI(title="pywxdump", description="微信工具", version=__version__,
|
||||||
|
terms_of_service="https://github.com/xaoyaoo/pywxdump",
|
||||||
|
contact={"name": "xaoyaoo", "url": "https://github.com/xaoyaoo/pywxdump", "email": "<EMAIL>"},
|
||||||
|
license_info={"name": "MIT License", "url": "https://github.com/xaoyaoo/pywxdump/blob/main/LICENSE"})
|
||||||
|
|
||||||
|
# 跨域
|
||||||
|
origins = [
|
||||||
|
"http://localhost:5000",
|
||||||
|
"http://127.0.0.1:5000",
|
||||||
|
"http://localhost:8080", # 开发环境的客户端地址"
|
||||||
|
# "http://0.0.0.0:5000",
|
||||||
|
# "*"
|
||||||
|
]
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=origins, # 允许所有源
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"], # 允许所有方法
|
||||||
|
allow_headers=["*"], # 允许所有头
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.exception_handler(RequestValidationError)
|
||||||
|
async def request_validation_exception_handler(request: Request, exc: RequestValidationError):
|
||||||
|
# print(request.body)
|
||||||
|
return ReJson(1002, {"detail": exc.errors()})
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
@app.get("/index.html")
|
||||||
|
async def redirect():
|
||||||
|
response = RedirectResponse(url="/s/index.html", status_code=307)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
# 路由挂载
|
||||||
|
app.include_router(rs_api, prefix='/api/rs', tags=['远程api'])
|
||||||
|
app.include_router(ls_api, prefix='/api/ls', tags=['本地api'])
|
||||||
|
|
||||||
|
web_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "ui", "web")
|
||||||
|
# 静态文件挂载
|
||||||
|
app.mount("/s", StaticFiles(directory=web_path), name="static")
|
||||||
|
|
||||||
|
|
||||||
|
def start_server(port=5000, online=False, debug=False, isopenBrowser=True):
|
||||||
|
"""
|
||||||
|
启动flask
|
||||||
|
:param port: 端口号
|
||||||
|
:param online: 是否在线查看(局域网查看)
|
||||||
|
:param debug: 是否开启debug模式
|
||||||
|
:param isopenBrowser: 是否自动打开浏览器
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# 全局变量
|
||||||
|
work_path = os.path.join(os.getcwd(), "wxdump_work") # 临时文件夹,用于存放图片等
|
||||||
|
if not os.path.exists(work_path):
|
||||||
|
os.makedirs(work_path)
|
||||||
|
server_loger.info(f"[+] 创建临时文件夹:{work_path}")
|
||||||
|
print(f"[+] 创建临时文件夹:{work_path}")
|
||||||
|
conf_file = os.path.join(work_path, "conf_auto.json") # 用于存放各种基础信息
|
||||||
|
auto_setting = "auto_setting"
|
||||||
|
env_file = os.path.join(work_path, ".env") # 用于存放环境变量
|
||||||
|
# set 环境变量
|
||||||
|
os.environ["PYWXDUMP_WORK_PATH"] = work_path
|
||||||
|
os.environ["PYWXDUMP_CONF_FILE"] = conf_file
|
||||||
|
os.environ["PYWXDUMP_AUTO_SETTING"] = auto_setting
|
||||||
|
|
||||||
|
with open(env_file, "w", encoding="utf-8") as f:
|
||||||
|
f.write(f"PYWXDUMP_WORK_PATH = '{work_path}'\n")
|
||||||
|
f.write(f"PYWXDUMP_CONF_FILE = '{conf_file}'\n")
|
||||||
|
f.write(f"PYWXDUMP_AUTO_SETTING = '{auto_setting}'\n")
|
||||||
|
|
||||||
|
# 检查端口是否被占用
|
||||||
|
if online:
|
||||||
|
host = '0.0.0.0'
|
||||||
|
else:
|
||||||
|
host = "127.0.0.1"
|
||||||
|
|
||||||
|
if is_port_in_use(host, port):
|
||||||
|
server_loger.error(f"Port {port} is already in use. Choose a different port.")
|
||||||
|
print(f"Port {port} is already in use. Choose a different port.")
|
||||||
|
input("Press Enter to exit...")
|
||||||
|
return # 退出程序
|
||||||
|
if isopenBrowser:
|
||||||
|
try:
|
||||||
|
# 自动打开浏览器
|
||||||
|
url = f"http://127.0.0.1:{port}/"
|
||||||
|
# 根据操作系统使用不同的命令打开默认浏览器
|
||||||
|
if sys.platform.startswith('darwin'): # macOS
|
||||||
|
subprocess.call(['open', url])
|
||||||
|
elif sys.platform.startswith('win'): # Windows
|
||||||
|
subprocess.call(['start', url], shell=True)
|
||||||
|
elif sys.platform.startswith('linux'): # Linux
|
||||||
|
subprocess.call(['xdg-open', url])
|
||||||
|
else:
|
||||||
|
server_loger.error(f"Unsupported platform, can't open browser automatically.", exc_info=True)
|
||||||
|
print("Unsupported platform, can't open browser automatically.")
|
||||||
|
except Exception as e:
|
||||||
|
server_loger.error(f"自动打开浏览器失败:{e}", exc_info=True)
|
||||||
|
|
||||||
|
time.sleep(1)
|
||||||
|
server_loger.info(f"启动flask服务,host:port:{host}:{port}")
|
||||||
|
print("[+] 请使用浏览器访问 http://127.0.0.1:5000/ 查看聊天记录")
|
||||||
|
|
||||||
|
uvicorn.run(app=app, host=host, port=port, reload=debug, log_level="info", workers=1, env_file=env_file)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["start_server", "app"]
|
||||||
|
@ -5,58 +5,55 @@
|
|||||||
# Author: xaoyaoo
|
# Author: xaoyaoo
|
||||||
# Date: 2024/08/01
|
# Date: 2024/08/01
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
import base64
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import time
|
import time
|
||||||
import shutil
|
import shutil
|
||||||
import pythoncom
|
import pythoncom
|
||||||
import pywxdump
|
|
||||||
|
|
||||||
from flask import Flask, request, render_template, g, Blueprint, send_file, make_response, session
|
from pydantic import BaseModel
|
||||||
from pywxdump import get_core_db, all_merge_real_time_db, get_wx_db
|
from fastapi import APIRouter
|
||||||
from pywxdump.api.rjson import ReJson, RqJson
|
|
||||||
from pywxdump.api.utils import get_conf, get_conf_wxids, set_conf, error9999, gen_base64, validate_title, \
|
from pywxdump import all_merge_real_time_db, get_wx_db
|
||||||
get_conf_local_wxid, ls_loger, random_str
|
from pywxdump import get_wx_info, batch_decrypt, BiasAddr, merge_db, decrypt_merge
|
||||||
from pywxdump import get_wx_info, WX_OFFS, batch_decrypt, BiasAddr, merge_db, decrypt_merge, merge_real_time_db
|
|
||||||
ls_api = Blueprint('ls_api', __name__, template_folder='../ui/web', static_folder='../ui/web/assets/', )
|
from .rjson import ReJson, RqJson
|
||||||
ls_api.debug = False
|
from .utils import error9999, ls_loger, random_str, gc
|
||||||
|
|
||||||
|
ls_api = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
# 以下为初始化相关 *******************************************************************************************************
|
# 以下为初始化相关 *******************************************************************************************************
|
||||||
|
|
||||||
@ls_api.route('/api/ls/init_last_local_wxid', methods=["GET", 'POST'])
|
@ls_api.api_route('/init_last_local_wxid', methods=["GET", 'POST'])
|
||||||
@error9999
|
@error9999
|
||||||
def init_last_local_wxid():
|
def init_last_local_wxid():
|
||||||
"""
|
"""
|
||||||
初始化,包括key
|
初始化,包括key
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
local_wxid = get_conf_local_wxid(g.caf)
|
local_wxid = gc.get_local_wxids()
|
||||||
local_wxid.remove(g.at)
|
local_wxid.remove(gc.at)
|
||||||
if local_wxid:
|
if local_wxid:
|
||||||
return ReJson(0, {"local_wxids": local_wxid})
|
return ReJson(0, {"local_wxids": local_wxid})
|
||||||
return ReJson(0, {"local_wxids": []})
|
return ReJson(0, {"local_wxids": []})
|
||||||
|
|
||||||
|
|
||||||
@ls_api.route('/api/ls/init_last', methods=["GET", 'POST'])
|
@ls_api.api_route('/init_last', methods=["GET", 'POST'])
|
||||||
@error9999
|
@error9999
|
||||||
def init_last():
|
def init_last(my_wxid: str):
|
||||||
"""
|
"""
|
||||||
是否初始化
|
是否初始化
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
my_wxid = request.json.get("my_wxid", "")
|
|
||||||
my_wxid = my_wxid.strip().strip("'").strip('"') if isinstance(my_wxid, str) else ""
|
my_wxid = my_wxid.strip().strip("'").strip('"') if isinstance(my_wxid, str) else ""
|
||||||
if not my_wxid:
|
if not my_wxid:
|
||||||
my_wxid = get_conf(g.caf, "auto_setting", "last")
|
my_wxid = gc.get_conf(gc.at, "last")
|
||||||
|
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
||||||
if my_wxid:
|
if my_wxid:
|
||||||
set_conf(g.caf, "auto_setting", "last", my_wxid)
|
gc.set_conf(gc.at, "last", my_wxid)
|
||||||
merge_path = get_conf(g.caf, my_wxid, "merge_path")
|
merge_path = gc.get_conf(my_wxid, "merge_path")
|
||||||
wx_path = get_conf(g.caf, my_wxid, "wx_path")
|
wx_path = gc.get_conf(my_wxid, "wx_path")
|
||||||
key = get_conf(g.caf, my_wxid, "key")
|
key = gc.get_conf(my_wxid, "key")
|
||||||
rdata = {
|
rdata = {
|
||||||
"merge_path": merge_path,
|
"merge_path": merge_path,
|
||||||
"wx_path": wx_path,
|
"wx_path": wx_path,
|
||||||
@ -69,16 +66,23 @@ def init_last():
|
|||||||
return ReJson(0, {"is_init": False, "my_wxid": ""})
|
return ReJson(0, {"is_init": False, "my_wxid": ""})
|
||||||
|
|
||||||
|
|
||||||
@ls_api.route('/api/ls/init_key', methods=["GET", 'POST'])
|
class InitKeyRequest(BaseModel):
|
||||||
|
wx_path: str
|
||||||
|
key: str
|
||||||
|
my_wxid: str
|
||||||
|
|
||||||
|
|
||||||
|
@ls_api.api_route('/init_key', methods=["GET", 'POST'])
|
||||||
@error9999
|
@error9999
|
||||||
def init_key():
|
def init_key(request: InitKeyRequest):
|
||||||
"""
|
"""
|
||||||
初始化,包括key
|
初始化key
|
||||||
|
:param request:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
wx_path = request.json.get("wx_path", "").strip().strip("'").strip('"')
|
wx_path = request.wx_path.strip().strip("'").strip('"')
|
||||||
key = request.json.get("key", "").strip().strip("'").strip('"')
|
key = request.key.strip().strip("'").strip('"')
|
||||||
my_wxid = request.json.get("my_wxid", "").strip().strip("'").strip('"')
|
my_wxid = request.my_wxid.strip().strip("'").strip('"')
|
||||||
if not wx_path:
|
if not wx_path:
|
||||||
return ReJson(1002, body=f"wx_path is required: {wx_path}")
|
return ReJson(1002, body=f"wx_path is required: {wx_path}")
|
||||||
if not os.path.exists(wx_path):
|
if not os.path.exists(wx_path):
|
||||||
@ -92,8 +96,10 @@ def init_key():
|
|||||||
# if isinstance(db_config, dict) and db_config and os.path.exists(db_config.get("path")):
|
# if isinstance(db_config, dict) and db_config and os.path.exists(db_config.get("path")):
|
||||||
# pmsg = DBHandler(db_config)
|
# pmsg = DBHandler(db_config)
|
||||||
# # pmsg.close_all_connection()
|
# # pmsg.close_all_connection()
|
||||||
|
print(id(gc))
|
||||||
|
print(wx_path, "\n", key, "\n", my_wxid, "\n", gc.work_path)
|
||||||
|
|
||||||
out_path = os.path.join(g.work_path, "decrypted", my_wxid) if my_wxid else os.path.join(g.work_path, "decrypted")
|
out_path = os.path.join(gc.work_path, "decrypted", my_wxid) if my_wxid else os.path.join(gc.work_path, "decrypted")
|
||||||
# 检查文件夹中文件是否被占用
|
# 检查文件夹中文件是否被占用
|
||||||
if os.path.exists(out_path):
|
if os.path.exists(out_path):
|
||||||
try:
|
try:
|
||||||
@ -107,9 +113,9 @@ def init_key():
|
|||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
if code:
|
if code:
|
||||||
# 移动merge_save_path到g.work_path/my_wxid
|
# 移动merge_save_path到g.work_path/my_wxid
|
||||||
if not os.path.exists(os.path.join(g.work_path, my_wxid)):
|
if not os.path.exists(os.path.join(gc.work_path, my_wxid)):
|
||||||
os.makedirs(os.path.join(g.work_path, my_wxid))
|
os.makedirs(os.path.join(gc.work_path, my_wxid))
|
||||||
merge_save_path_new = os.path.join(g.work_path, my_wxid, "merge_all.db")
|
merge_save_path_new = os.path.join(gc.work_path, my_wxid, "merge_all.db")
|
||||||
shutil.move(merge_save_path, str(merge_save_path_new))
|
shutil.move(merge_save_path, str(merge_save_path_new))
|
||||||
|
|
||||||
# 删除out_path
|
# 删除out_path
|
||||||
@ -124,12 +130,13 @@ def init_key():
|
|||||||
"type": "sqlite",
|
"type": "sqlite",
|
||||||
"path": merge_save_path_new
|
"path": merge_save_path_new
|
||||||
}
|
}
|
||||||
set_conf(g.caf, my_wxid, "db_config", db_config)
|
gc.set_conf(my_wxid, "db_config", db_config)
|
||||||
set_conf(g.caf, my_wxid, "merge_path", merge_save_path_new)
|
gc.set_conf(my_wxid, "db_config", db_config)
|
||||||
set_conf(g.caf, my_wxid, "wx_path", wx_path)
|
gc.set_conf(my_wxid, "merge_path", merge_save_path_new)
|
||||||
set_conf(g.caf, my_wxid, "key", key)
|
gc.set_conf(my_wxid, "wx_path", wx_path)
|
||||||
set_conf(g.caf, my_wxid, "my_wxid", my_wxid)
|
gc.set_conf(my_wxid, "key", key)
|
||||||
set_conf(g.caf, "auto_setting", "last", my_wxid)
|
gc.set_conf(my_wxid, "my_wxid", my_wxid)
|
||||||
|
gc.set_conf(gc.at, "last", my_wxid)
|
||||||
rdata = {
|
rdata = {
|
||||||
"merge_path": merge_save_path_new,
|
"merge_path": merge_save_path_new,
|
||||||
"wx_path": wx_path,
|
"wx_path": wx_path,
|
||||||
@ -142,16 +149,22 @@ def init_key():
|
|||||||
return ReJson(2001, body=merge_save_path)
|
return ReJson(2001, body=merge_save_path)
|
||||||
|
|
||||||
|
|
||||||
@ls_api.route('/api/ls/init_nokey', methods=["GET", 'POST'])
|
class InitNoKeyRequest(BaseModel):
|
||||||
|
merge_path: str
|
||||||
|
wx_path: str
|
||||||
|
my_wxid: str
|
||||||
|
|
||||||
|
|
||||||
|
@ls_api.post('/init_nokey')
|
||||||
@error9999
|
@error9999
|
||||||
def init_nokey():
|
def init_nokey(request: InitNoKeyRequest):
|
||||||
"""
|
"""
|
||||||
初始化,包括key
|
初始化,包括key
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
merge_path = request.json.get("merge_path", "").strip().strip("'").strip('"')
|
merge_path = request.merge_path.strip().strip("'").strip('"')
|
||||||
wx_path = request.json.get("wx_path", "").strip().strip("'").strip('"')
|
wx_path = request.wx_path.strip().strip("'").strip('"')
|
||||||
my_wxid = request.json.get("my_wxid", "").strip().strip("'").strip('"')
|
my_wxid = request.my_wxid.strip().strip("'").strip('"')
|
||||||
|
|
||||||
if not wx_path:
|
if not wx_path:
|
||||||
return ReJson(1002, body=f"wx_path is required: {wx_path}")
|
return ReJson(1002, body=f"wx_path is required: {wx_path}")
|
||||||
@ -162,18 +175,18 @@ def init_nokey():
|
|||||||
if not my_wxid:
|
if not my_wxid:
|
||||||
return ReJson(1002, body=f"my_wxid is required: {my_wxid}")
|
return ReJson(1002, body=f"my_wxid is required: {my_wxid}")
|
||||||
|
|
||||||
key = get_conf(g.caf, my_wxid, "key")
|
key = gc.get_conf(my_wxid, "key")
|
||||||
db_config = {
|
db_config = {
|
||||||
"key": random_str(16),
|
"key": random_str(16),
|
||||||
"type": "sqlite",
|
"type": "sqlite",
|
||||||
"path": merge_path
|
"path": merge_path
|
||||||
}
|
}
|
||||||
set_conf(g.caf, my_wxid, "db_config", db_config)
|
gc.set_conf(my_wxid, "db_config", db_config)
|
||||||
set_conf(g.caf, my_wxid, "merge_path", merge_path)
|
gc.set_conf(my_wxid, "merge_path", merge_path)
|
||||||
set_conf(g.caf, my_wxid, "wx_path", wx_path)
|
gc.set_conf(my_wxid, "wx_path", wx_path)
|
||||||
set_conf(g.caf, my_wxid, "key", key)
|
gc.set_conf(my_wxid, "key", key)
|
||||||
set_conf(g.caf, my_wxid, "my_wxid", my_wxid)
|
gc.set_conf(my_wxid, "my_wxid", my_wxid)
|
||||||
set_conf(g.caf, g.at, "last", my_wxid)
|
gc.set_conf(gc.at, "last", my_wxid)
|
||||||
rdata = {
|
rdata = {
|
||||||
"merge_path": merge_path,
|
"merge_path": merge_path,
|
||||||
"wx_path": wx_path,
|
"wx_path": wx_path,
|
||||||
@ -187,24 +200,24 @@ def init_nokey():
|
|||||||
# END 以上为初始化相关 ***************************************************************************************************
|
# END 以上为初始化相关 ***************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
@ls_api.route('/api/ls/realtimemsg', methods=["GET", "POST"])
|
@ls_api.api_route('/realtimemsg', methods=["GET", "POST"])
|
||||||
@error9999
|
@error9999
|
||||||
def get_real_time_msg():
|
def get_real_time_msg():
|
||||||
"""
|
"""
|
||||||
获取实时消息 使用 merge_real_time_db()函数
|
获取实时消息 使用 merge_real_time_db()函数
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
my_wxid = get_conf(g.caf, g.at, "last")
|
my_wxid = gc.get_conf(gc.at, "last")
|
||||||
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
||||||
|
|
||||||
merge_path = get_conf(g.caf, my_wxid, "merge_path")
|
merge_path = gc.get_conf(my_wxid, "merge_path")
|
||||||
key = get_conf(g.caf, my_wxid, "key")
|
key = gc.get_conf(my_wxid, "key")
|
||||||
wx_path = get_conf(g.caf, my_wxid, "wx_path")
|
wx_path = gc.get_conf(my_wxid, "wx_path")
|
||||||
|
|
||||||
if not merge_path or not key or not wx_path or not wx_path:
|
if not merge_path or not key or not wx_path or not wx_path:
|
||||||
return ReJson(1002, body="msg_path or media_path or wx_path or key is required")
|
return ReJson(1002, body="msg_path or media_path or wx_path or key is required")
|
||||||
|
|
||||||
real_time_exe_path = get_conf(g.caf, g.at, "real_time_exe_path")
|
real_time_exe_path = gc.get_conf(gc.at, "real_time_exe_path")
|
||||||
|
|
||||||
code, ret = all_merge_real_time_db(key=key, wx_path=wx_path, merge_path=merge_path,
|
code, ret = all_merge_real_time_db(key=key, wx_path=wx_path, merge_path=merge_path,
|
||||||
real_time_exe_path=real_time_exe_path)
|
real_time_exe_path=real_time_exe_path)
|
||||||
@ -216,7 +229,7 @@ def get_real_time_msg():
|
|||||||
|
|
||||||
# start 这部分为专业工具的api *********************************************************************************************
|
# start 这部分为专业工具的api *********************************************************************************************
|
||||||
|
|
||||||
@ls_api.route('/api/ls/wxinfo', methods=["GET", 'POST'])
|
@ls_api.api_route('/wxinfo', methods=["GET", 'POST'])
|
||||||
@error9999
|
@error9999
|
||||||
def get_wxinfo():
|
def get_wxinfo():
|
||||||
"""
|
"""
|
||||||
@ -224,24 +237,33 @@ def get_wxinfo():
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
import pythoncom
|
import pythoncom
|
||||||
pythoncom.CoInitialize()
|
from pywxdump import WX_OFFS
|
||||||
|
pythoncom.CoInitialize() # 初始化COM库
|
||||||
wxinfos = get_wx_info(WX_OFFS)
|
wxinfos = get_wx_info(WX_OFFS)
|
||||||
pythoncom.CoUninitialize()
|
pythoncom.CoUninitialize() # 释放COM库
|
||||||
return ReJson(0, wxinfos)
|
return ReJson(0, wxinfos)
|
||||||
|
|
||||||
|
|
||||||
@ls_api.route('/api/ls/biasaddr', methods=["GET", 'POST'])
|
class BiasAddrRequest(BaseModel):
|
||||||
|
mobile: str
|
||||||
|
name: str
|
||||||
|
account: str
|
||||||
|
key: str = ""
|
||||||
|
wxdbPath: str = ""
|
||||||
|
|
||||||
|
|
||||||
|
@ls_api.post('/biasaddr')
|
||||||
@error9999
|
@error9999
|
||||||
def biasaddr():
|
def biasaddr(request: BiasAddrRequest):
|
||||||
"""
|
"""
|
||||||
BiasAddr
|
BiasAddr
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
mobile = request.json.get("mobile")
|
mobile = request.mobile
|
||||||
name = request.json.get("name")
|
name = request.name
|
||||||
account = request.json.get("account")
|
account = request.account
|
||||||
key = request.json.get("key", "")
|
key = request.json.key
|
||||||
wxdbPath = request.json.get("wxdbPath", "")
|
wxdbPath = request.wxdbPath
|
||||||
if not mobile or not name or not account:
|
if not mobile or not name or not account:
|
||||||
return ReJson(1002)
|
return ReJson(1002)
|
||||||
pythoncom.CoInitialize()
|
pythoncom.CoInitialize()
|
||||||
@ -249,39 +271,33 @@ def biasaddr():
|
|||||||
return ReJson(0, str(rdata))
|
return ReJson(0, str(rdata))
|
||||||
|
|
||||||
|
|
||||||
@ls_api.route('/api/ls/decrypt', methods=["GET", 'POST'])
|
@ls_api.api_route('/decrypt', methods=["GET", 'POST'])
|
||||||
@error9999
|
@error9999
|
||||||
def decrypt():
|
def decrypt(key: str, wxdbPath: str, outPath: str = ""):
|
||||||
"""
|
"""
|
||||||
解密
|
解密
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
key = request.json.get("key")
|
if not outPath:
|
||||||
if not key:
|
outPath = gc.work_path
|
||||||
return ReJson(1002)
|
wxinfos = batch_decrypt(key, wxdbPath, out_path=outPath)
|
||||||
wxdb_path = request.json.get("wxdbPath")
|
|
||||||
if not wxdb_path:
|
|
||||||
return ReJson(1002)
|
|
||||||
out_path = request.json.get("outPath")
|
|
||||||
if not out_path:
|
|
||||||
out_path = g.tmp_path
|
|
||||||
wxinfos = batch_decrypt(key, wxdb_path, out_path=out_path)
|
|
||||||
return ReJson(0, str(wxinfos))
|
return ReJson(0, str(wxinfos))
|
||||||
|
|
||||||
|
|
||||||
@ls_api.route('/api/ls/merge', methods=["GET", 'POST'])
|
class MergeRequest(BaseModel):
|
||||||
|
dbPath: str
|
||||||
|
outPath: str
|
||||||
|
|
||||||
|
|
||||||
|
@ls_api.post('/merge')
|
||||||
@error9999
|
@error9999
|
||||||
def merge():
|
def merge(request: MergeRequest):
|
||||||
"""
|
"""
|
||||||
合并
|
合并
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
wxdb_path = request.json.get("dbPath")
|
wxdb_path = request.dbPath
|
||||||
if not wxdb_path:
|
out_path = request.outPath
|
||||||
return ReJson(1002)
|
|
||||||
out_path = request.json.get("outPath")
|
|
||||||
if not out_path:
|
|
||||||
return ReJson(1002)
|
|
||||||
db_path = get_wx_db(wxdb_path)
|
db_path = get_wx_db(wxdb_path)
|
||||||
# for i in db_path:print(i)
|
# for i in db_path:print(i)
|
||||||
rdata = merge_db(db_path, out_path)
|
rdata = merge_db(db_path, out_path)
|
||||||
|
@ -5,43 +5,38 @@
|
|||||||
# Author: xaoyaoo
|
# Author: xaoyaoo
|
||||||
# Date: 2024/01/02
|
# Date: 2024/01/02
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
import base64
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import time
|
import time
|
||||||
import shutil
|
import shutil
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
|
from urllib.parse import quote
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from fastapi import APIRouter,Response
|
||||||
|
from starlette.responses import StreamingResponse, FileResponse
|
||||||
|
|
||||||
import pythoncom
|
|
||||||
import pywxdump
|
import pywxdump
|
||||||
|
from pywxdump import decrypt_merge,get_core_db
|
||||||
from flask import Flask, request, render_template, g, Blueprint, send_file, make_response, session
|
|
||||||
from pywxdump import get_core_db, all_merge_real_time_db
|
|
||||||
from pywxdump.api.rjson import ReJson, RqJson
|
|
||||||
from pywxdump.api.utils import get_conf, get_conf_wxids, set_conf, error9999, gen_base64, validate_title, \
|
|
||||||
get_conf_local_wxid
|
|
||||||
from pywxdump import get_wx_info, WX_OFFS, batch_decrypt, BiasAddr, merge_db, decrypt_merge, merge_real_time_db
|
|
||||||
|
|
||||||
from pywxdump.db import DBHandler, download_file, dat2img
|
from pywxdump.db import DBHandler, download_file, dat2img
|
||||||
from pywxdump.db.export import export_csv, export_json, export_html
|
from pywxdump.db.export import export_csv, export_json, export_html
|
||||||
|
|
||||||
# app = Flask(__name__, static_folder='../ui/web/dist', static_url_path='/')
|
from .rjson import ReJson, RqJson
|
||||||
|
from .utils import error9999, gc, asyncError9999
|
||||||
|
|
||||||
rs_api = Blueprint('rs_api', __name__, template_folder='../ui/web', static_folder='../ui/web/assets/', )
|
rs_api = APIRouter()
|
||||||
rs_api.debug = False
|
|
||||||
|
|
||||||
|
|
||||||
# 是否初始化
|
# 是否初始化
|
||||||
@rs_api.route('/api/rs/is_init', methods=["GET", 'POST'])
|
@rs_api.api_route('/is_init', methods=["GET", 'POST'])
|
||||||
@error9999
|
@error9999
|
||||||
def is_init():
|
def is_init():
|
||||||
"""
|
"""
|
||||||
是否初始化
|
是否初始化
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
local_wxids = get_conf_local_wxid(g.caf)
|
|
||||||
|
local_wxids = gc.get_local_wxids()
|
||||||
if len(local_wxids) > 1:
|
if len(local_wxids) > 1:
|
||||||
return ReJson(0, True)
|
return ReJson(0, True)
|
||||||
return ReJson(0, False)
|
return ReJson(0, False)
|
||||||
@ -49,72 +44,62 @@ def is_init():
|
|||||||
|
|
||||||
# start 以下为聊天联系人相关api *******************************************************************************************
|
# start 以下为聊天联系人相关api *******************************************************************************************
|
||||||
|
|
||||||
@rs_api.route('/api/rs/mywxid', methods=["GET", 'POST'])
|
@rs_api.api_route('/mywxid', methods=["GET", 'POST'])
|
||||||
@error9999
|
@error9999
|
||||||
def mywxid():
|
def mywxid():
|
||||||
"""
|
"""
|
||||||
获取我的微信id
|
获取我的微信id
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
my_wxid = get_conf(g.caf, g.at, "last")
|
my_wxid = gc.get_conf(gc.at, "last")
|
||||||
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
||||||
return ReJson(0, {"my_wxid": my_wxid})
|
return ReJson(0, {"my_wxid": my_wxid})
|
||||||
|
|
||||||
|
|
||||||
@rs_api.route('/api/rs/user_session_list', methods=["GET", 'POST'])
|
@rs_api.api_route('/user_session_list', methods=["GET", 'POST'])
|
||||||
@error9999
|
@error9999
|
||||||
def user_session_list():
|
def user_session_list():
|
||||||
"""
|
"""
|
||||||
获取联系人列表
|
获取联系人列表
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
my_wxid = get_conf(g.caf, g.at, "last")
|
my_wxid = gc.get_conf(gc.at, "last")
|
||||||
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
||||||
db_config = get_conf(g.caf, my_wxid, "db_config")
|
db_config = gc.get_conf(my_wxid, "db_config")
|
||||||
db = DBHandler(db_config, my_wxid=my_wxid)
|
db = DBHandler(db_config, my_wxid=my_wxid)
|
||||||
ret = db.get_session_list()
|
ret = db.get_session_list()
|
||||||
return ReJson(0, list(ret.values()))
|
return ReJson(0, list(ret.values()))
|
||||||
|
|
||||||
|
|
||||||
@rs_api.route('/api/rs/user_labels_dict', methods=["GET", 'POST'])
|
@rs_api.api_route('/user_labels_dict', methods=["GET", 'POST'])
|
||||||
@error9999
|
@error9999
|
||||||
def user_labels_dict():
|
def user_labels_dict():
|
||||||
"""
|
"""
|
||||||
获取标签字典
|
获取标签字典
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
my_wxid = get_conf(g.caf, g.at, "last")
|
|
||||||
|
my_wxid = gc.get_conf(gc.at, "last")
|
||||||
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
||||||
db_config = get_conf(g.caf, my_wxid, "db_config")
|
db_config = gc.get_conf(my_wxid, "db_config")
|
||||||
db = DBHandler(db_config, my_wxid=my_wxid)
|
db = DBHandler(db_config, my_wxid=my_wxid)
|
||||||
user_labels_dict = db.get_labels()
|
user_labels_dict = db.get_labels()
|
||||||
return ReJson(0, user_labels_dict)
|
return ReJson(0, user_labels_dict)
|
||||||
|
|
||||||
|
|
||||||
@rs_api.route('/api/rs/user_list', methods=["GET", 'POST'])
|
@rs_api.post('/user_list')
|
||||||
@error9999
|
@error9999
|
||||||
def user_list():
|
def user_list(word: str = "", wxids: List[str] = None, labels: List[str] = None):
|
||||||
"""
|
"""
|
||||||
获取联系人列表,可用于搜索
|
获取联系人列表,可用于搜索
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if request.method == "GET":
|
|
||||||
word = request.args.get("word", "")
|
|
||||||
wxids = request.args.get("wxids", [])
|
|
||||||
labels = request.args.get("labels", [])
|
|
||||||
elif request.method == "POST":
|
|
||||||
word = request.json.get("word", "")
|
|
||||||
wxids = request.json.get("wxids", [])
|
|
||||||
labels = request.json.get("labels", [])
|
|
||||||
else:
|
|
||||||
return ReJson(1003, msg="Unsupported method")
|
|
||||||
|
|
||||||
if isinstance(wxids, str) and wxids == '' or wxids is None: wxids = []
|
if isinstance(wxids, str) and wxids == '' or wxids is None: wxids = []
|
||||||
if isinstance(labels, str) and labels == '' or labels is None: labels = []
|
if isinstance(labels, str) and labels == '' or labels is None: labels = []
|
||||||
|
|
||||||
my_wxid = get_conf(g.caf, g.at, "last")
|
my_wxid = gc.get_conf(gc.at, "last")
|
||||||
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
||||||
db_config = get_conf(g.caf, my_wxid, "db_config")
|
db_config = gc.get_conf(my_wxid, "db_config")
|
||||||
db = DBHandler(db_config, my_wxid=my_wxid)
|
db = DBHandler(db_config, my_wxid=my_wxid)
|
||||||
users = db.get_user(word, wxids, labels)
|
users = db.get_user(word, wxids, labels)
|
||||||
return ReJson(0, users)
|
return ReJson(0, users)
|
||||||
@ -124,26 +109,72 @@ def user_list():
|
|||||||
|
|
||||||
# start 以下为聊天记录相关api *********************************************************************************************
|
# start 以下为聊天记录相关api *********************************************************************************************
|
||||||
|
|
||||||
|
class MsgCountRequest(BaseModel):
|
||||||
|
wxids: Optional[List[str]]
|
||||||
|
|
||||||
@rs_api.route('/api/rs/imgsrc/<path:imgsrc>', methods=["GET", 'POST'])
|
|
||||||
|
@rs_api.post('/msg_count')
|
||||||
@error9999
|
@error9999
|
||||||
def get_imgsrc(imgsrc):
|
def msg_count(request: MsgCountRequest):
|
||||||
|
"""
|
||||||
|
获取联系人的聊天记录数量
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
wxids = request.wxids
|
||||||
|
my_wxid = gc.get_conf(gc.at, "last")
|
||||||
|
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
||||||
|
db_config = gc.get_db_config()
|
||||||
|
db = DBHandler(db_config, my_wxid=my_wxid)
|
||||||
|
count = db.get_msgs_count(wxids)
|
||||||
|
return ReJson(0, count)
|
||||||
|
|
||||||
|
|
||||||
|
class MsgListRequest(BaseModel):
|
||||||
|
wxid: str
|
||||||
|
start: int
|
||||||
|
limit: int
|
||||||
|
|
||||||
|
|
||||||
|
@rs_api.api_route('/msg_list', methods=["GET", 'POST'])
|
||||||
|
@error9999
|
||||||
|
def get_msgs(request: MsgListRequest):
|
||||||
|
"""
|
||||||
|
获取联系人的聊天记录
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
wxid = request.wxid
|
||||||
|
start = request.start
|
||||||
|
limit = request.limit
|
||||||
|
|
||||||
|
my_wxid = gc.get_conf(gc.at, "last")
|
||||||
|
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
||||||
|
db_config = gc.get_conf(my_wxid, "db_config")
|
||||||
|
|
||||||
|
db = DBHandler(db_config, my_wxid=my_wxid)
|
||||||
|
msgs, users = db.get_msgs(wxid=wxid, start_index=start, page_size=limit)
|
||||||
|
return ReJson(0, {"msg_list": msgs, "user_list": users})
|
||||||
|
|
||||||
|
|
||||||
|
@rs_api.get('/imgsrc')
|
||||||
|
@asyncError9999
|
||||||
|
async def get_imgsrc(src: str):
|
||||||
"""
|
"""
|
||||||
获取图片,
|
获取图片,
|
||||||
1. 从网络获取图片,主要功能只是下载图片,缓存到本地
|
1. 从网络获取图片,主要功能只是下载图片,缓存到本地
|
||||||
2. 读取本地图片
|
2. 读取本地图片
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
imgsrc = src
|
||||||
if not imgsrc:
|
if not imgsrc:
|
||||||
return ReJson(1002)
|
return ReJson(1002)
|
||||||
if imgsrc.startswith("FileStorage"): # 如果是本地图片文件则调用get_img
|
if imgsrc.startswith("FileStorage"): # 如果是本地图片文件则调用get_img
|
||||||
my_wxid = get_conf(g.caf, g.at, "last")
|
my_wxid = gc.get_conf(gc.at, "last")
|
||||||
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
||||||
wx_path = get_conf(g.caf, my_wxid, "wx_path")
|
wx_path = gc.get_conf(my_wxid, "wx_path")
|
||||||
|
|
||||||
img_path = imgsrc.replace("\\\\", "\\")
|
img_path = imgsrc.replace("\\\\", "\\")
|
||||||
|
|
||||||
img_tmp_path = os.path.join(g.work_path, my_wxid, "img")
|
img_tmp_path = os.path.join(gc.work_path, my_wxid, "img")
|
||||||
original_img_path = os.path.join(wx_path, img_path)
|
original_img_path = os.path.join(wx_path, img_path)
|
||||||
if os.path.exists(original_img_path):
|
if os.path.exists(original_img_path):
|
||||||
rc, fomt, md5, out_bytes = dat2img(original_img_path)
|
rc, fomt, md5, out_bytes = dat2img(original_img_path)
|
||||||
@ -151,21 +182,21 @@ def get_imgsrc(imgsrc):
|
|||||||
return ReJson(1001, body=original_img_path)
|
return ReJson(1001, body=original_img_path)
|
||||||
imgsavepath = os.path.join(str(img_tmp_path), img_path + "_" + "".join([md5, fomt]))
|
imgsavepath = os.path.join(str(img_tmp_path), img_path + "_" + "".join([md5, fomt]))
|
||||||
if os.path.exists(imgsavepath):
|
if os.path.exists(imgsavepath):
|
||||||
return send_file(imgsavepath)
|
return FileResponse(imgsavepath)
|
||||||
if not os.path.exists(os.path.dirname(imgsavepath)):
|
if not os.path.exists(os.path.dirname(imgsavepath)):
|
||||||
os.makedirs(os.path.dirname(imgsavepath))
|
os.makedirs(os.path.dirname(imgsavepath))
|
||||||
with open(imgsavepath, "wb") as f:
|
with open(imgsavepath, "wb") as f:
|
||||||
f.write(out_bytes)
|
f.write(out_bytes)
|
||||||
return send_file(imgsavepath)
|
return Response(content=out_bytes, media_type="image/jpeg")
|
||||||
else:
|
else:
|
||||||
return ReJson(1001, body=f"{original_img_path} not exists")
|
return ReJson(1001, body=f"{original_img_path} not exists")
|
||||||
elif imgsrc.startswith("http://") or imgsrc.startswith("https://"):
|
elif imgsrc.startswith("http://") or imgsrc.startswith("https://"):
|
||||||
# 将?后面的参数连接到imgsrc
|
# 将?后面的参数连接到imgsrc
|
||||||
imgsrc = imgsrc + "?" + request.query_string.decode("utf-8") if request.query_string else imgsrc
|
|
||||||
my_wxid = get_conf(g.caf, g.at, "last")
|
my_wxid = gc.get_conf(gc.at, "last")
|
||||||
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
||||||
|
|
||||||
img_tmp_path = os.path.join(g.work_path, my_wxid, "imgsrc")
|
img_tmp_path = os.path.join(gc.work_path, my_wxid, "imgsrc")
|
||||||
if not os.path.exists(img_tmp_path):
|
if not os.path.exists(img_tmp_path):
|
||||||
os.makedirs(img_tmp_path)
|
os.makedirs(img_tmp_path)
|
||||||
file_name = imgsrc.replace("http://", "").replace("https://", "").replace("/", "_").replace("?", "_")
|
file_name = imgsrc.replace("http://", "").replace("https://", "").replace("/", "_").replace("?", "_")
|
||||||
@ -176,97 +207,69 @@ def get_imgsrc(imgsrc):
|
|||||||
|
|
||||||
img_path_all = os.path.join(str(img_tmp_path), file_name)
|
img_path_all = os.path.join(str(img_tmp_path), file_name)
|
||||||
if os.path.exists(img_path_all):
|
if os.path.exists(img_path_all):
|
||||||
return send_file(img_path_all)
|
return FileResponse(img_path_all)
|
||||||
else:
|
else:
|
||||||
download_file(imgsrc, img_path_all)
|
# proxies = {
|
||||||
|
# "http": "http://127.0.0.1:10809",
|
||||||
|
# "https": "http://127.0.0.1:10809",
|
||||||
|
# }
|
||||||
|
proxies = None
|
||||||
|
download_file(imgsrc, img_path_all, proxies=proxies)
|
||||||
if os.path.exists(img_path_all):
|
if os.path.exists(img_path_all):
|
||||||
return send_file(img_path_all)
|
return FileResponse(img_path_all)
|
||||||
else:
|
else:
|
||||||
return ReJson(4004, body=imgsrc)
|
return ReJson(4004, body=imgsrc)
|
||||||
else:
|
else:
|
||||||
return ReJson(1002, body=imgsrc)
|
return ReJson(1002, body=imgsrc)
|
||||||
|
|
||||||
|
|
||||||
@rs_api.route('/api/rs/msg_count', methods=["GET", 'POST'])
|
@rs_api.api_route('/video', methods=["GET", 'POST'])
|
||||||
@error9999
|
def get_video(src: str):
|
||||||
def msg_count():
|
|
||||||
"""
|
"""
|
||||||
获取联系人的聊天记录数量
|
获取视频
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if request.method == "GET":
|
videoPath = src
|
||||||
wxid = request.args.get("wxids", [])
|
if not videoPath:
|
||||||
elif request.method == "POST":
|
return ReJson(1002)
|
||||||
wxid = request.json.get("wxids", [])
|
my_wxid = gc.get_conf(gc.at, "last")
|
||||||
else:
|
|
||||||
return ReJson(1003, msg="Unsupported method")
|
|
||||||
|
|
||||||
my_wxid = get_conf(g.caf, g.at, "last")
|
|
||||||
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
||||||
db_config = get_conf(g.caf, my_wxid, "db_config")
|
wx_path = gc.get_conf(my_wxid, "wx_path")
|
||||||
db = DBHandler(db_config, my_wxid=my_wxid)
|
|
||||||
count = db.get_msgs_count(wxid)
|
|
||||||
return ReJson(0, count)
|
|
||||||
|
|
||||||
|
|
||||||
@rs_api.route('/api/rs/msg_list', methods=["GET", 'POST'])
|
|
||||||
@error9999
|
|
||||||
def get_msgs():
|
|
||||||
my_wxid = get_conf(g.caf, g.at, "last")
|
|
||||||
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
|
||||||
db_config = get_conf(g.caf, my_wxid, "db_config")
|
|
||||||
|
|
||||||
start = request.json.get("start")
|
|
||||||
limit = request.json.get("limit")
|
|
||||||
wxid = request.json.get("wxid")
|
|
||||||
|
|
||||||
if not wxid:
|
|
||||||
return ReJson(1002, body=f"wxid is required: {wxid}")
|
|
||||||
if start and isinstance(start, str) and start.isdigit():
|
|
||||||
start = int(start)
|
|
||||||
if limit and isinstance(limit, str) and limit.isdigit():
|
|
||||||
limit = int(limit)
|
|
||||||
if start is None or limit is None:
|
|
||||||
return ReJson(1002, body=f"start or limit is required {start} {limit}")
|
|
||||||
if not isinstance(start, int) and not isinstance(limit, int):
|
|
||||||
return ReJson(1002, body=f"start or limit is not int {start} {limit}")
|
|
||||||
|
|
||||||
db = DBHandler(db_config, my_wxid=my_wxid)
|
|
||||||
msgs, users = db.get_msgs(wxid=wxid, start_index=start, page_size=limit)
|
|
||||||
return ReJson(0, {"msg_list": msgs, "user_list": users})
|
|
||||||
|
|
||||||
|
|
||||||
@rs_api.route('/api/rs/video/<path:videoPath>', methods=["GET", 'POST'])
|
|
||||||
def get_video(videoPath):
|
|
||||||
my_wxid = get_conf(g.caf, g.at, "last")
|
|
||||||
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
|
||||||
wx_path = get_conf(g.caf, my_wxid, "wx_path")
|
|
||||||
|
|
||||||
videoPath = videoPath.replace("\\\\", "\\")
|
videoPath = videoPath.replace("\\\\", "\\")
|
||||||
|
|
||||||
video_tmp_path = os.path.join(g.work_path, my_wxid, "video")
|
video_tmp_path = os.path.join(gc.work_path, my_wxid, "video")
|
||||||
original_img_path = os.path.join(wx_path, videoPath)
|
original_img_path = os.path.join(wx_path, videoPath)
|
||||||
if not os.path.exists(original_img_path):
|
if not os.path.exists(original_img_path):
|
||||||
return ReJson(5002)
|
return ReJson(5002)
|
||||||
# 复制文件到临时文件夹
|
# 复制文件到临时文件夹
|
||||||
|
assert isinstance(video_tmp_path, str)
|
||||||
video_save_path = os.path.join(video_tmp_path, videoPath)
|
video_save_path = os.path.join(video_tmp_path, videoPath)
|
||||||
if not os.path.exists(os.path.dirname(video_save_path)):
|
if not os.path.exists(os.path.dirname(video_save_path)):
|
||||||
os.makedirs(os.path.dirname(video_save_path))
|
os.makedirs(os.path.dirname(video_save_path))
|
||||||
if os.path.exists(video_save_path):
|
if os.path.exists(video_save_path):
|
||||||
return send_file(video_save_path)
|
return FileResponse(path=video_save_path)
|
||||||
shutil.copy(original_img_path, video_save_path)
|
shutil.copy(original_img_path, video_save_path)
|
||||||
return send_file(original_img_path)
|
return FileResponse(path=video_save_path)
|
||||||
|
|
||||||
|
|
||||||
@rs_api.route('/api/rs/audio/<path:savePath>', methods=["GET", 'POST'])
|
@rs_api.api_route('/audio', methods=["GET", 'POST'])
|
||||||
def get_audio(savePath):
|
def get_audio(src: str):
|
||||||
my_wxid = get_conf(g.caf, g.at, "last")
|
"""
|
||||||
|
获取语音
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
savePath = src.replace("audio\\", "")
|
||||||
|
if not savePath:
|
||||||
|
return ReJson(1002)
|
||||||
|
my_wxid = gc.get_conf(gc.at, "last")
|
||||||
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
||||||
db_config = get_conf(g.caf, my_wxid, "db_config")
|
db_config = gc.get_conf(my_wxid, "db_config")
|
||||||
|
|
||||||
savePath = os.path.join(g.work_path, my_wxid, "audio", savePath) # 这个是从url中获取的
|
savePath = os.path.join(gc.work_path, my_wxid, "audio", savePath) # 这个是从url中获取的
|
||||||
if os.path.exists(savePath):
|
if os.path.exists(savePath):
|
||||||
return send_file(savePath)
|
assert isinstance(savePath, str)
|
||||||
|
return FileResponse(path=savePath, media_type='audio/mpeg')
|
||||||
|
|
||||||
MsgSvrID = savePath.split("_")[-1].replace(".wav", "")
|
MsgSvrID = savePath.split("_")[-1].replace(".wav", "")
|
||||||
if not savePath:
|
if not savePath:
|
||||||
@ -282,21 +285,25 @@ def get_audio(savePath):
|
|||||||
return ReJson(1001, body="wave_data is required")
|
return ReJson(1001, body="wave_data is required")
|
||||||
|
|
||||||
if os.path.exists(savePath):
|
if os.path.exists(savePath):
|
||||||
return send_file(savePath)
|
assert isinstance(savePath, str)
|
||||||
|
return FileResponse(path=savePath, media_type='audio/mpeg')
|
||||||
else:
|
else:
|
||||||
return ReJson(4004, body=savePath)
|
return ReJson(4004, body=savePath)
|
||||||
|
|
||||||
|
|
||||||
@rs_api.route('/api/rs/file_info', methods=["GET", 'POST'])
|
class FileInfoRequest(BaseModel):
|
||||||
def get_file_info():
|
file_path: str
|
||||||
file_path = request.args.get("file_path")
|
|
||||||
file_path = request.json.get("file_path", file_path)
|
|
||||||
|
@rs_api.api_route('/file_info', methods=["GET", 'POST'])
|
||||||
|
def get_file_info(request: FileInfoRequest):
|
||||||
|
file_path = request.file_path
|
||||||
if not file_path:
|
if not file_path:
|
||||||
return ReJson(1002)
|
return ReJson(1002)
|
||||||
|
|
||||||
my_wxid = get_conf(g.caf, g.at, "last")
|
my_wxid = gc.get_conf(gc.at, "last")
|
||||||
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
||||||
wx_path = get_conf(g.caf, my_wxid, "wx_path")
|
wx_path = gc.get_conf(my_wxid, "wx_path")
|
||||||
|
|
||||||
all_file_path = os.path.join(wx_path, file_path)
|
all_file_path = os.path.join(wx_path, file_path)
|
||||||
if not os.path.exists(all_file_path):
|
if not os.path.exists(all_file_path):
|
||||||
@ -306,34 +313,59 @@ def get_file_info():
|
|||||||
return ReJson(0, {"file_name": file_name, "file_size": str(file_size)})
|
return ReJson(0, {"file_name": file_name, "file_size": str(file_size)})
|
||||||
|
|
||||||
|
|
||||||
@rs_api.route('/api/rs/file/<path:filePath>', methods=["GET", 'POST'])
|
@rs_api.get('/file')
|
||||||
def get_file(filePath):
|
def get_file(src: str):
|
||||||
my_wxid = get_conf(g.caf, g.at, "last")
|
"""
|
||||||
|
获取文件
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
file_path = src
|
||||||
|
if not file_path:
|
||||||
|
return ReJson(1002)
|
||||||
|
my_wxid = gc.get_conf(gc.at, "last")
|
||||||
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
||||||
wx_path = get_conf(g.caf, my_wxid, "wx_path")
|
wx_path = gc.get_conf(my_wxid, "wx_path")
|
||||||
|
|
||||||
all_file_path = os.path.join(wx_path, filePath)
|
all_file_path = os.path.join(wx_path, file_path)
|
||||||
if not os.path.exists(all_file_path):
|
if not os.path.exists(all_file_path):
|
||||||
return ReJson(5002)
|
return ReJson(5002)
|
||||||
return send_file(all_file_path)
|
|
||||||
|
def file_iterator(file_path, chunk_size=8192):
|
||||||
|
with open(file_path, "rb") as f:
|
||||||
|
while True:
|
||||||
|
chunk = f.read(chunk_size)
|
||||||
|
if not chunk:
|
||||||
|
break
|
||||||
|
yield chunk
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Content-Disposition": f'attachment; filename*=UTF-8\'\'{quote(os.path.basename(all_file_path))}',
|
||||||
|
}
|
||||||
|
return StreamingResponse(file_iterator(all_file_path), media_type="application/octet-stream", headers=headers)
|
||||||
|
|
||||||
|
|
||||||
# end 以上为聊天记录相关api *********************************************************************************************
|
# end 以上为聊天记录相关api *********************************************************************************************
|
||||||
|
|
||||||
# start 导出聊天记录 *****************************************************************************************************
|
# start 导出聊天记录 *****************************************************************************************************
|
||||||
|
class ExportEndbRequest(BaseModel):
|
||||||
|
wx_path: str = ""
|
||||||
|
outpath: str = ""
|
||||||
|
key: str = ""
|
||||||
|
|
||||||
@rs_api.route('/api/rs/export_endb', methods=["GET", 'POST'])
|
|
||||||
def get_export_endb():
|
@rs_api.api_route('/export_endb', methods=["GET", 'POST'])
|
||||||
|
def get_export_endb(request: ExportEndbRequest):
|
||||||
"""
|
"""
|
||||||
导出加密数据库
|
导出加密数据库
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
my_wxid = get_conf(g.caf, g.at, "last")
|
|
||||||
|
my_wxid = gc.get_conf(gc.at, "last")
|
||||||
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
||||||
|
|
||||||
wx_path = request.json.get("wx_path", "")
|
wx_path = request.wx_path
|
||||||
if not wx_path:
|
if not wx_path:
|
||||||
wx_path = get_conf(g.caf, my_wxid, "wx_path")
|
wx_path = gc.get_conf(my_wxid, "wx_path")
|
||||||
if not os.path.exists(wx_path if wx_path else ""):
|
if not os.path.exists(wx_path if wx_path else ""):
|
||||||
return ReJson(1002, body=f"wx_path is required: {wx_path}")
|
return ReJson(1002, body=f"wx_path is required: {wx_path}")
|
||||||
|
|
||||||
@ -342,7 +374,7 @@ def get_export_endb():
|
|||||||
if not code:
|
if not code:
|
||||||
return ReJson(2001, body=wxdbpaths)
|
return ReJson(2001, body=wxdbpaths)
|
||||||
|
|
||||||
outpath = os.path.join(g.work_path, "export", my_wxid, "endb")
|
outpath = os.path.join(gc.work_path, "export", my_wxid, "endb")
|
||||||
if not os.path.exists(outpath):
|
if not os.path.exists(outpath):
|
||||||
os.makedirs(outpath)
|
os.makedirs(outpath)
|
||||||
|
|
||||||
@ -354,25 +386,28 @@ def get_export_endb():
|
|||||||
return ReJson(0, body=outpath)
|
return ReJson(0, body=outpath)
|
||||||
|
|
||||||
|
|
||||||
@rs_api.route('/api/rs/export_dedb', methods=["GET", "POST"])
|
class ExportDedbRequest(BaseModel):
|
||||||
def get_export_dedb():
|
wx_path: str = ""
|
||||||
|
outpath: str = ""
|
||||||
|
key: str = ""
|
||||||
|
|
||||||
|
|
||||||
|
@rs_api.api_route('/export_dedb', methods=["GET", "POST"])
|
||||||
|
def get_export_dedb(request: ExportDedbRequest):
|
||||||
"""
|
"""
|
||||||
导出解密数据库
|
导出解密数据库
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if request.method not in ["GET", "POST"]:
|
key = request.key
|
||||||
return ReJson(1003, msg="Unsupported method")
|
wx_path = request.wx_path
|
||||||
rq_data = request.json if request.method == "POST" else request.args
|
|
||||||
key = rq_data.get("key", "")
|
|
||||||
wx_path = rq_data.get("wx_path", "")
|
|
||||||
|
|
||||||
my_wxid = get_conf(g.caf, g.at, "last")
|
my_wxid = gc.get_conf(gc.at, "last")
|
||||||
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
||||||
|
|
||||||
if not key:
|
if not key:
|
||||||
key = get_conf(g.caf, my_wxid, "key")
|
key = gc.get_conf(my_wxid, "key")
|
||||||
if not wx_path:
|
if not wx_path:
|
||||||
wx_path = get_conf(g.caf, my_wxid, "wx_path")
|
wx_path = gc.get_conf(my_wxid, "wx_path")
|
||||||
|
|
||||||
if not key:
|
if not key:
|
||||||
return ReJson(1002, body=f"key is required: {key}")
|
return ReJson(1002, body=f"key is required: {key}")
|
||||||
@ -381,7 +416,7 @@ def get_export_dedb():
|
|||||||
if not os.path.exists(wx_path):
|
if not os.path.exists(wx_path):
|
||||||
return ReJson(1001, body=f"wx_path not exists: {wx_path}")
|
return ReJson(1001, body=f"wx_path not exists: {wx_path}")
|
||||||
|
|
||||||
outpath = os.path.join(g.work_path, "export", my_wxid, "dedb")
|
outpath = os.path.join(gc.work_path, "export", my_wxid, "dedb")
|
||||||
if not os.path.exists(outpath):
|
if not os.path.exists(outpath):
|
||||||
os.makedirs(outpath)
|
os.makedirs(outpath)
|
||||||
assert isinstance(outpath, str)
|
assert isinstance(outpath, str)
|
||||||
@ -393,17 +428,22 @@ def get_export_dedb():
|
|||||||
return ReJson(2001, body=merge_save_path)
|
return ReJson(2001, body=merge_save_path)
|
||||||
|
|
||||||
|
|
||||||
@rs_api.route('/api/rs/export_csv', methods=["GET", 'POST'])
|
class ExportCsvRequest(BaseModel):
|
||||||
def get_export_csv():
|
wxid: str
|
||||||
|
|
||||||
|
|
||||||
|
@rs_api.api_route('/export_csv', methods=["GET", 'POST'])
|
||||||
|
def get_export_csv(request: ExportCsvRequest):
|
||||||
"""
|
"""
|
||||||
导出csv
|
导出csv
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
my_wxid = get_conf(g.caf, g.at, "last")
|
|
||||||
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
|
||||||
db_config = get_conf(g.caf, my_wxid, "db_config")
|
|
||||||
|
|
||||||
wxid = request.json.get("wxid")
|
my_wxid = gc.get_conf(gc.at, "last")
|
||||||
|
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
||||||
|
db_config = gc.get_conf(my_wxid, "db_config")
|
||||||
|
|
||||||
|
wxid = request.wxid
|
||||||
# st_ed_time = request.json.get("datetime", [0, 0])
|
# st_ed_time = request.json.get("datetime", [0, 0])
|
||||||
if not wxid:
|
if not wxid:
|
||||||
return ReJson(1002, body=f"username is required: {wxid}")
|
return ReJson(1002, body=f"username is required: {wxid}")
|
||||||
@ -413,7 +453,7 @@ def get_export_csv():
|
|||||||
# if not isinstance(start, int) or not isinstance(end, int) or start >= end:
|
# if not isinstance(start, int) or not isinstance(end, int) or start >= end:
|
||||||
# return ReJson(1002, body=f"datetime is required: {st_ed_time}")
|
# return ReJson(1002, body=f"datetime is required: {st_ed_time}")
|
||||||
|
|
||||||
outpath = os.path.join(g.work_path, "export", my_wxid, "csv", wxid)
|
outpath = os.path.join(gc.work_path, "export", my_wxid, "csv", wxid)
|
||||||
if not os.path.exists(outpath):
|
if not os.path.exists(outpath):
|
||||||
os.makedirs(outpath)
|
os.makedirs(outpath)
|
||||||
|
|
||||||
@ -424,21 +464,26 @@ def get_export_csv():
|
|||||||
return ReJson(2001, body=ret)
|
return ReJson(2001, body=ret)
|
||||||
|
|
||||||
|
|
||||||
@rs_api.route('/api/rs/export_json', methods=["GET", 'POST'])
|
class ExportJsonRequest(BaseModel):
|
||||||
def get_export_json():
|
wxid: str
|
||||||
|
|
||||||
|
|
||||||
|
@rs_api.api_route('/export_json', methods=["GET", 'POST'])
|
||||||
|
def get_export_json(request: ExportJsonRequest):
|
||||||
"""
|
"""
|
||||||
导出json
|
导出json
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
my_wxid = get_conf(g.caf, g.at, "last")
|
|
||||||
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
|
||||||
db_config = get_conf(g.caf, my_wxid, "db_config")
|
|
||||||
|
|
||||||
wxid = request.json.get("wxid")
|
my_wxid = gc.get_conf(gc.at, "last")
|
||||||
|
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
||||||
|
db_config = gc.get_conf(my_wxid, "db_config")
|
||||||
|
|
||||||
|
wxid = request.wxid
|
||||||
if not wxid:
|
if not wxid:
|
||||||
return ReJson(1002, body=f"username is required: {wxid}")
|
return ReJson(1002, body=f"username is required: {wxid}")
|
||||||
|
|
||||||
outpath = os.path.join(g.work_path, "export", my_wxid, "json", wxid)
|
outpath = os.path.join(gc.work_path, "export", my_wxid, "json", wxid)
|
||||||
if not os.path.exists(outpath):
|
if not os.path.exists(outpath):
|
||||||
os.makedirs(outpath)
|
os.makedirs(outpath)
|
||||||
|
|
||||||
@ -449,21 +494,26 @@ def get_export_json():
|
|||||||
return ReJson(2001, body=ret)
|
return ReJson(2001, body=ret)
|
||||||
|
|
||||||
|
|
||||||
@rs_api.route('/api/rs/export_html', methods=["GET", 'POST'])
|
class ExportHtmlRequest(BaseModel):
|
||||||
def get_export_html():
|
wxid: str
|
||||||
|
|
||||||
|
|
||||||
|
@rs_api.api_route('/export_html', methods=["GET", 'POST'])
|
||||||
|
def get_export_html(request: ExportHtmlRequest):
|
||||||
"""
|
"""
|
||||||
导出json
|
导出json
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
my_wxid = get_conf(g.caf, g.at, "last")
|
|
||||||
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
|
||||||
db_config = get_conf(g.caf, my_wxid, "db_config")
|
|
||||||
|
|
||||||
wxid = request.json.get("wxid")
|
my_wxid = gc.get_conf(gc.at, "last")
|
||||||
|
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
||||||
|
db_config = gc.get_conf(my_wxid, "db_config")
|
||||||
|
|
||||||
|
wxid = request.wxid
|
||||||
if not wxid:
|
if not wxid:
|
||||||
return ReJson(1002, body=f"username is required: {wxid}")
|
return ReJson(1002, body=f"username is required: {wxid}")
|
||||||
|
|
||||||
html_outpath = os.path.join(g.work_path, "export", my_wxid, "html")
|
html_outpath = os.path.join(gc.work_path, "export", my_wxid, "html")
|
||||||
if not os.path.exists(html_outpath):
|
if not os.path.exists(html_outpath):
|
||||||
os.makedirs(html_outpath)
|
os.makedirs(html_outpath)
|
||||||
assert isinstance(html_outpath, str)
|
assert isinstance(html_outpath, str)
|
||||||
@ -474,7 +524,6 @@ def get_export_html():
|
|||||||
web_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "ui", "web")
|
web_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "ui", "web")
|
||||||
shutil.copytree(web_path, outpath)
|
shutil.copytree(web_path, outpath)
|
||||||
|
|
||||||
|
|
||||||
code, ret = export_html(wxid, outpath, db_config, my_wxid=my_wxid)
|
code, ret = export_html(wxid, outpath, db_config, my_wxid=my_wxid)
|
||||||
|
|
||||||
if code:
|
if code:
|
||||||
@ -486,69 +535,75 @@ def get_export_html():
|
|||||||
# end 导出聊天记录 *******************************************************************************************************
|
# end 导出聊天记录 *******************************************************************************************************
|
||||||
|
|
||||||
# start 聊天记录分析api **************************************************************************************************
|
# start 聊天记录分析api **************************************************************************************************
|
||||||
|
class DateCountRequest(BaseModel):
|
||||||
|
wxid: str = ""
|
||||||
|
start_time: int = 0
|
||||||
|
end_time: int = 0
|
||||||
|
time_format: str = "%Y-%m-%d"
|
||||||
|
|
||||||
@rs_api.route('/api/rs/date_count', methods=["GET", 'POST'])
|
|
||||||
def get_date_count():
|
@rs_api.api_route('/date_count', methods=["GET", 'POST'])
|
||||||
|
def get_date_count(request: DateCountRequest):
|
||||||
"""
|
"""
|
||||||
获取日期统计
|
获取日期统计
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if request.method not in ["GET", "POST"]:
|
wxid = request.wxid
|
||||||
return ReJson(1003, msg="Unsupported method")
|
start_time = request.start_time
|
||||||
rq_data = request.json if request.method == "POST" else request.args
|
end_time = request.end_time
|
||||||
word = rq_data.get("wxid", "")
|
time_format = request.time_format
|
||||||
start_time = rq_data.get("start_time", 0)
|
|
||||||
end_time = rq_data.get("end_time", 0)
|
|
||||||
time_format = rq_data.get("time_format", "%Y-%m-%d")
|
|
||||||
|
|
||||||
my_wxid = get_conf(g.caf, g.at, "last")
|
my_wxid = gc.get_conf(gc.at, "last")
|
||||||
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
||||||
db_config = get_conf(g.caf, my_wxid, "db_config")
|
db_config = gc.get_conf(my_wxid, "db_config")
|
||||||
db = DBHandler(db_config, my_wxid=my_wxid)
|
db = DBHandler(db_config, my_wxid=my_wxid)
|
||||||
date_count = db.get_date_count(wxid=word, start_time=start_time, end_time=end_time, time_format=time_format)
|
date_count = db.get_date_count(wxid=wxid, start_time=start_time, end_time=end_time, time_format=time_format)
|
||||||
return ReJson(0, date_count)
|
return ReJson(0, date_count)
|
||||||
|
|
||||||
|
|
||||||
@rs_api.route('/api/rs/top_talker_count', methods=["GET", 'POST'])
|
class TopTalkerCountRequest(BaseModel):
|
||||||
def get_top_talker_count():
|
top: int = 10
|
||||||
|
start_time: int = 0
|
||||||
|
end_time: int = 0
|
||||||
|
|
||||||
|
|
||||||
|
@rs_api.api_route('/top_talker_count', methods=["GET", 'POST'])
|
||||||
|
def get_top_talker_count(request: TopTalkerCountRequest):
|
||||||
"""
|
"""
|
||||||
获取最多聊天的人
|
获取最多聊天的人
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if request.method not in ["GET", "POST"]:
|
top = request.top
|
||||||
return ReJson(1003, msg="Unsupported method")
|
start_time = request.start_time
|
||||||
rq_data = request.json if request.method == "POST" else request.args
|
end_time = request.end_time
|
||||||
top = rq_data.get("top", 10)
|
|
||||||
start_time = rq_data.get("start_time", 0)
|
|
||||||
end_time = rq_data.get("end_time", 0)
|
|
||||||
|
|
||||||
my_wxid = get_conf(g.caf, g.at, "last")
|
my_wxid = gc.get_conf(gc.at, "last")
|
||||||
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
||||||
db_config = get_conf(g.caf, my_wxid, "db_config")
|
db_config = gc.get_conf(my_wxid, "db_config")
|
||||||
date_count = DBHandler(db_config, my_wxid=my_wxid).get_top_talker_count(top=top, start_time=start_time,
|
date_count = DBHandler(db_config, my_wxid=my_wxid).get_top_talker_count(top=top, start_time=start_time,
|
||||||
end_time=end_time)
|
end_time=end_time)
|
||||||
return ReJson(0, date_count)
|
return ReJson(0, date_count)
|
||||||
|
|
||||||
|
|
||||||
@rs_api.route('/api/rs/wordcloud', methods=["GET", 'POST'])
|
class WordCloudRequest(BaseModel):
|
||||||
@error9999
|
target: str = "signature"
|
||||||
def wordcloud():
|
|
||||||
if request.method not in ["GET", "POST"]:
|
|
||||||
return ReJson(1003, msg="Unsupported method")
|
|
||||||
rq_data = request.json if request.method == "POST" else request.args
|
|
||||||
|
|
||||||
|
|
||||||
|
@rs_api.api_route('/wordcloud', methods=["GET", 'POST'])
|
||||||
|
@error9999
|
||||||
|
def get_wordcloud(request: WordCloudRequest):
|
||||||
try:
|
try:
|
||||||
import jieba
|
import jieba
|
||||||
except ImportError:
|
except ImportError:
|
||||||
return ReJson(9999, body="jieba is required")
|
return ReJson(9999, body="jieba is required")
|
||||||
|
|
||||||
target = rq_data.get("target", "")
|
target = request.target
|
||||||
if not target:
|
if not target:
|
||||||
return ReJson(1002, body="target is required")
|
return ReJson(1002, body="target is required")
|
||||||
|
|
||||||
my_wxid = get_conf(g.caf, g.at, "last")
|
my_wxid = gc.get_conf(gc.at, "last")
|
||||||
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
if not my_wxid: return ReJson(1001, body="my_wxid is required")
|
||||||
db_config = get_conf(g.caf, my_wxid, "db_config")
|
db_config = gc.get_conf(my_wxid, "db_config")
|
||||||
db = DBHandler(db_config, my_wxid=my_wxid)
|
db = DBHandler(db_config, my_wxid=my_wxid)
|
||||||
|
|
||||||
if target == "signature":
|
if target == "signature":
|
||||||
@ -583,7 +638,7 @@ def wordcloud():
|
|||||||
# end 聊天记录分析api ****************************************************************************************************
|
# end 聊天记录分析api ****************************************************************************************************
|
||||||
|
|
||||||
# 关于、帮助、设置 *******************************************************************************************************
|
# 关于、帮助、设置 *******************************************************************************************************
|
||||||
@rs_api.route('/api/rs/check_update', methods=["GET", 'POST'])
|
@rs_api.api_route('/check_update', methods=["GET", 'POST'])
|
||||||
@error9999
|
@error9999
|
||||||
def check_update():
|
def check_update():
|
||||||
"""
|
"""
|
||||||
@ -609,7 +664,7 @@ def check_update():
|
|||||||
return ReJson(9999, msg=str(e))
|
return ReJson(9999, msg=str(e))
|
||||||
|
|
||||||
|
|
||||||
@rs_api.route('/api/rs/version', methods=["GET", 'POST'])
|
@rs_api.api_route('/version', methods=["GET", "POST"])
|
||||||
@error9999
|
@error9999
|
||||||
def version():
|
def version():
|
||||||
"""
|
"""
|
||||||
@ -619,7 +674,7 @@ def version():
|
|||||||
return ReJson(0, pywxdump.__version__)
|
return ReJson(0, pywxdump.__version__)
|
||||||
|
|
||||||
|
|
||||||
@rs_api.route('/api/rs/get_readme', methods=["GET", 'POST'])
|
@rs_api.api_route('/get_readme', methods=["GET", 'POST'])
|
||||||
@error9999
|
@error9999
|
||||||
def get_readme():
|
def get_readme():
|
||||||
"""
|
"""
|
||||||
@ -638,9 +693,3 @@ def get_readme():
|
|||||||
|
|
||||||
|
|
||||||
# END 关于、帮助、设置 ***************************************************************************************************
|
# END 关于、帮助、设置 ***************************************************************************************************
|
||||||
|
|
||||||
|
|
||||||
@rs_api.route('/')
|
|
||||||
@error9999
|
|
||||||
def index():
|
|
||||||
return render_template('index.html')
|
|
||||||
|
@ -16,8 +16,122 @@ from .rjson import ReJson
|
|||||||
from functools import wraps
|
from functools import wraps
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
rs_loger = logging.getLogger("rs_api")
|
server_loger = logging.getLogger("server")
|
||||||
ls_loger = logging.getLogger("ls_api")
|
rs_loger = server_loger
|
||||||
|
ls_loger = server_loger
|
||||||
|
|
||||||
|
|
||||||
|
class ConfData(object):
|
||||||
|
_instances = None
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
if cls._instances:
|
||||||
|
return cls._instances
|
||||||
|
cls._instances = object.__new__(cls)
|
||||||
|
return cls._instances
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._work_path = None
|
||||||
|
self.conf_file = None
|
||||||
|
self.auto_setting = None
|
||||||
|
|
||||||
|
self.is_init = False
|
||||||
|
|
||||||
|
self.conf = {}
|
||||||
|
|
||||||
|
self.init()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cf(self):
|
||||||
|
if not self.is_init:
|
||||||
|
self.init()
|
||||||
|
return self.conf_file
|
||||||
|
|
||||||
|
@property
|
||||||
|
def work_path(self):
|
||||||
|
if not self.is_init:
|
||||||
|
self.init()
|
||||||
|
return self._work_path
|
||||||
|
|
||||||
|
@property
|
||||||
|
def at(self):
|
||||||
|
if not self.is_init:
|
||||||
|
self.init()
|
||||||
|
return self.auto_setting
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
self.is_init = False
|
||||||
|
|
||||||
|
work_path = os.getenv("PYWXDUMP_WORK_PATH")
|
||||||
|
conf_file = os.getenv("PYWXDUMP_CONF_FILE")
|
||||||
|
auto_setting = os.getenv("PYWXDUMP_AUTO_SETTING")
|
||||||
|
|
||||||
|
if work_path is None or conf_file is None or auto_setting is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
self._work_path = work_path
|
||||||
|
self.conf_file = conf_file
|
||||||
|
self.auto_setting = auto_setting
|
||||||
|
if not os.path.exists(self.conf_file):
|
||||||
|
self.set_conf(self.auto_setting, "last", "")
|
||||||
|
self.is_init = True
|
||||||
|
self.read_conf()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def read_conf(self):
|
||||||
|
if not self.is_init:
|
||||||
|
self.init()
|
||||||
|
try:
|
||||||
|
with open(self.conf_file, 'r') as f:
|
||||||
|
conf = json.load(f)
|
||||||
|
self.conf = conf
|
||||||
|
return True
|
||||||
|
except FileNotFoundError:
|
||||||
|
logging.error(f"Session file not found: {self.conf_file}")
|
||||||
|
return False
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
logging.error(f"Error decoding JSON file: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def write_conf(self):
|
||||||
|
if not self.is_init:
|
||||||
|
self.init()
|
||||||
|
try:
|
||||||
|
with open(self.conf_file, 'w') as f:
|
||||||
|
json.dump(self.conf, f, indent=4, ensure_ascii=False)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error writing to file: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def set_conf(self, wxid, arg, value):
|
||||||
|
if not self.is_init:
|
||||||
|
self.init()
|
||||||
|
if wxid not in self.conf:
|
||||||
|
self.conf[wxid] = {}
|
||||||
|
if not isinstance(self.conf[wxid], dict):
|
||||||
|
self.conf[wxid] = {}
|
||||||
|
self.conf[wxid][arg] = value
|
||||||
|
self.write_conf()
|
||||||
|
|
||||||
|
def get_conf(self, wxid, arg):
|
||||||
|
if not self.is_init:
|
||||||
|
self.init()
|
||||||
|
return self.conf.get(wxid, {}).get(arg, None)
|
||||||
|
|
||||||
|
def get_local_wxids(self):
|
||||||
|
if not self.is_init:
|
||||||
|
self.init()
|
||||||
|
return list(self.conf.keys())
|
||||||
|
|
||||||
|
def get_db_config(self):
|
||||||
|
if not self.is_init:
|
||||||
|
self.init()
|
||||||
|
my_wxid = self.get_conf(self.at, "last")
|
||||||
|
return self.get_conf(my_wxid, "db_config")
|
||||||
|
|
||||||
|
|
||||||
|
gc: ConfData = ConfData()
|
||||||
|
|
||||||
|
|
||||||
def get_conf_local_wxid(conf_file):
|
def get_conf_local_wxid(conf_file):
|
||||||
@ -83,6 +197,16 @@ def set_conf(conf_file, wxid, arg, value):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def is_port_in_use(_host, _port):
|
||||||
|
import socket
|
||||||
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||||
|
try:
|
||||||
|
s.bind((_host, _port))
|
||||||
|
except socket.error:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def validate_title(title):
|
def validate_title(title):
|
||||||
"""
|
"""
|
||||||
校验文件名是否合法
|
校验文件名是否合法
|
||||||
@ -106,6 +230,20 @@ def error9999(func):
|
|||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def asyncError9999(func):
|
||||||
|
@wraps(func)
|
||||||
|
async def wrapper(*args, **kwargs):
|
||||||
|
try:
|
||||||
|
return await func(*args, **kwargs)
|
||||||
|
except Exception as e:
|
||||||
|
traceback_data = traceback.format_exc()
|
||||||
|
rdata = f"{traceback_data}"
|
||||||
|
# logging.error(rdata)
|
||||||
|
return ReJson(9999, body=f"{str(e)}\n{rdata}", error=str(e))
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
def gen_base64(path):
|
def gen_base64(path):
|
||||||
# 获取文件名后缀
|
# 获取文件名后缀
|
||||||
extension = os.path.splitext(path)[1]
|
extension = os.path.splitext(path)[1]
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
# Date: 2023/10/14
|
# Date: 2023/10/14
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
import argparse
|
import argparse
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
|
||||||
|
|
||||||
from pywxdump import *
|
from pywxdump import *
|
||||||
import pywxdump
|
import pywxdump
|
||||||
@ -289,7 +289,7 @@ class MainShowChatRecords(BaseSubMainClass):
|
|||||||
print("[-] 输入数据库路径不存在")
|
print("[-] 输入数据库路径不存在")
|
||||||
return
|
return
|
||||||
|
|
||||||
start_falsk(merge_path=merge_path, wx_path=args.wx_path, key="", my_wxid=args.my_wxid, online=online)
|
start_server(merge_path=merge_path, wx_path=args.wx_path, key="", my_wxid=args.my_wxid, online=online)
|
||||||
|
|
||||||
|
|
||||||
class MainExportChatRecords(BaseSubMainClass):
|
class MainExportChatRecords(BaseSubMainClass):
|
||||||
@ -327,7 +327,7 @@ class MainUi(BaseSubMainClass):
|
|||||||
parser.add_argument("-p", '--port', metavar="", type=int, help="(可选)端口号", default=5000)
|
parser.add_argument("-p", '--port', metavar="", type=int, help="(可选)端口号", default=5000)
|
||||||
parser.add_argument("--online", help="(可选)是否在线查看(局域网查看)", default=False, action='store_true')
|
parser.add_argument("--online", help="(可选)是否在线查看(局域网查看)", default=False, action='store_true')
|
||||||
parser.add_argument("--debug", help="(可选)是否开启debug模式", default=False, action='store_true')
|
parser.add_argument("--debug", help="(可选)是否开启debug模式", default=False, action='store_true')
|
||||||
parser.add_argument("--noOpenBrowser", dest='isOpenBrowser', action='store_false', default=True,
|
parser.add_argument("--noOpenBrowser", dest='isOpenBrowser', default=True, action='store_false',
|
||||||
help="(可选)用于禁用自动打开浏览器")
|
help="(可选)用于禁用自动打开浏览器")
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
@ -339,7 +339,7 @@ class MainUi(BaseSubMainClass):
|
|||||||
debug = args.debug
|
debug = args.debug
|
||||||
isopenBrowser = args.isOpenBrowser
|
isopenBrowser = args.isOpenBrowser
|
||||||
|
|
||||||
start_falsk(port=port, online=online, debug=debug, isopenBrowser=isopenBrowser)
|
start_server(port=port, online=online, debug=debug, isopenBrowser=isopenBrowser)
|
||||||
|
|
||||||
|
|
||||||
class MainApi(BaseSubMainClass):
|
class MainApi(BaseSubMainClass):
|
||||||
@ -360,7 +360,7 @@ class MainApi(BaseSubMainClass):
|
|||||||
port = args.port
|
port = args.port
|
||||||
debug = args.debug
|
debug = args.debug
|
||||||
|
|
||||||
start_falsk(port=port, online=online, debug=debug, isopenBrowser=False)
|
start_server(port=port, online=online, debug=debug, isopenBrowser=False)
|
||||||
|
|
||||||
|
|
||||||
def console_run():
|
def console_run():
|
||||||
|
@ -242,7 +242,7 @@ class MsgHandler(DatabaseBase):
|
|||||||
voicelength = int(voicelength) / 1000
|
voicelength = int(voicelength) / 1000
|
||||||
voicelength = f"{voicelength:.2f}"
|
voicelength = f"{voicelength:.2f}"
|
||||||
msg = f"语音时长:{voicelength}秒\n翻译结果:{transtext}" if transtext else f"语音时长:{voicelength}秒"
|
msg = f"语音时长:{voicelength}秒\n翻译结果:{transtext}" if transtext else f"语音时长:{voicelength}秒"
|
||||||
src = os.path.join("audio", f"{StrTalker}",
|
src = os.path.join(f"{StrTalker}",
|
||||||
f"{CreateTime.replace(':', '-').replace(' ', '_')}_{IsSender}_{MsgSvrID}.wav")
|
f"{CreateTime.replace(':', '-').replace(' ', '_')}_{IsSender}_{MsgSvrID}.wav")
|
||||||
elif type_id == (43, 0): # 视频
|
elif type_id == (43, 0): # 视频
|
||||||
DictExtra = get_BytesExtra(BytesExtra)
|
DictExtra = get_BytesExtra(BytesExtra)
|
||||||
|
@ -96,8 +96,11 @@ class DatabaseBase(DatabaseSingletonBase):
|
|||||||
def __get_existed_tables(self):
|
def __get_existed_tables(self):
|
||||||
sql = "SELECT tbl_name FROM sqlite_master WHERE type = 'table' and tbl_name!='sqlite_sequence';"
|
sql = "SELECT tbl_name FROM sqlite_master WHERE type = 'table' and tbl_name!='sqlite_sequence';"
|
||||||
existing_tables = self.execute(sql)
|
existing_tables = self.execute(sql)
|
||||||
self.existed_tables = [row[0].lower() for row in existing_tables]
|
if existing_tables:
|
||||||
return self.existed_tables
|
self.existed_tables = [row[0].lower() for row in existing_tables]
|
||||||
|
return self.existed_tables
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
def tables_exist(self, required_tables: str or list):
|
def tables_exist(self, required_tables: str or list):
|
||||||
"""
|
"""
|
||||||
|
@ -252,18 +252,19 @@ def xml2dict(xml_string):
|
|||||||
return parse_xml(root)
|
return parse_xml(root)
|
||||||
|
|
||||||
|
|
||||||
def download_file(url, save_path=None):
|
def download_file(url, save_path=None, proxies=None):
|
||||||
"""
|
"""
|
||||||
下载文件
|
下载文件
|
||||||
:param url: 文件下载地址
|
:param url: 文件下载地址
|
||||||
:param save_path: 保存路径
|
:param save_path: 保存路径
|
||||||
|
:param proxies: requests 代理
|
||||||
:return: 保存路径
|
:return: 保存路径
|
||||||
"""
|
"""
|
||||||
headers = {
|
headers = {
|
||||||
"User-Agent": "Mozilla/5.0 (Linux; Android 10; Redmi K40 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Mobile Safari/537.36"
|
"User-Agent": "Mozilla/5.0 (Linux; Android 10; Redmi K40 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Mobile Safari/537.36"
|
||||||
|
|
||||||
}
|
}
|
||||||
r = requests.get(url, headers=headers)
|
r = requests.get(url, headers=headers, proxies=proxies)
|
||||||
if r.status_code != 200:
|
if r.status_code != 200:
|
||||||
return None
|
return None
|
||||||
data = r.content
|
data = r.content
|
||||||
|
@ -1,143 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-#
|
|
||||||
# -------------------------------------------------------------------------------
|
|
||||||
# Name: server.py
|
|
||||||
# Description:
|
|
||||||
# Author: xaoyaoo
|
|
||||||
# Date: 2024/01/04
|
|
||||||
# -------------------------------------------------------------------------------
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
import logging
|
|
||||||
|
|
||||||
server_loger = logging.getLogger("server")
|
|
||||||
|
|
||||||
|
|
||||||
def is_port_in_use(_host, _port):
|
|
||||||
import socket
|
|
||||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
||||||
try:
|
|
||||||
s.bind((_host, _port))
|
|
||||||
except socket.error:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def start_falsk(merge_path="", wx_path="", key="", my_wxid="", port=5000, online=False, debug=False,
|
|
||||||
isopenBrowser=True, loger_handler=None):
|
|
||||||
"""
|
|
||||||
启动flask
|
|
||||||
:param merge_path: 合并后的数据库路径
|
|
||||||
:param wx_path: 微信文件夹的路径(用于显示图片)
|
|
||||||
:param key: 密钥
|
|
||||||
:param my_wxid: 微信账号(本人微信id)
|
|
||||||
:param port: 端口号
|
|
||||||
:param online: 是否在线查看(局域网查看)
|
|
||||||
:param debug: 是否开启debug模式
|
|
||||||
:param isopenBrowser: 是否自动打开浏览器
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
work_path = os.path.join(os.getcwd(), "wxdump_work") # 临时文件夹,用于存放图片等
|
|
||||||
if not os.path.exists(work_path):
|
|
||||||
os.makedirs(work_path)
|
|
||||||
server_loger.info(f"[+] 创建临时文件夹:{work_path}")
|
|
||||||
print(f"[+] 创建临时文件夹:{work_path}")
|
|
||||||
|
|
||||||
conf_auto_file = os.path.join(work_path, "conf_auto.json") # 用于存放各种基础信息
|
|
||||||
at = "auto_setting"
|
|
||||||
|
|
||||||
from flask import Flask, g
|
|
||||||
from flask_cors import CORS
|
|
||||||
from pywxdump.api import rs_api, ls_api, get_conf, set_conf
|
|
||||||
|
|
||||||
# 检查端口是否被占用
|
|
||||||
if online:
|
|
||||||
host = '0.0.0.0'
|
|
||||||
else:
|
|
||||||
host = "127.0.0.1"
|
|
||||||
|
|
||||||
app = Flask(__name__, template_folder='./ui/web', static_folder='./ui/web/assets/', static_url_path='/assets/')
|
|
||||||
with app.app_context():
|
|
||||||
# 设置超时时间为 1000 秒
|
|
||||||
app.config['TIMEOUT'] = 1000
|
|
||||||
app.secret_key = 'secret_key'
|
|
||||||
|
|
||||||
app.logger.setLevel(logging.WARNING)
|
|
||||||
if loger_handler:
|
|
||||||
app.logger.addHandler(loger_handler)
|
|
||||||
# 获取 Werkzeug 的日志记录器
|
|
||||||
werkzeug_logger = logging.getLogger('werkzeug')
|
|
||||||
# 将自定义格式器应用到 Werkzeug 的日志记录器
|
|
||||||
werkzeug_logger.addHandler(loger_handler)
|
|
||||||
werkzeug_logger.setLevel(logging.DEBUG)
|
|
||||||
|
|
||||||
CORS(app, resources={r"/*": {"origins": "*"}}, supports_credentials=True) # 允许所有域名跨域
|
|
||||||
|
|
||||||
@app.after_request # 请求后的处理 用于解决部分用户浏览器不支持flask以及vue的js文件返回问题
|
|
||||||
def changeHeader(response):
|
|
||||||
disposition = response.get_wsgi_headers('environ').get(
|
|
||||||
'Content-Disposition') or '' # 获取返回头文件名描述,如'inline; filename=index.562b9b5a.js'
|
|
||||||
if disposition.rfind('.js') == len(disposition) - 3:
|
|
||||||
response.mimetype = 'application/javascript'
|
|
||||||
return response
|
|
||||||
|
|
||||||
@app.before_request
|
|
||||||
def before_request():
|
|
||||||
g.work_path = work_path # 临时文件夹,用于存放图片等-新版本
|
|
||||||
g.caf = conf_auto_file # 用于存放各种基础信息-新版本
|
|
||||||
g.at = at # 用于默认设置-新版本
|
|
||||||
|
|
||||||
if merge_path:
|
|
||||||
set_conf(conf_auto_file, at, "merge_path", merge_path)
|
|
||||||
db_config = {
|
|
||||||
"key": "merge_all",
|
|
||||||
"type": "sqlite",
|
|
||||||
"path": "D:\\_code\\py_code\\pywxdumpProject\\z_test\\wxdump_work\\wxid_zh12s67kxsqs22\\merge_all.db"
|
|
||||||
}
|
|
||||||
set_conf(conf_auto_file, at, "db_config", db_config)
|
|
||||||
if wx_path: set_conf(conf_auto_file, at, "wx_path", wx_path)
|
|
||||||
if key: set_conf(conf_auto_file, at, "key", key)
|
|
||||||
if my_wxid: set_conf(conf_auto_file, at, "my_wxid", my_wxid)
|
|
||||||
if not os.path.exists(conf_auto_file):
|
|
||||||
set_conf(conf_auto_file, at, "last", my_wxid)
|
|
||||||
|
|
||||||
app.register_blueprint(rs_api)
|
|
||||||
app.register_blueprint(ls_api)
|
|
||||||
|
|
||||||
if isopenBrowser:
|
|
||||||
try:
|
|
||||||
# 自动打开浏览器
|
|
||||||
url = f"http://127.0.0.1:{port}/"
|
|
||||||
# 根据操作系统使用不同的命令打开默认浏览器
|
|
||||||
if sys.platform.startswith('darwin'): # macOS
|
|
||||||
subprocess.call(['open', url])
|
|
||||||
elif sys.platform.startswith('win'): # Windows
|
|
||||||
subprocess.call(['start', url], shell=True)
|
|
||||||
elif sys.platform.startswith('linux'): # Linux
|
|
||||||
subprocess.call(['xdg-open', url])
|
|
||||||
else:
|
|
||||||
server_loger.error(f"Unsupported platform, can't open browser automatically.", exc_info=True)
|
|
||||||
print("Unsupported platform, can't open browser automatically.")
|
|
||||||
except Exception as e:
|
|
||||||
server_loger.error(f"自动打开浏览器失败:{e}", exc_info=True)
|
|
||||||
|
|
||||||
if is_port_in_use(host, port):
|
|
||||||
server_loger.error(f"Port {port} is already in use. Choose a different port.")
|
|
||||||
print(f"Port {port} is already in use. Choose a different port.")
|
|
||||||
input("Press Enter to exit...")
|
|
||||||
else:
|
|
||||||
time.sleep(1)
|
|
||||||
server_loger.info(f"启动flask服务,host:port:{host}:{port}")
|
|
||||||
print("[+] 请使用浏览器访问 http://127.0.0.1:5000/ 查看聊天记录")
|
|
||||||
app.run(host=host, port=port, debug=debug, threaded=False)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
merge_path = r"****.db"
|
|
||||||
|
|
||||||
wx_path = r"****"
|
|
||||||
my_wxid = "****"
|
|
||||||
|
|
||||||
start_falsk(merge_path=merge_path, wx_path=wx_path, my_wxid=my_wxid,
|
|
||||||
port=5000, online=False, debug=False, isopenBrowser=False)
|
|
@ -15,3 +15,7 @@ lz4
|
|||||||
lxml
|
lxml
|
||||||
flask_cors
|
flask_cors
|
||||||
pandas
|
pandas
|
||||||
|
|
||||||
|
fastapi
|
||||||
|
uvicorn
|
||||||
|
dotenv
|
Loading…
Reference in New Issue
Block a user