Skip to content

Commit 10a3949

Browse files
authored
Merge pull request #25 from H3CoF6/feat/dynamic-next
fix (dynamic_next_id): get next action id dynamically
2 parents d8d2139 + 1df1b76 commit 10a3949

1 file changed

Lines changed: 61 additions & 31 deletions

File tree

src/wechat_decrypt_tool/key_service.py

Lines changed: 61 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
import hashlib
1515
import os
1616
import json
17+
import re
1718
import random
1819
import logging
20+
import asyncio
1921
import httpx
2022
from pathlib import Path
2123
from typing import Optional, List, Dict, Any
@@ -203,7 +205,56 @@ def get_db_key_workflow():
203205

204206
# 远程 API 配置
205207
REMOTE_URL = "https://view.free.c3o.re/dashboard"
206-
NEXT_ACTION_ID = "7c8f99280c70626ccf5960cc4a68f368197e15f8e9"
208+
BASE_URL = "https://view.free.c3o.re" # 用于拼接js
209+
210+
# NEXT_ACTION_ID = "7c8f99280c70626ccf5960cc4a68f368197e15f8e9" # 不可以硬编码
211+
212+
213+
async def fetch_js_and_scan(client: httpx.AsyncClient, js_path: str) -> Optional[str]:
214+
"""
215+
异步下载单个 JS 文件并匹配 Action ID
216+
"""
217+
full_url = f"{BASE_URL}{js_path}" if js_path.startswith("/") else js_path
218+
try:
219+
response = await client.get(full_url)
220+
if response.status_code != 200:
221+
return None
222+
223+
content = response.text
224+
225+
action_id_pattern = re.compile(r'createServerReference.*?["\']([a-f0-9]{42})["\'].*?["\']getUserConfigFromBytes["\']')
226+
227+
match = action_id_pattern.search(content)
228+
if match:
229+
found_id = match.group(1)
230+
return found_id
231+
232+
except Exception as e:
233+
logger.error(f"Error fetching {js_path}: {e}")
234+
return None
235+
236+
237+
async def _get_next_action_id_async() -> str:
238+
async with httpx.AsyncClient(timeout=10.0) as client:
239+
240+
resp = await client.get(REMOTE_URL)
241+
html = resp.text
242+
243+
js_file_pattern = re.compile(r'src="(/_next/static/chunks/[^"]+\.js)"')
244+
js_files = set(js_file_pattern.findall(html))
245+
246+
if not js_files:
247+
raise Exception("未找到任何 Next.js chunk 文件,可能页面结构已变动。")
248+
249+
tasks = [fetch_js_and_scan(client, js_path) for js_path in js_files]
250+
251+
results = await asyncio.gather(*tasks)
252+
253+
for res in results:
254+
if res:
255+
return res
256+
257+
raise Exception("遍历了所有 JS 文件,但未找到匹配的 createServerReference ID。")
207258

208259

209260
def get_wechat_internal_global_config(wx_dir: Path, file_name1) -> bytes:
@@ -221,41 +272,20 @@ def get_wechat_internal_global_config(wx_dir: Path, file_name1) -> bytes:
221272
return Path(target_path).read_bytes()
222273

223274

224-
# def get_local_config_sha3_224() -> bytes:
225-
# """
226-
# 不要在意,抽象的实现 哈哈哈
227-
# """
228-
# content = json.dumps({
229-
# "wxfile_dir": "C:\\Users\\17078\\xwechat_files",
230-
# "weixin_id_folder": "wxid_lnyf4hdo9csb12_f1c4",
231-
# "cache_dir": "C:\\Users\\17078\\Desktop\\wxDBHook\\test\\wx-dat\\wx-dat\\.cache",
232-
# "db_key": "",
233-
# "port": 8001
234-
# }, indent=4).encode("utf-8")
235-
#
236-
# # 计算 SHA3-224
237-
# digest = hashlib.sha3_224(content).digest()
238-
# return digest
239-
240-
# async def log_request(request):
241-
# print(f"--- Request Raw ---")
242-
# print(f"{request.method} {request.url} {request.extensions.get('http_version', b'HTTP/1.1').decode()}")
243-
# for name, value in request.headers.items():
244-
# print(f"{name}: {value}")
245-
#
246-
# print()
247-
#
248-
# body = request.read()
249-
# if body:
250-
# print(body.decode(errors='replace'))
251-
# print(f"-------------------\n")
252-
253275

254276
async def fetch_and_save_remote_keys(account: Optional[str] = None) -> Dict[str, Any]:
255277
account_dir = _resolve_account_dir(account)
256278
wx_id_dir = _resolve_account_wxid_dir(account_dir)
257279
wxid = wx_id_dir.name
258280

281+
logger.info("尝试获取next_action_id")
282+
try:
283+
next_action_id = await _get_next_action_id_async()
284+
logger.info(f"获取next_action_id成功: {next_action_id}")
285+
except Exception as e:
286+
raise RuntimeError(f"获取next_action_id失败:{e}")
287+
288+
259289
logger.info(f"正在为账号 {wxid} 获取密钥...")
260290

261291
try:
@@ -274,7 +304,7 @@ async def fetch_and_save_remote_keys(account: Optional[str] = None) -> Dict[str,
274304

275305
headers = {
276306
"Accept": "text/x-component",
277-
"Next-Action": NEXT_ACTION_ID,
307+
"Next-Action": next_action_id,
278308
"Next-Router-State-Tree": "%5B%22%22%2C%7B%22children%22%3A%5B%22dashboard%22%2C%7B%22children%22%3A%5B%22__PAGE__%22%2C%7B%7D%2Cnull%2Cnull%5D%7D%2Cnull%2Cnull%5D%7D%2Cnull%2Cnull%2Ctrue%5D",
279309
"Origin": "https://view.free.c3o.re",
280310
"Referer": "https://view.free.c3o.re/dashboard",

0 commit comments

Comments
 (0)