From d0b83a4969b6f5fc19bbda1862ccaba20110933a Mon Sep 17 00:00:00 2001 From: kang Date: Sat, 9 May 2026 17:52:25 +0800 Subject: [PATCH] auto-save 2026-05-09 17:52 (~3) --- .memory/worklog.json | 27 +++++++++++++-------------- server/README.md | 1 + server/feishu_bridge.py | 16 ++++++++++------ 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/.memory/worklog.json b/.memory/worklog.json index 6e195da..df041be 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -1,19 +1,5 @@ { "entries": [ - { - "files_changed": 1, - "hash": "d8b5e3a", - "message": "auto-save 2026-05-07 15:26 (~1)", - "ts": "2026-05-07T15:26:53+08:00", - "type": "commit" - }, - { - "files_changed": 1, - "hash": "a6a644e", - "message": "auto-save 2026-05-07 15:32 (~1)", - "ts": "2026-05-07T15:34:35+08:00", - "type": "commit" - }, { "files_changed": 1, "hash": "d8dcef7", @@ -3474,6 +3460,19 @@ "type": "session-heartbeat", "message": "Codex 会话活跃 · 最近命令:codex · 分支 master · 1 项未提交变更 · 最近提交:auto-save 2026-05-09 17:41 (~1)", "files_changed": 1 + }, + { + "ts": "2026-05-09T17:46:54+08:00", + "type": "commit", + "message": "auto-save 2026-05-09 17:46 (~1)", + "hash": "4dc1b24", + "files_changed": 1 + }, + { + "ts": "2026-05-09T09:48:27Z", + "type": "session-heartbeat", + "message": "Codex 会话活跃 · 最近命令:codex · 分支 master · 1 项未提交变更 · 最近提交:auto-save 2026-05-09 17:46 (~1)", + "files_changed": 1 } ] } diff --git a/server/README.md b/server/README.md index d669969..9b62fbe 100644 --- a/server/README.md +++ b/server/README.md @@ -18,6 +18,7 @@ 2. 事件订阅里添加请求地址:默认应用用 `https://hermes.kang-kang.com/feishu/events`;其它应用用 `https://hermes.kang-kang.com/feishu/events/{app_id}`。 3. 如果启用事件加密,需要先给本服务补充解密支持;当前版本按明文事件回调处理。 4. 建议配置 `FEISHU_VERIFICATION_TOKEN`,并保持和飞书后台一致。 +5. 如果飞书后台刚切换过 token,`FEISHU_VERIFICATION_TOKEN` 可临时用英文逗号配置多个值,兼容旧事件投递和新 URL 校验。 ## 主动通知示例 diff --git a/server/feishu_bridge.py b/server/feishu_bridge.py index 01f0054..a68edb0 100644 --- a/server/feishu_bridge.py +++ b/server/feishu_bridge.py @@ -37,6 +37,10 @@ def _csv_set(value: str) -> set[str]: return {item.strip() for item in value.split(",") if item.strip()} +def _csv_list(value: str) -> list[str]: + return [item.strip() for item in value.split(",") if item.strip()] + + def _load_feishu_apps() -> dict[str, dict[str, Any]]: apps: dict[str, dict[str, Any]] = {} for suffix in ["", *[f"_{idx}" for idx in range(2, 10)]]: @@ -47,7 +51,7 @@ def _load_feishu_apps() -> dict[str, dict[str, Any]]: apps[app_id] = { "app_id": app_id, "app_secret": app_secret, - "verification_token": _env(f"FEISHU_VERIFICATION_TOKEN{suffix}"), + "verification_tokens": _csv_list(_env(f"FEISHU_VERIFICATION_TOKEN{suffix}")), "default_receive_id": _env(f"FEISHU_DEFAULT_RECEIVE_ID{suffix}"), "default_receive_id_type": _env( f"FEISHU_DEFAULT_RECEIVE_ID_TYPE{suffix}", @@ -188,17 +192,17 @@ def token_digest(value: str) -> str: def verify_callback_token(body: dict[str, Any], app_id: str) -> bool: app = Config.feishu_apps.get(app_id, {}) - expected = app.get("verification_token", "") - if not expected: + expected_tokens = app.get("verification_tokens", []) + if not expected_tokens: return True token = callback_token(body) - ok = token == expected + ok = token in expected_tokens if not ok: logging.warning( - "invalid Feishu verification token app_id=%s got=%s expected=%s body_keys=%s header_keys=%s event_keys=%s", + "invalid Feishu verification token app_id=%s got=%s expected_any=%s body_keys=%s header_keys=%s event_keys=%s", app_id, token_digest(token), - token_digest(expected), + [token_digest(item) for item in expected_tokens], sorted(body.keys()), sorted(body.get("header", {}).keys()) if isinstance(body.get("header"), dict) else [], sorted(body.get("event", {}).keys()) if isinstance(body.get("event"), dict) else [],