feat: polish group binding

Signed-off-by: d0zingcat <iamtangli42@gmail.com>
This commit is contained in:
2026-01-14 17:29:50 +08:00
parent 75306a67b1
commit ee67cefb4d
8 changed files with 32 additions and 12 deletions

View File

@@ -10,7 +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 请求/响应对象的兼容性问题引发的编译错误
- **Hono 兼容性**: 修正了 `feishu-event.ts``lark.adaptDefault` 的错误用法。改为使用手动 Challenge 处理和 `eventDispatcher.invoke`并通过原型链注入 Header 解决了与 Hono 请求/响应对象的兼容性以及签名校验失败的问题。
- **群聊解绑**: 增加对 `im.chat.member.bot.deleted_v1` 事件的支持。当机器人被移除群聊时,自动清理 `known_group_chats``topic_group_chats` 关联,确保订阅关系自动解绑。
### 新增

View File

@@ -13,7 +13,16 @@
支持通过 **Topic (主题)** 订阅模式分发告警,同时也提供 **Personal Inbox (个人信箱)** 功能,无需创建话题即可快速给自己推送消息。
![Topics View](docs/images/topics_view.png)
### 2. 管理员看板 (Live Stats)
除了个人订阅外,您可以将 Topic 绑定至多个**飞书群聊**。
> [!TIP]
> **群聊发现**:请先将机器人邀请进入目标群聊。机器人入群后会触发自动感应,此时刷新管理页面即可在下拉菜单中看到并绑定该群组。
### 2. 群聊告警分发
支持将机器人加入飞书群聊,并将话题绑定到群聊中,实现告警的群组广播。
![Group Binding](docs/images/group_binding.png)
![Group Alert](docs/images/group_alert.png)
### 3. 管理员看板 (Live Stats)
实时追踪全系统的告警负载、分发成功率以及各话题的热度。
![Admin Dashboard](docs/images/admin_dashboard.png)
@@ -22,11 +31,13 @@
## 🔥 核心特性
- **🚀 极简推送 (Personal Inbox)**: 每个用户拥有专属的 Webhook Token直接向 `/dm` 接口发送即可在飞书收到私聊,零配置成本。
- **📢 主题订阅 (Topic Model)**: 灵活的“发布-订阅”机制。告警发送至 Topic系统自动分发给所有订阅成员,避免群聊骚扰
- **📢 主题订阅 (Topic Model)**: 灵活的“发布-订阅”机制。告警发送至 Topic系统自动分发给所有订阅成员。
- **👥 群聊分发 (Group Support)**: 告警可同步分发至绑定的飞书群聊,支持机器人自动发现与解绑。
- **🛡️ 权限与审计**:
- 话题创建需经过管理员审批。
- 记录完整的 `Alert Task` 日志,包含发送者、时间、审批人及分发成功率
- 记录完整的 `Alert Task` 日志,实现发送链路可追溯
- **📊 实时看板**: Grafana 风格的监控界面,直观展示系统运行健壮性。
- **🔌 长连接模式 (WebSocket)**: 支持飞书开放平台长连接,无需公网 IP 或域名即可在内网环境接收事件回调。
- **⚡ 高性能架构**: 基于 Bun + Hono 的全异步架构,毫秒级分发延迟。
---

View File

@@ -31,11 +31,11 @@ feishuEvent.post('/', async (c) => {
}
// 2. Dispatch event
// The dispatcher expects an object containing headers and body
const result = await eventDispatcher.invoke({
...req.body,
headers: req.headers
});
// The dispatcher expects an object containing headers and body.
// We use Object.create to put headers on the prototype so they are accessible
// but not included in JSON.stringify, which preserves signature verification.
const payload = Object.assign(Object.create({ headers: headerRecord }), body);
const result = await eventDispatcher.invoke(payload);
return c.json(result || {});
} catch (e) {

View File

@@ -4,7 +4,10 @@ import { eq } from 'drizzle-orm';
import * as lark from '@larksuiteoapi/node-sdk';
import { logger } from './lib/logger';
export const eventDispatcher = new lark.EventDispatcher({}).register({
export const eventDispatcher = new lark.EventDispatcher({
encryptKey: process.env.FEISHU_ENCRYPT_KEY,
verificationToken: process.env.FEISHU_VERIFICATION_TOKEN,
}).register({
'im.chat.member.bot.added_v1': async (data) => {
const { chat_id, name } = data as any;
logger.info({ chat_id, name }, '[Feishu Event] Bot added to group');

View File

@@ -175,6 +175,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.
- **Signature Verification Hack**: To preserve Feishu's signature verification, the internal `invoke` call uses `Object.create({ headers })` to inject HTTP headers on the prototype of the payload object. This ensures headers are accessible to the SDK's internal verification logic but are **excluded** from `JSON.stringify`, which is critical for matching the SHA256 content checksum.
### Webhook
- `POST /api/webhook/:token/topic/:slug`: Trigger an alert for a topic.
@@ -183,9 +184,9 @@ The database schema is defined in `apps/server/src/db/schema.ts`.
## 6. Future Roadmap (Planned)
- [ ] **Message Preview**: Preview Feishu card JSON in the UI.
- [ ] **History/Logs**: Keep a log of sent alerts for auditing.
- [x] **History/Logs**: Tracking for sent alerts (Alert Tasks/Logs).
- [ ] **Retry Mechanism**: Handle Feishu API failures.
- [x] **Deployment**: Dockerfile and deployment scripts.
- [x] **Deployment**: Dockerfile and CI/CD.
## 7. Development Conventions
@@ -201,6 +202,11 @@ The database schema is defined in `apps/server/src/db/schema.ts`.
- **Environment Isolation**:
- Each workspace (`apps/server`, `apps/web`) uses its own `.env` file via Bun's `--env-file .env` flag.
- Development proxy target for the frontend is configurable via `VITE_API_URL` (default: `http://localhost:3000`).
- **Critical Environment Variables**:
- `FEISHU_ENCRYPT_KEY`: Essential for the `lark.generateChallenge` and event signature verification.
- `FEISHU_VERIFICATION_TOKEN`: Used by `EventDispatcher` for event authentication.
- `FEISHU_USE_WS`: Set to `true` to enable WebSocket mode (bypasses `feishu-event.ts`).
- `ADMIN_EMAILS`: Comma-separated list of emails that automatically receive `isAdmin=true` upon first login.
- **CI/CD**:
- GitHub Actions automates building a multi-stage Docker image and pushing it to GitHub Container Registry (GHCR).
- Image path: `ghcr.io/${USER}/alert-message-center`.

BIN
docs/images/group_alert.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

After

Width:  |  Height:  |  Size: 409 KiB