113 lines
4.2 KiB
Python
113 lines
4.2 KiB
Python
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
from pathlib import Path
|
|
|
|
|
|
TS_HELPER = '''// AIOS Weixin lossless getUpdates JSON begin
|
|
const WEIXIN_EXACT_ID_FIELDS = new Set(["message_id", "msg_id", "root_id", "parent_id", "server_msg_id", "new_msg_id"]);
|
|
function parseWeixinGetUpdatesJson(rawText: string): GetUpdatesResp {
|
|
return JSON.parse(rawText, (key: string, value: unknown, context?: { source?: string }) => {
|
|
if (!WEIXIN_EXACT_ID_FIELDS.has(key) || value == null || typeof value === "string") return value;
|
|
if (typeof value !== "number") throw new Error(`invalid ${key} type in getUpdates response`);
|
|
const source = context?.source;
|
|
if (!source || !/^(?:0|[1-9]\\d*)$/.test(source)) throw new Error(`unsafe numeric ${key} in getUpdates response`);
|
|
return source;
|
|
}) as GetUpdatesResp;
|
|
}
|
|
// AIOS Weixin lossless getUpdates JSON end'''
|
|
|
|
JS_HELPER = '''// AIOS Weixin lossless getUpdates JSON begin
|
|
const WEIXIN_EXACT_ID_FIELDS = new Set(["message_id", "msg_id", "root_id", "parent_id", "server_msg_id", "new_msg_id"]);
|
|
function parseWeixinGetUpdatesJson(rawText) {
|
|
return JSON.parse(rawText, (key, value, context) => {
|
|
if (!WEIXIN_EXACT_ID_FIELDS.has(key) || value == null || typeof value === "string")
|
|
return value;
|
|
if (typeof value !== "number")
|
|
throw new Error(`invalid ${key} type in getUpdates response`);
|
|
const source = context?.source;
|
|
if (!source || !/^(?:0|[1-9]\\d*)$/.test(source))
|
|
throw new Error(`unsafe numeric ${key} in getUpdates response`);
|
|
return source;
|
|
});
|
|
}
|
|
// AIOS Weixin lossless getUpdates JSON end'''
|
|
|
|
|
|
def insert_once(text: str, marker: str, addition: str) -> str:
|
|
if addition in text:
|
|
return text
|
|
if marker not in text:
|
|
raise RuntimeError(f'marker not found: {marker}')
|
|
return text.replace(marker, marker + '\n' + addition, 1)
|
|
|
|
|
|
def patch_api_ts(text: str) -> str:
|
|
text = insert_once(text, 'import { redactBody, redactUrl } from "../util/redact.js";', '\n' + TS_HELPER)
|
|
old = 'const resp: GetUpdatesResp = JSON.parse(rawText);'
|
|
new = 'const resp: GetUpdatesResp = parseWeixinGetUpdatesJson(rawText);'
|
|
if new not in text:
|
|
if old not in text:
|
|
raise RuntimeError('TypeScript getUpdates parser marker not found')
|
|
text = text.replace(old, new, 1)
|
|
return text
|
|
|
|
|
|
def patch_api_js(text: str) -> str:
|
|
text = insert_once(text, 'import { redactBody, redactUrl } from "../util/redact.js";', '\n' + JS_HELPER)
|
|
old = 'const resp = JSON.parse(rawText);'
|
|
new = 'const resp = parseWeixinGetUpdatesJson(rawText);'
|
|
if new not in text:
|
|
if old not in text:
|
|
raise RuntimeError('JavaScript getUpdates parser marker not found')
|
|
text = text.replace(old, new, 1)
|
|
return text
|
|
|
|
|
|
def patch_types_ts(text: str) -> str:
|
|
new = 'message_id?: string | number;'
|
|
if new in text:
|
|
return text
|
|
old = 'message_id?: number;'
|
|
if old not in text:
|
|
raise RuntimeError('message_id type marker not found')
|
|
return text.replace(old, new, 1)
|
|
|
|
|
|
def write_if_changed(path: Path, content: str) -> bool:
|
|
original = path.read_text(encoding='utf-8')
|
|
if original == content:
|
|
return False
|
|
path.write_text(content, encoding='utf-8')
|
|
return True
|
|
|
|
|
|
def apply(plugin_root: Path) -> dict[str, bool]:
|
|
paths = {
|
|
'src/api/api.ts': patch_api_ts,
|
|
'dist/src/api/api.js': patch_api_js,
|
|
'src/api/types.ts': patch_types_ts,
|
|
}
|
|
changed: dict[str, bool] = {}
|
|
for relative, transform in paths.items():
|
|
path = plugin_root / relative
|
|
if not path.exists():
|
|
raise RuntimeError(f'missing plugin file: {path}')
|
|
original = path.read_text(encoding='utf-8')
|
|
changed[relative] = write_if_changed(path, transform(original))
|
|
return changed
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('plugin_root', type=Path)
|
|
args = parser.parse_args()
|
|
changed = apply(args.plugin_root.resolve())
|
|
print(json.dumps({'plugin_root': str(args.plugin_root.resolve()), 'changed': changed}, ensure_ascii=False))
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
raise SystemExit(main())
|