Skip to content

feat: bot multi-mode platform with restricted mode and admin diagnostics#129

Merged
yazelin merged 19 commits intomainfrom
openspec/bot-multi-mode-platform
Feb 26, 2026
Merged

feat: bot multi-mode platform with restricted mode and admin diagnostics#129
yazelin merged 19 commits intomainfrom
openspec/bot-multi-mode-platform

Conversation

@yazelin
Copy link
Owner

@yazelin yazelin commented Feb 26, 2026

Summary

  • 身份分流路由器:根據 BOT_UNBOUND_USER_POLICY 設定(reject/restricted),決定未綁定用戶的處理路徑
  • 受限模式 AI:未綁定用戶可透過 bot-restricted Agent 使用受限功能(限制工具白名單、縮短對話歷史)
  • 頻率限制:原子性 check-and-increment 防止 TOCTOU 競爭,支援每小時/每日訊息上限
  • 斜線指令框架:可擴充的 /reset/debug 指令路由,支援 Line/Telegram 跨平台
  • 管理員診斷/debug 指令搭配 debug-skill 腳本(server logs、AI logs、nginx、DB、system health)
  • 知識庫公開存取is_public 欄位控制未綁定用戶可查詢的知識範圍
  • 圖書館公開資料夾LIBRARY_PUBLIC_FOLDERS 環境變數控制未綁定用戶可瀏覽的資料夾

Key Changes

  • 新增 identity_router.pyrate_limiter.pycommands.pycommand_handlers.py
  • 新增 debug-skill/ 含 5 個診斷腳本
  • 修改 linebot_router.pytelegram_handler.py 整合身份分流
  • 新增 bot_usage_tracking 資料表 migration
  • 知識庫 model/service/MCP 工具新增 is_public 支援
  • 安全強化:錯誤訊息不洩漏內部細節、輸入驗證、nginx log_type 白名單

Test plan

  • 708 tests passing(含 49 個新增測試)
  • Identity router 單元測試
  • Rate limiter 單元測試(含 TOCTOU、fail-open、cleanup)
  • Debug command 單元測試
  • 整合測試(reject 回歸、restricted 流程、公開知識過濾、權限控制)

🤖 Generated with Claude Code

yazelin and others added 11 commits February 26, 2026 16:43
建立 Bot 多模式平台基礎架構:
- 新增斜線指令路由框架(commands.py),支援指令註冊、別名、權限檢查
- 將 /reset 指令遷移到 CommandRouter,Line/Telegram 共用
- 新增 bot_usage_tracking 資料表 migration(rate limiter 用)
- 新增 BOT_UNBOUND_USER_POLICY 等 6 個環境變數設定
- 新增 OpenSpec change:proposal、design、specs、tasks 完整規劃

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add services/bot/identity_router.py with route_unbound() and handle_restricted_mode()
- Modify linebot_router.py access control to use identity router for user_not_bound
- Modify telegram handler access control to use identity router
- Binding code verification remains prioritized before identity routing
- 16 unit tests for route_unbound, get_unbound_policy, and handle_restricted_mode

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add BOT_RESTRICTED_PROMPT for unbound user restricted mode
- Add BOT_DEBUG_PROMPT for admin diagnostic mode with debug-skill scripts
- Add DEFAULT_BOT_MODE_AGENTS config (bot-restricted, bot-debug)
- Refactor ensure_default_linebot_agents() into shared _ensure_agents() helper
- Agents auto-created on startup, won't overwrite existing customizations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add services/bot/rate_limiter.py with check_rate_limit() and record_usage()
- Integrate rate limit check at restricted mode entry point
- Record usage after successful AI processing
- PostgreSQL UPSERT for hourly/daily counters
- 9 unit tests all passing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- SKILL.md defining debug-skill with run_skill_script tool
- check-server-logs: journalctl with lines/keyword params
- check-ai-logs: query ai_logs table with limit/errors_only params
- check-nginx-logs: docker logs with lines/type (access/error) params
- check-db-status: connections, table sizes, database size
- check-system-health: comprehensive health check with status report

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Register /debug command with aliases /診斷 /diag (admin-only, private-only)
- Handler uses bot-debug Agent with BOT_DEBUG_MODEL
- Default prompt triggers check-system-health when no problem description
- 10 unit tests: handler logic + routing permissions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…control (Group 8)

知識庫新增 is_public 欄位支援公開存取控制:
- models 新增 is_public 布林欄位(預設 false)
- search_knowledge 新增 public_only 過濾(未綁定用戶自動啟用)
- MCP search_knowledge 工具根據 ctos_user_id 自動設定 public_only
- 前端知識庫編輯器新增「公開」勾選框
- list_library_folders 支援 LIBRARY_PUBLIC_FOLDERS 環境變數過濾
- 修復相關測試(rate limiter mock、CommandRouter 初始化、agent 數量調整)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
23 個整合測試覆蓋:
- reject 策略回歸測試(Line/Telegram 個人/群組)
- restricted 策略受限模式完整流程
- 知識庫 public_only 過濾邏輯驗證
- /debug 指令權限控制(管理員/非管理員/群組/未綁定)
- rate limiter 超限阻斷與 key 格式
- 跨功能整合(config、models、指令別名)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
歸檔已完成的 bot-multi-mode-platform change,並同步 delta specs:
- 新增 3 個 capability specs: bot-identity-router, bot-rate-limiter, bot-slash-commands
- 更新 bot-platform spec: 新增 binding_status 欄位
- 更新 line-bot spec: 策略式未綁定用戶存取控制
- 更新 knowledge-base spec: is_public 公開存取旗標、圖書館公開資料夾

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
修復 code review 發現的重要問題:
- parse_ai_response 呼叫方式錯誤(傳入 ClaudeResponse 而非 .message,回傳 tuple 被當 dict)
- 未綁定用戶永遠被 _check_library_permission 拒絕(跳過完整權限檢查改用公開資料夾路徑)
- _sanitize_path_segment 破壞多層路徑(改為分段清理後重組)
- call_claude 在受限模式無 try/except(新增錯誤處理)
- /debug 例外訊息洩漏內部細節(改用通用錯誤訊息)
- 移除未使用的 record_usage import

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rate limiter: atomic check-and-increment in single transaction (TOCTOU fix),
  add cleanup_old_tracking() for data retention, fail-open on DB errors
- Line router: intercept slash commands before restricted mode AI processing,
  fallback to push_text when reply_token expires during long AI calls
- Commands: guard against missing handler (was non-async lambda default)
- Debug scripts: sanitize error messages (no str(e) leak), validate int inputs
  with range clamping, whitelist nginx log_type, strict boolean for errors_only
- SKILL.md: set requires_app to admin (was null/unrestricted)
- Config: validate bot_rate_limit_hourly <= bot_rate_limit_daily
- Update all tests for new atomic rate limiter API (708 tests pass)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gemini-code-assist
Copy link

Summary of Changes

Hello @yazelin, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取請求引入了一個多模式 Bot 平台,旨在提升系統的靈活性和安全性。它透過身份分流機制,允許未綁定用戶在受限模式下使用部分 AI 功能,並對其進行頻率限制。同時,新增的斜線指令框架和管理員診斷工具,顯著增強了 Bot 的可擴展性和系統維護能力,並透過知識庫公開存取控制,精確管理資訊可見性。

Highlights

  • Bot 多模式平台: 引入身份分流路由器,根據 BOT_UNBOUND_USER_POLICY 設定(reject/restricted),決定未綁定用戶的處理路徑。
  • 受限模式 AI: 未綁定用戶可透過 bot-restricted Agent 使用受限功能,包括限制工具白名單和縮短對話歷史。
  • 頻率限制: 實作原子性 check-and-increment 機制,防止 TOCTOU 競爭,支援每小時/每日訊息上限,並新增 bot_usage_tracking 資料表。
  • 斜線指令框架: 建立可擴充的 /reset/debug 指令路由系統,支援 Line/Telegram 跨平台,並將現有 /reset 指令遷移至此框架。
  • 管理員診斷工具: 新增 /debug 指令,搭配 debug-skill 腳本(server logs、AI logs、nginx、DB、system health),供管理員透過 AI 分析系統狀態。
  • 知識庫公開存取: 知識庫模型新增 is_public 欄位,控制未綁定用戶可查詢的知識範圍,並支援圖書館公開資料夾設定。

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • backend/migrations/versions/009_add_bot_usage_tracking.py
    • 新增 bot_usage_tracking 資料表,用於追蹤 Bot 用戶的訊息使用量以支援頻率限制。
  • backend/src/ching_tech_os/api/linebot_router.py
    • 更新 Line Bot 路由,整合身份分流邏輯,處理未綁定用戶的拒絕或受限模式流程。
    • 引入斜線指令攔截,優先處理 /reset/debug 等指令。
  • backend/src/ching_tech_os/config.py
    • 新增多模式 Bot 平台的配置設定,包括未綁定用戶策略、受限模式和 Debug 模式的 AI 模型、頻率限制參數及圖書館公開資料夾列表。
  • backend/src/ching_tech_os/main.py
    • 在應用程式啟動時註冊內建的 Bot 斜線指令。
  • backend/src/ching_tech_os/models/knowledge.py
    • 為知識庫相關模型(KnowledgeCreate, KnowledgeUpdate, KnowledgeResponse, KnowledgeListItem, IndexEntry)新增 is_public 欄位,用於控制公開存取權限。
  • backend/src/ching_tech_os/services/bot/command_handlers.py
    • 新增內建指令處理器,實作 /reset 指令的對話歷史清除功能,以及 /debug 指令的管理員系統診斷功能。
  • backend/src/ching_tech_os/services/bot/commands.py
    • 新增 Bot 斜線指令路由系統,提供可擴充的指令框架,支援指令註冊、解析、分發及權限檢查。
  • backend/src/ching_tech_os/services/bot/identity_router.py
    • 新增 Bot 身份分流路由器,根據配置策略(reject/restricted)和用戶綁定狀態,決定未綁定用戶的訊息處理路徑,並實作受限模式 AI 流程。
  • backend/src/ching_tech_os/services/bot/rate_limiter.py
    • 新增 Bot 頻率限制器,使用 bot_usage_tracking 資料表原子性地檢查並遞增未綁定用戶的訊息計數,以實施每小時/每日使用上限。
  • backend/src/ching_tech_os/services/bot_telegram/handler.py
    • 更新 Telegram Bot 處理器,整合斜線指令路由系統,並將未綁定用戶的處理委派給身份分流路由器。
    • 移除舊有的硬編碼 /reset 指令處理邏輯。
  • backend/src/ching_tech_os/services/knowledge.py
    • 修改知識庫搜尋服務,新增 public_only 參數,允許在受限模式下僅回傳公開的知識項目。
  • backend/src/ching_tech_os/services/linebot_agents.py
    • 新增 AGENT_BOT_RESTRICTEDAGENT_BOT_DEBUG 常數,並定義其預設 Agent 設定和 System Prompt。
    • 修改 Agent 初始化邏輯,使其能確保所有預設 Bot Agent 的存在,包括受限模式和 Debug 模式 Agent。
  • backend/src/ching_tech_os/services/linebot_ai.py
    • 在 Line Bot AI 訊息處理流程中,於 AI 處理之前插入斜線指令攔截邏輯,以優先處理指令。
  • backend/src/ching_tech_os/services/mcp/knowledge_tools.py
    • 修改知識庫 MCP 工具的 search_knowledge 函式,當呼叫者為未綁定用戶時,自動設定 public_only=True 以限制查詢範圍。
  • backend/src/ching_tech_os/services/mcp/nas_tools.py
    • 修改 NAS 工具的 list_library_folders 函式,為未綁定用戶實施公開資料夾過濾,僅顯示配置為公開的資料夾。
  • backend/src/ching_tech_os/skills/debug-skill/SKILL.md
    • 新增 debug-skill 的技能定義文件,描述其功能和可用的診斷腳本。
  • backend/src/ching_tech_os/skills/debug-skill/scripts/check-ai-logs.py
    • 新增 check-ai-logs 診斷腳本,用於查詢 AI 對話記錄,支援限制筆數和僅顯示錯誤。
  • backend/src/ching_tech_os/skills/debug-skill/scripts/check-db-status.py
    • 新增 check-db-status 診斷腳本,用於查詢資料庫狀態,包括連線數、表行數和資料庫大小。
  • backend/src/ching_tech_os/skills/debug-skill/scripts/check-nginx-logs.py
    • 新增 check-nginx-logs 診斷腳本,用於查詢 Nginx 日誌,支援限制行數和選擇日誌類型(access/error)。
  • backend/src/ching_tech_os/skills/debug-skill/scripts/check-server-logs.py
    • 新增 check-server-logs 診斷腳本,用於查詢 CTOS 伺服器日誌,支援限制行數和關鍵字過濾。
  • backend/src/ching_tech_os/skills/debug-skill/scripts/check-system-health.py
    • 新增 check-system-health 診斷腳本,提供綜合系統健康檢查,匯總所有診斷項目的狀態報告。
  • backend/tests/test_bot_commands.py
    • 新增 Bot 斜線指令路由框架的單元測試,涵蓋指令解析、別名、參數處理及權限檢查。
  • backend/tests/test_bot_multi_mode_integration.py
    • 新增 Bot 多模式平台整合測試,驗證身份分流、受限模式、知識庫公開存取、/debug 指令及頻率限制等功能的端對端行為。
  • backend/tests/test_bot_telegram_handler.py
    • 更新 Telegram Bot 處理器的測試,以反映斜線指令路由的整合。
  • backend/tests/test_debug_command.py
    • 新增 /debug 指令的單元測試,驗證其功能、權限控制和錯誤處理。
  • backend/tests/test_identity_router.py
    • 新增身份分流路由器的單元測試,驗證不同策略下的行為和受限模式 AI 流程。
  • backend/tests/test_linebot_agents_init.py
    • 更新 Line Bot Agent 初始化測試,以涵蓋新加入的 bot-restrictedbot-debug Agent。
  • backend/tests/test_rate_limiter.py
    • 新增頻率限制器的單元測試,驗證使用量追蹤、限額檢查、原子性操作及錯誤處理。
  • frontend/js/knowledge-base.js
    • 修改知識庫編輯器,新增 is_public 勾選框,允許用戶設定知識項目是否公開。
  • openspec/changes/archive/2026-02-26-bot-multi-mode-platform/.openspec.yaml
    • 新增 openspec 歸檔的元數據文件。
  • openspec/changes/archive/2026-02-26-bot-multi-mode-platform/design.md
    • 新增 Bot 多模式平台功能的設計文件,詳細說明了指令路由、身份分流、受限模式、Debug 指令及頻率限制的設計決策。
  • openspec/changes/archive/2026-02-26-bot-multi-mode-platform/proposal.md
    • 新增 Bot 多模式平台功能的提案文件,闡述了變更的動機、內容、新功能、修改功能及影響。
  • openspec/changes/archive/2026-02-26-bot-multi-mode-platform/specs/bot-identity-router/spec.md
    • 新增 Bot 身份分流路由器的規格文件,定義了未綁定用戶策略、分流邏輯、受限模式 AI 流程及 Agent 初始化要求。
  • openspec/changes/archive/2026-02-26-bot-multi-mode-platform/specs/bot-platform/spec.md
    • 新增 Bot 平台規格文件,定義了 BotContext 應包含 binding_status 欄位,並根據此狀態選擇 Agent。
  • openspec/changes/archive/2026-02-26-bot-multi-mode-platform/specs/bot-rate-limiter/spec.md
    • 新增 Bot 頻率限制器的規格文件,定義了使用量追蹤資料表、頻率限制檢查邏輯及配置要求。
  • openspec/changes/archive/2026-02-26-bot-multi-mode-platform/specs/bot-slash-commands/spec.md
    • 新增 Bot 斜線指令規格文件,定義了指令解析、註冊機制、權限檢查、/reset 遷移及 /debug 診斷指令的要求。
  • openspec/changes/archive/2026-02-26-bot-multi-mode-platform/specs/knowledge-base/spec.md
    • 新增知識庫規格文件,定義了知識項目公開存取旗標、受限模式知識查詢過濾及圖書館公開資料夾的要求。
  • openspec/changes/archive/2026-02-26-bot-multi-mode-platform/specs/line-bot/spec.md
    • 新增 Line Bot 規格文件,定義了 Line Bot 存取控制應根據未綁定用戶策略決定處理方式。
  • openspec/changes/archive/2026-02-26-bot-multi-mode-platform/tasks.md
    • 新增 Bot 多模式平台功能的任務清單,詳細列出了開發和測試的各個步驟。
  • openspec/specs/bot-identity-router/spec.md
    • 新增 Bot 身份分流路由器的規格文件,定義了未綁定用戶策略、分流邏輯、受限模式 AI 流程及 Agent 初始化要求。
  • openspec/specs/bot-platform/spec.md
    • 修改 Bot 平台規格文件,新增 BotContext 應包含 binding_status 欄位,並根據此狀態選擇 Agent。
  • openspec/specs/bot-rate-limiter/spec.md
    • 新增 Bot 頻率限制器的規格文件,定義了使用量追蹤資料表、頻率限制檢查邏輯及配置要求。
  • openspec/specs/bot-slash-commands/spec.md
    • 新增 Bot 斜線指令規格文件,定義了指令解析、註冊機制、權限檢查、/reset 遷移及 /debug 診斷指令的要求。
  • openspec/specs/knowledge-base/spec.md
    • 修改知識庫規格文件,新增知識項目公開存取旗標、受限模式知識查詢過濾及圖書館公開資料夾的要求。
  • openspec/specs/line-bot/spec.md
    • 修改 Line Bot 規格文件,定義了 Line Bot 存取控制應根據未綁定用戶策略決定處理方式。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Cover lines 279-341: slash command interception, AI processing,
reply_token expiry fallback, push_text fallback, error handling.
Raises overall coverage from 84.33% to 87.83%.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces an important and well-structured multi-mode bot platform, featuring a clear identity router, a flexible command framework, and admin diagnostic tools, including a restricted mode for unbound users. While the implementation includes security controls like rate limiting and access checks, I've identified potential prompt injection vulnerabilities where user-supplied content is directly concatenated into LLM prompts. These should be mitigated by using proper delimiters and sanitizing platform-provided metadata like display names. Furthermore, I have some suggestions regarding database migration, exception handling, and code consistency to further enhance the quality of these changes.

Comment on lines +198 to +201
if user_display_name:
user_message = f"user[{user_display_name}]: {content}"
else:
user_message = f"user: {content}"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

Potential prompt injection vulnerability. User-supplied content and the platform display name are directly concatenated into the LLM prompt. An attacker could manipulate their display name or the message content to inject instructions that bypass the 'restricted mode' constraints. It is recommended to sanitize the display name and use clear delimiters (e.g., XML-style tags) to separate user input from the rest of the prompt.

Comment on lines +55 to +57
user_problem = ctx.raw_args.strip() if ctx.raw_args else ""
if user_problem:
prompt = f"管理員問題:{user_problem}"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

Potential prompt injection vulnerability. The user-provided problem description is directly embedded into the prompt sent to the diagnostic agent. Although this command is restricted to administrators, using delimiters to wrap the user input is a best practice to prevent unintended manipulation of the LLM's behavior.

Comment on lines 29 to 32
op.execute("""
CREATE INDEX idx_bot_usage_tracking_user_period
ON bot_usage_tracking(bot_user_id, period_type, period_key)
""")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

在第 26 行的 UNIQUE 約束已經隱含地在 (bot_user_id, period_type, period_key) 上建立了一個唯一索引。因此,在第 29-32 行再次對相同的欄位集合建立索引是多餘的,會消耗不必要的資料庫資源。建議移除這個多餘的 CREATE INDEX 陳述式。

Comment on lines 340 to 341
except Exception:
pass

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

這個 except Exception: pass 區塊會靜默地忽略嘗試透過 push_text 向使用者發送錯誤通知時發生的任何錯誤。這可能導致主要流程失敗,而使用者未收到通知,且日誌中沒有任何記錄說明為何通知失敗。建議至少將此異常記錄下來。

Suggested change
except Exception:
pass
except Exception as push_e:
logger.warning(f"推送錯誤訊息失敗: {push_e}")

return "診斷完成,但未產生回報內容。"

# 過濾 FILE_MESSAGE(debug 不需要檔案傳送)
import re

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

import re 陳述式位於 _handle_debug 函式內部。根據 PEP 8 的規範,import 通常應放在檔案的頂部。請將此行移除,並在檔案開頭處加入 import re,這樣可以讓所有依賴項一目了然,從而提高程式碼的可讀性和可維護性。

Comment on lines 141 to 142
get_tools_for_user,
get_tool_routing_for_user,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

函式 get_tools_for_userget_tool_routing_for_user 在此檔案中被匯入,但似乎未被使用。移除未使用的匯入是個好習慣,可以保持程式碼整潔並避免混淆。

Comment on lines +467 to 476
# Telegram 專屬指令(不在 CommandRouter 中的)
if not is_group:
# /start 和 /help 指令(不需綁定即可使用)
cmd = text.strip().split("@")[0] # 處理 /start@botname 格式
cmd = text.strip().split("@")[0]
if cmd == "/start":
await adapter.send_text(chat_id, START_MESSAGE)
return
if cmd == "/help":
await adapter.send_text(chat_id, HELP_MESSAGE)
return

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

/start/help 指令是作為特殊情況在新的 command_router 框架之外處理的。為了保持一致性和更好的可維護性,建議將它們也註冊為 SlashCommand 物件(例如在 command_handlers.py 中),並移除此處的特殊處理邏輯。這將有助於將所有指令處理邏輯集中管理。

yazelin and others added 4 commits February 26, 2026 21:40
- Remove redundant index from migration 009 (UNIQUE already implies it)
- Log warning instead of silent pass on push_text failure
- Move `import re` to file top in identity_router and command_handlers
- Remove unused imports (get_tools_for_user, get_tool_routing_for_user)
- Add slash command interception to Telegram restricted mode

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cover slash command interception, AI processing, error handling,
and silent routing in restricted mode to maintain 85% coverage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- H1: Rate limiter rollback on denial (avoid inflating counters)
- H3: Remove dead code in Telegram restricted mode command parsing
- H4: Rename reset_conversation param to platform_user_id (cross-platform)
- M1: Telegram restricted mode now saves message and passes message_uuid
- M2: Schedule cleanup_old_bot_tracking (daily at 04:30)
- M4: Remove redundant group check in _handle_reset

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
asyncpg returns UUID objects from row["id"], but downstream code
(CommandContext, rate_limiter, identity_router) expects str.
Add str() conversion at the source to match Line-side behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@yazelin
Copy link
Owner Author

yazelin commented Feb 26, 2026

/gemini review

- Remove is_reset_command block from process_message_with_ai (now
  handled by CommandRouter in handle_text_message)
- Remove unused RESET_COMMANDS constant from Telegram handler
- Remove unused check_rate_limit backward-compat wrapper
- Fix misleading SQL comment in check-ai-logs.py
- Remove related obsolete tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces multi-mode support for the Bot platform, including identity routing, restricted mode, frequency limiting, and administrator diagnostic commands. While the implementation follows security best practices in many areas (e.g., parameterized SQL queries, path traversal prevention) and features well-designed components like the command routing framework (commands.py), identity routing (identity_router.py), and atomic frequency limiting (rate_limiter.py), it introduces potential prompt injection vulnerabilities in the new identity router and command handlers by directly concatenating untrusted user input into LLM prompts. Overall, the code quality is high, but two suggestions were made regarding code structure to improve readability and maintainability.

Comment on lines +196 to +223
user_message = f"user[{user_display_name}]: {content}"
else:
user_message = f"user: {content}"

# 7. 組裝工具列表(僅 Agent 定義的工具 + 對應的 MCP 工具)
# 受限模式的 MCP 工具也受 Agent 設定限制
mcp_tools = await get_mcp_tool_names(exclude_group_only=not is_group)
# 只保留 Agent 工具白名單中的 MCP 工具
if agent_tools:
mcp_tool_set = set(mcp_tools)
allowed_mcp = [t for t in agent_tools if t in mcp_tool_set]
# Agent 工具中不在 MCP 列表中的可能是外部 skill
non_mcp_tools = [t for t in agent_tools if t not in mcp_tool_set]
all_tools = list(dict.fromkeys(allowed_mcp + non_mcp_tools))
else:
all_tools = []

# 取得需要的 MCP server(按需載入)
required_mcp_servers = set()
if all_tools:
# 受限模式使用空權限取得 MCP servers
required_mcp_servers = await get_mcp_servers_for_user(app_permissions)

# 8. 呼叫 Claude CLI
start_time = time.time()

try:
response = await call_claude(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

Untrusted user input (content and user_display_name) is directly concatenated into the prompt sent to the LLM. This can lead to prompt injection attacks where an attacker can manipulate the LLM's behavior by providing specially crafted input. It is recommended to use structured prompts (e.g., separate system and user messages) and to sanitize or delimit user input to prevent it from being interpreted as instructions.

Comment on lines +54 to +64
user_problem = ctx.raw_args.strip() if ctx.raw_args else ""
if user_problem:
prompt = f"管理員問題:{user_problem}"
else:
prompt = "請執行系統綜合健康檢查(check-system-health),分析目前系統狀態並回報結果。"

# 呼叫 Claude CLI
model = settings.bot_debug_model
start_time = time.time()

try:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

Untrusted user input (user_problem) is directly concatenated into the prompt sent to the LLM in the /debug command handler. Although this command is restricted to administrators, it still poses a risk of prompt injection if an administrator's account is compromised or if an administrator is tricked into providing malicious input. Structured prompts and input sanitization should be used.

Comment on lines +279 to +342
elif route_result.action == "restricted":
# 受限模式:先攔截斜線指令
from ..services.bot.commands import router as cmd_router

parsed_cmd = cmd_router.parse(content)
if parsed_cmd:
cmd, cmd_args = parsed_cmd
from ..services.bot.commands import CommandContext

cmd_ctx = CommandContext(
platform_type="line",
platform_user_id=line_user_id,
bot_user_id=str(user_uuid),
ctos_user_id=None, # 未綁定
is_admin=False,
is_group=is_group,
group_id=str(group_uuid) if group_uuid else None,
reply_token=event.reply_token,
raw_args=cmd_args,
)
cmd_reply = await cmd_router.dispatch(cmd, cmd_args, cmd_ctx)
if cmd_reply and event.reply_token:
try:
await reply_text(event.reply_token, cmd_reply)
except Exception:
await push_text(line_user_id, cmd_reply)
return

# 受限模式 AI 處理
try:
await reply_text(
event.reply_token,
"請先在 CTOS 系統綁定您的 Line 帳號才能使用此服務。\n\n"
"步驟:\n"
"1. 登入 CTOS 系統\n"
"2. 進入 Line Bot 管理頁面\n"
"3. 點擊「綁定 Line 帳號」產生驗證碼\n"
"4. 將驗證碼發送給我完成綁定",
# 取得使用者顯示名稱
display_name = None
user_row = await get_line_user_record(
line_user_id, "display_name"
)
if user_row:
display_name = user_row["display_name"]

reply = await handle_restricted_mode(
content=content,
platform_user_id=line_user_id,
bot_user_id=str(user_uuid),
is_group=is_group,
line_group_id=group_uuid,
message_uuid=message_uuid,
user_display_name=display_name,
)
if reply:
# reply_token 可能在長時間 AI 處理後過期,
# 先嘗試 reply,失敗則 fallback 到 push
if event.reply_token:
try:
await reply_text(event.reply_token, reply)
except Exception:
await push_text(line_user_id, reply)
else:
await push_text(line_user_id, reply)
except Exception as e:
logger.warning(f"回覆未綁定訊息失敗: {e}")
# 群組對話:靜默不回應
logger.error(f"受限模式 AI 處理失敗: {e}", exc_info=True)
try:
await push_text(line_user_id, "抱歉,處理訊息時發生錯誤,請稍後再試。")
except Exception as push_e:
logger.warning(f"推送錯誤訊息失敗: {push_e}")
# silent: 群組靜默忽略

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

這個 elif route_result.action == "restricted": 區塊的邏輯相當複雜且長,在單一函式中混合了指令處理和 AI 處理兩種不同的路徑。

為了提高可讀性和可維護性,建議將這段邏輯提取到一個獨立的輔助函式中,例如 _handle_restricted_mode_event。這樣可以讓 process_message_event 的主流程更清晰,只負責分派事件,而將受限模式的具體處理細節封裝起來。

雖然無法在此提供可直接套用的程式碼建議來新增函式,但重構的方向是將 lines 280-341 的內容移至一個新的 async def _handle_restricted_mode_event(...) 函式中,並在原處呼叫它。

Comment on lines 434 to 449
if bot_user_id:
try:
async with get_connection() as conn:
row = await conn.fetchrow(
"SELECT user_id FROM bot_users WHERE id = $1",
bot_user_id,
)
if row and row["user_id"]:
ctos_user_id = row["user_id"]
user_info = await get_user_role_and_permissions(ctos_user_id)
is_admin = user_info["role"] == "admin"
except Exception as e:
logger.error(f"查詢用戶綁定狀態失敗: {e}", exc_info=True)

ctx = CommandContext(
platform_type="telegram",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

這段用於查詢用戶綁定狀態和管理員身份的邏輯,與 backend/src/ching_tech_os/services/linebot_ai.py 中 L1420-1430 的程式碼非常相似。

為了避免程式碼重複並提高可維護性,建議將這段邏輯提取到一個共用的輔助函式中,例如 _get_command_user_context(bot_user_id)。這個函式可以回傳一個包含 ctos_user_idis_admin 的元組或字典。

這樣一來,linebot_ai.pybot_telegram/handler.py 都可以呼叫這個共用函式來建構 CommandContext,從而簡化程式碼並確保兩邊的邏輯一致。

yazelin and others added 2 commits February 26, 2026 23:47
- H1: Command dispatch reply falls back to push_text when reply_token
  expires (e.g., /debug taking 3+ minutes)
- M2: /debug command now records AI logs via log_linebot_ai_call with
  context_type "bot-debug"; make message_uuid param accept None
- M3: Merge duplicate get_line_user_record queries in handle_text_message
  into a single call before command parse, reused for both paths

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract the "bot_user_id → ctos_user_id + is_admin" query pattern
into a shared function in bot/commands.py, replacing the inline SQL
in Telegram handler. Addresses Gemini review suggestion to deduplicate
user context lookup across platforms.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@yazelin yazelin merged commit 80b195f into main Feb 26, 2026
2 checks passed
@yazelin yazelin deleted the openspec/bot-multi-mode-platform branch February 26, 2026 15:59
yazelin added a commit that referenced this pull request Feb 26, 2026
Add /agent slash command that allows admins to switch the AI agent used
in a specific group or personal chat. Each group/user stores their own
preference independently via active_agent_id FK on bot_users/bot_groups.

Also fix MCP tool permission issue in bypassPermissions mode where
ctos_user_id was not injected into tool parameters, causing "permission
denied" errors for search_nas_files and read_document.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant