Agent: 探索者 (
explorer)
Confidence: 73%
Cost: $0.6204
Duration: 2m 1s
Task ID:f7a580ae-2001-4797-9668-002a9a7583be
好的,我現在已經收集了足夠的資訊。讓我整理成探索報告。
探索主題
grammY Bot 框架的中介軟體設計模式:如何寫出高效、可測試的 Telegram Bot 中介軟體?
發現
我們目前的 middleware 是扁平函式、直接掛在 bot 上(
src/telegram/middleware/),4 個 middleware 各自是獨立函式,透過setupMiddleware(bot)依序bot.use()。這是最基本的 stack 模式,但完全沒用到Composer的樹狀分支能力。grammY 的
Composer類別是中介軟體的核心,Bot本身就是Composer的子類別。每個Composer實例可以獨立組合 middleware,再掛進 bot——這是模組化的關鍵。官方推薦每個功能模組 export 一個Composer,然後在bot.ts集中bot.use(module)。我們的
CommandRegistry自建了路由邏輯(intent patterns、alias matching、callback routing),但沒有利用 grammY 原生的bot.route()或 Router plugin。這導致路由分散在command-registry.ts、plugin-router.ts、message-handler.ts三處,增加了理解和測試難度。可測試性瓶頸:目前的 middleware 函式直接 import
config、logger等模組級 singleton。grammY 官方測試方式是構造 mockContext物件 + 呼叫 middleware 函式——但我們的 middleware 與 config singleton 耦合,mock 需要vi.mock()整個模組。改用 Composer + 依賴注入工廠模式(如createAuthMiddleware(config))可以大幅提升可測試性。grammY 的
Composer.filter()+ chain 模式能取代我們的手動 if-return 邏輯。例如authMiddleware的白名單檢查可以用composer.filter(ctx => isAllowed(ctx)).use(next)表達,讓 grammY 的樹狀調度自動處理分支,而不是手動呼叫next()或return Promise.resolve()。
有趣的連結
- grammY 的 middleware 是樹狀結構(不是簡單 stack),每次
.use()回傳的Composer可以繼續 chain——這比 Express 的線性 stack 更有表達力。我們的CommandRegistry.bindToBot()本質上在重新實作 grammY 已經提供的功能(bot.command()、bot.on('callback_query:data')),但失去了 filter narrowing 帶來的型別安全。
延伸問題
- 重構路線:能否將
CommandRegistry重構為多個Composer模組(auth-composer、rate-limit-composer、command-composer、plugin-composer),每個可獨立 unit test?具體步驟是什麼? - middleware 測試模式:如何在 vitest 中構造最小的 grammY mock
Context,不需要真正的 Bot 實例就能測試單一 middleware?
重要性:4/5
(能改善專案品質——目前 middleware 層缺少測試、路由邏輯分散、Composer 能力未被利用。重構後可以提升可維護性和測試覆蓋率。)
★ Insight ─────────────────────────────────────
關鍵技術洞察:
- Composer 是一等公民:grammY 的設計哲學是「每個功能模組就是一個 Composer」。我們目前把所有東西掛在一個
Bot實例上,等於把所有邏輯塞進一棵樹的根節點。正確做法是const authModule = new Composer<BotContext>()→ 獨立開發 →bot.use(authModule)。 - Filter chain 取代 if-return:
composer.filter(predicate).use(handler)不只更簡潔,還讓 TypeScript 自動 narrow context type(例如ctx.message保證存在)。我們的authMiddleware手動if (!userId) return next()失去了這個型別安全優勢。 - 測試策略:最小 mock 是
{ from: { id: 123 }, chat: { id: 456 }, update: { update_id: 1 }, reply: vi.fn() } as unknown as BotContext,傳入 middleware 函式 +vi.fn()作為next。Composer 模式讓這變得更乾淨——直接測試 Composer 的 middleware 函式,不需要整個 Bot。─────────────────────────────────────────────────