diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e25c1f..4300739 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ ### 修复 - **WebSocket 初始化**: 修复了 `@larksuiteoapi/node-sdk` v1.56.0+ 中 WebSocket 初始化不正确的 `TypeError`。现在正确使用了 `WSClient` 类并修复了参数类型错误。 - **事件处理**: 修正了 `im.chat.member.bot.added_v1` 事件的 Payload 解析逻辑。 +- **Hono 兼容性**: 修正了 `feishu-event.ts` 中 `lark.adaptDefault` 的错误用法。改为使用手动 Challenge 处理和 `eventDispatcher.invoke`,解决了与 Hono 请求/响应对象的兼容性问题引发的编译错误。 - **群聊解绑**: 增加对 `im.chat.member.bot.deleted_v1` 事件的支持。当机器人被移除群聊时,自动清理 `known_group_chats` 和 `topic_group_chats` 关联,确保订阅关系自动解绑。 ### 新增 diff --git a/apps/server/src/api/feishu-event.ts b/apps/server/src/api/feishu-event.ts index 30630af..69ae257 100644 --- a/apps/server/src/api/feishu-event.ts +++ b/apps/server/src/api/feishu-event.ts @@ -5,32 +5,39 @@ import { eventDispatcher } from '../event-handler'; const feishuEvent = new Hono(); // Helper to adapt Hono request to Lark SDK request -const adaptRequest = async (c: any) => { - const headers = c.req.raw.headers; - // Convert Headers object to Record - const headerRecord: Record = {}; - headers.forEach((value: string, key: string) => { - headerRecord[key] = value; - }); - - return { - headers: headerRecord, - body: await c.req.json(), - }; -}; feishuEvent.post('/', async (c) => { try { - const req = await adaptRequest(c); + const headers = c.req.raw.headers; + const headerRecord: Record = {}; + headers.forEach((value, key) => { + headerRecord[key] = value; + }); - // Use the official SDK to handle the request - // It handles URL verification, encryption, and dispatching - const res = await lark.adaptDefault('/api/feishu/event', req, eventDispatcher, { - autoChallenge: true, - encryptKey: process.env.FEISHU_ENCRYPT_KEY - }) as any; + const body = await c.req.json(); + const req = { + headers: headerRecord, + body, + }; - return c.json(res?.body || {}); + // Use the official SDK functions directly for Hono compatibility + // 1. Handle URL verification (Challenge) + const { isChallenge, challenge } = lark.generateChallenge(body, { + encryptKey: process.env.FEISHU_ENCRYPT_KEY || '' + }); + + if (isChallenge) { + return c.json(challenge); + } + + // 2. Dispatch event + // The dispatcher expects an object containing headers and body + const result = await eventDispatcher.invoke({ + ...req.body, + headers: req.headers + }); + + return c.json(result || {}); } catch (e) { console.error('[Feishu Event] Error:', e); return c.json({ error: 'Internal Server Error' }, 500); diff --git a/docs/copilot-context.md b/docs/copilot-context.md index 6ff79ca..7ed59f2 100644 --- a/docs/copilot-context.md +++ b/docs/copilot-context.md @@ -69,6 +69,23 @@ The database schema is defined in `apps/server/src/db/schema.ts`. - `name`: Group name. - `lastActiveAt`: Timestamp of last event from this group. - **Purpose**: Caches groups the bot has been added to, facilitating easy selection in the UI. +7. **Alert Task** (`alert_tasks`) + - `id`: UUID (Primary Key). + - `topicSlug`: The slug of the target topic (or `NULL` for DM). + - `senderId`: Foreign Key -> `users.id` (who triggered the webhook). + - `status`: `pending`, `processing`, `completed`, or `failed`. + - `recipientCount`: Total recipients (subscribers + groups). + - `successCount`: Number of successful deliveries. + - `payload`: Snapshot of the incoming webhook body (JSONB). + - `error`: Last error message if failed. + - **Purpose**: Tracks the lifecycle of a single alert ingestion events. + +8. **Alert Log** (`alert_logs`) + - `id`: UUID (Primary Key). + - `taskId`: Foreign Key -> `alert_tasks.id`. + - `userId`: Target user open_id (snapshot). + - `status`: `sent` or `failed`. + - **Purpose**: Granular tracking for each individual delivery within a task. ## 4. Key Workflows @@ -157,6 +174,7 @@ The database schema is defined in `apps/server/src/db/schema.ts`. ### Feishu Event - `POST /api/feishu/event`: Endpoint for receiving Feishu events (Webhook mode). + - **Note**: This endpoint uses **manual challenge handling** (`lark.generateChallenge`) and `eventDispatcher.invoke` instead of the SDK's `adaptDefault` to maintain compatibility with Hono's non-standard Node.js response object. ### Webhook - `POST /api/webhook/:token/topic/:slug`: Trigger an alert for a topic.