9b4a25799f
Replace fixed ackText with a rule-based AckProvider that picks response templates by message type and intent (translate, summary, rewrite, poster, ppt, mindmap, code, search, schedule). Pure sync, zero I/O, auto-falls back to config.ackText on any error. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
106 lines
4.7 KiB
Python
106 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
|
from pathlib import Path
|
|
|
|
path = Path("/Users/john/Project/Memind/server.mjs")
|
|
text = path.read_text()
|
|
|
|
if "function verifyWechatMpUrlChallenge" not in text:
|
|
text = text.replace(
|
|
' acceptLink: env.H5_WECHAT_MP_ACCEPT_LINK !== "0"\n };\n}',
|
|
' acceptLink: env.H5_WECHAT_MP_ACCEPT_LINK !== "0",\n encodingAesKey: env.H5_WECHAT_MP_ENCODING_AES_KEY?.trim() ?? ""\n };\n}',
|
|
1,
|
|
)
|
|
old_sig = """function verifyWechatMpSignature({ token, timestamp, nonce, signature }) {
|
|
const digest = crypto27.createHash("sha1").update([token, timestamp, nonce].sort().join("")).digest("hex");
|
|
return digest === String(signature ?? "");
|
|
}"""
|
|
new_sig = """function sha1WechatHex(parts) {
|
|
return crypto27.createHash("sha1").update([...parts].sort().join("")).digest("hex");
|
|
}
|
|
function verifyWechatMpSignature({ token, timestamp, nonce, signature }) {
|
|
return sha1WechatHex([token, timestamp, nonce]) === String(signature ?? "");
|
|
}
|
|
function verifyWechatMpMsgSignature({ token, timestamp, nonce, echoStr, msgSignature }) {
|
|
return sha1WechatHex([token, timestamp, nonce, echoStr]) === String(msgSignature ?? "");
|
|
}
|
|
function decodeWechatMpAesKey(encodingAesKey) {
|
|
const key = Buffer.from(`${String(encodingAesKey ?? "").trim()}=`, "base64");
|
|
if (key.length !== 32) throw new Error("invalid encoding aes key");
|
|
return key;
|
|
}
|
|
function decryptWechatMpPayload({ encodingAesKey, appId, encrypted }) {
|
|
const key = decodeWechatMpAesKey(encodingAesKey);
|
|
const iv = key.subarray(0, 16);
|
|
const decipher = crypto27.createDecipheriv("aes-256-cbc", key, iv);
|
|
decipher.setAutoPadding(false);
|
|
const cipherBuf = Buffer.from(String(encrypted ?? ""), "base64");
|
|
let decoded = Buffer.concat([decipher.update(cipherBuf), decipher.final()]);
|
|
const pad = decoded.at(-1);
|
|
if (!Number.isInteger(pad) || pad < 1 || pad > 32) throw new Error("invalid padding");
|
|
decoded = decoded.subarray(0, decoded.length - pad);
|
|
const content = decoded.subarray(16);
|
|
const msgLen = content.readUInt32BE(0);
|
|
const msg = content.subarray(4, 4 + msgLen).toString("utf8");
|
|
const receivedAppId = content.subarray(4 + msgLen).toString("utf8");
|
|
if (receivedAppId !== String(appId ?? "")) throw new Error("appid mismatch");
|
|
return msg;
|
|
}
|
|
function verifyWechatMpUrlChallenge(query = {}, config = {}) {
|
|
const timestamp = String(query.timestamp ?? "");
|
|
const nonce = String(query.nonce ?? "");
|
|
const echostr = String(query.echostr ?? "");
|
|
const encryptType = String(query.encrypt_type ?? "").toLowerCase();
|
|
if (encryptType === "aes") {
|
|
const msgSignature = String(query.msg_signature ?? "");
|
|
if (!config?.token || !config?.encodingAesKey || !config?.appId) {
|
|
return { ok: false, status: 503, body: "wechat mp aes mode not configured" };
|
|
}
|
|
if (!verifyWechatMpMsgSignature({ token: config.token, timestamp, nonce, echoStr: echostr, msgSignature })) {
|
|
return { ok: false, status: 403, body: "invalid signature" };
|
|
}
|
|
try {
|
|
const plain = decryptWechatMpPayload({ encodingAesKey: config.encodingAesKey, appId: config.appId, encrypted: echostr });
|
|
return { ok: true, status: 200, body: plain };
|
|
} catch {
|
|
return { ok: false, status: 403, body: "invalid echostr" };
|
|
}
|
|
}
|
|
if (!verifyWechatMpSignature({ token: config?.token, timestamp, nonce, signature: String(query.signature ?? "") })) {
|
|
return { ok: false, status: 403, body: "invalid signature" };
|
|
}
|
|
return { ok: true, status: 200, body: echostr };
|
|
}"""
|
|
if old_sig not in text:
|
|
raise SystemExit("signature block not found")
|
|
text = text.replace(old_sig, new_sig, 1)
|
|
text = text.replace(
|
|
""" const verifyRequest = (query = {}) => verifyWechatMpSignature({
|
|
token: config.token,
|
|
timestamp: String(query.timestamp ?? ""),
|
|
nonce: String(query.nonce ?? ""),
|
|
signature: String(query.signature ?? "")
|
|
});""",
|
|
""" const verifyRequest = (query = {}) => verifyWechatMpSignature({
|
|
token: config.token,
|
|
timestamp: String(query.timestamp ?? ""),
|
|
nonce: String(query.nonce ?? ""),
|
|
signature: String(query.signature ?? "")
|
|
});
|
|
const verifyUrlChallenge = (query = {}) => verifyWechatMpUrlChallenge(query, config);""",
|
|
1,
|
|
)
|
|
text = text.replace(
|
|
" verifyRequest,\n handleInboundMessage,",
|
|
" verifyRequest,\n verifyUrlChallenge,\n handleInboundMessage,",
|
|
1,
|
|
)
|
|
path.write_text(text)
|
|
print("bundled server.mjs aes patch applied")
|
|
else:
|
|
print("bundled aes helpers already present")
|
|
|
|
release = Path("/Users/john/Project/releases/memind-live-before-20260626-095637-d51df2f/wechat-mp.mjs")
|
|
if release.exists():
|
|
Path("/Users/john/Project/Memind/wechat-mp.mjs").write_text(release.read_text())
|
|
print("wechat-mp.mjs restored from release")
|