mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-09-11 02:44:57 +08:00
101 lines
4.6 KiB
Bash
Executable File
101 lines
4.6 KiB
Bash
Executable File
#!/bin/sh
|
||
# Telegram 配对语义契约。
|
||
#
|
||
# 背景 (issue #98 / PR #102):
|
||
# 旧实现在「Telegram 配对助手」里调用 `openclaw models auth login-telegram-bot`,
|
||
# 该子命令在 OpenClaw 2026.6+ 已不存在,实测报:
|
||
# Too many arguments for this command. Try: openclaw models auth --help
|
||
#
|
||
# 关键在于修法。配对与 Token 配置是两件不同的事:
|
||
# - Bot Token 配置 -> channels.telegram.botToken / channels add --bot-token
|
||
# - 用户配对 -> openclaw pairing list / pairing approve
|
||
# 把「配对助手」改成「再配一次 Token」虽然能消除报错,但等于删掉了配对功能。
|
||
# 本测试锁定二者不被混为一谈。
|
||
set -eu
|
||
|
||
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname "$0")" && pwd)
|
||
REPO_ROOT=$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd)
|
||
JS_CONFIG="$REPO_ROOT/root/usr/share/openclaw/oc-config-interactive.js"
|
||
SH_CONFIG="$REPO_ROOT/root/usr/share/openclaw/oc-config.sh"
|
||
|
||
fail() {
|
||
echo "FAIL: $1" >&2
|
||
exit 1
|
||
}
|
||
|
||
[ -f "$JS_CONFIG" ] || fail "missing $JS_CONFIG"
|
||
[ -f "$SH_CONFIG" ] || fail "missing $SH_CONFIG"
|
||
|
||
# ── 废弃 CLI 不得复活 ──
|
||
for f in "$JS_CONFIG" "$SH_CONFIG"; do
|
||
if grep -n 'login-telegram-bot' "$f" | grep -qv '^[0-9]*: *[/*#]'; then
|
||
fail "$(basename "$f") still calls the removed models auth login-telegram-bot subcommand"
|
||
fi
|
||
done
|
||
|
||
# ── 配对必须走上游 pairing 命令 (两侧实现一致) ──
|
||
grep -Fq "'pairing', 'list'" "$JS_CONFIG" || fail "interactive pairing must use openclaw pairing list"
|
||
grep -Fq "'pairing', 'approve'" "$JS_CONFIG" || fail "interactive pairing must use openclaw pairing approve"
|
||
grep -Fq 'pairing list telegram' "$SH_CONFIG" || fail "shell pairing must use openclaw pairing list"
|
||
grep -Fq 'pairing approve telegram' "$SH_CONFIG" || fail "shell pairing must use openclaw pairing approve"
|
||
|
||
# ── 配对与 Token 配置必须是两个独立入口 ──
|
||
grep -Fq "value: 'telegram' }" "$JS_CONFIG" || fail "Telegram token entry must remain in the channel menu"
|
||
grep -Fq "value: 'telegram-pairing' }" "$JS_CONFIG" || fail "Telegram pairing entry must remain in the channel menu"
|
||
|
||
# 配对分支不得把自己变成"再输入一次 Token"
|
||
pairing_block=$(awk "/case 'telegram-pairing': \{/,/^ \}$/" "$JS_CONFIG")
|
||
[ -n "$pairing_block" ] || fail "cannot locate telegram-pairing branch"
|
||
printf '%s' "$pairing_block" | grep -Fq 'pairing' || fail "telegram-pairing branch must invoke pairing commands"
|
||
if printf '%s' "$pairing_block" | grep -Eq 'channels\.telegram\.botToken *='; then
|
||
fail "telegram-pairing must not overwrite botToken (that is the token config feature, not pairing)"
|
||
fi
|
||
# 配对前应校验 Token 已存在,而不是引导用户重新录入
|
||
printf '%s' "$pairing_block" | grep -Fq "jsonGet('channels.telegram.botToken')" \
|
||
|| fail "telegram-pairing should require an existing bot token instead of asking for a new one"
|
||
|
||
# ── 捕获式 CLI 调用与解析器 ──
|
||
grep -Fq 'function ocCmdCapture' "$JS_CONFIG" || fail "need a silent CLI capture helper to parse pairing output"
|
||
grep -Fq 'function parsePairingCodes' "$JS_CONFIG" || fail "need a pairing code parser"
|
||
|
||
# ── 解析器行为验证 (需要 node) ──
|
||
NODE_BIN=""
|
||
for cand in node nodejs /opt/openclaw/node/bin/node; do
|
||
if command -v "$cand" >/dev/null 2>&1; then NODE_BIN=$(command -v "$cand"); break; fi
|
||
[ -x "$cand" ] && { NODE_BIN="$cand"; break; }
|
||
done
|
||
if [ -z "$NODE_BIN" ]; then
|
||
echo "ok (static only: no node interpreter available)"
|
||
exit 0
|
||
fi
|
||
|
||
"$NODE_BIN" -e '
|
||
const fs = require("fs");
|
||
const src = fs.readFileSync(process.argv[1], "utf8");
|
||
const start = src.indexOf("function parsePairingCodes");
|
||
if (start < 0) { console.error("parsePairingCodes not found"); process.exit(1); }
|
||
const body = src.slice(start, src.indexOf("\n}", start) + 2);
|
||
const parse = new Function(body + "; return parsePairingCodes;")();
|
||
|
||
const cases = [
|
||
// 上游 pairing list --json 的真实形状 (实测): {channel, requests:[...]}
|
||
["upstream shape", JSON.stringify({ channel: "telegram", requests: [{ code: "A1" }, { code: "B2" }] }), 2],
|
||
["upstream empty", JSON.stringify({ channel: "telegram", requests: [] }), 0],
|
||
["plain array", JSON.stringify([{ code: "X1" }]), 1],
|
||
["noise around json", "WARN boot\n[{\"code\":\"R1\"}]\ndone", 1],
|
||
["dedupe", JSON.stringify([{ code: "D" }, { code: "D" }]), 1],
|
||
["empty output", "", 0],
|
||
["garbage", "not json at all", 0],
|
||
];
|
||
let bad = [];
|
||
for (const [name, input, want] of cases) {
|
||
const got = parse(input);
|
||
if (!Array.isArray(got) || got.length !== want) {
|
||
bad.push(`${name}: want ${want}, got ${JSON.stringify(got)}`);
|
||
}
|
||
}
|
||
if (bad.length) { console.error(bad.join("; ")); process.exit(1); }
|
||
' "$JS_CONFIG" || fail "parsePairingCodes behavior mismatch"
|
||
|
||
echo "ok"
|