mirror of
https://github.com/ReCloudStudio/WebHooker.git
synced 2026-09-22 16:11:29 +00:00
feat(check_run): edit messages in place as the run progresses, like workflow_run
formatCheckRun now sets a stable updateKey (check_run:{repo}:{id}) so queued → in_progress → completed phases update the original message via KV msg:* tracking instead of posting new ones. Adds tests and syncs README/docs.
This commit is contained in:
parent
bf76b9342f
commit
b35c2c2f90
13 changed files with 31 additions and 15 deletions
|
|
@ -104,7 +104,7 @@ src/__tests__/ # bun test unit tests (webhook, formatter, discord, te
|
|||
- Mention Discord roles on route trigger: route-level `discordRoleIds` are rendered as `<@&id>` into the Discord message `content` (Telegram targets ignore the field)
|
||||
- Format 28 event types as platform-neutral messages (Discord embeds + Telegram HTML)
|
||||
- Route messages to Discord channels/threads and Telegram chats/topics via REST
|
||||
- Edit already-sent messages in place for `workflow_run` progress (stable `updateKey`, KV `msg:*` tracking)
|
||||
- Edit already-sent messages in place for `workflow_run` / `check_run` progress (stable `updateKey`, KV `msg:*` tracking)
|
||||
- Record every dispatch attempt to D1 `send_logs` (route id, event, target, ok/error, duration, error code)
|
||||
- Serve `/gh` slash commands + message context-menu commands + PR merge/close buttons + comment modals
|
||||
- Serve Telegram `/gh` commands (login/logout/comment/merge/close) via reply-message parsing
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ GitHub / Gitea webhook → Discord / Telegram dispatcher. Receives webhook event
|
|||
- Filter by event type, repo, actor, action, branch, keyword (supports regex)
|
||||
- Rich messages with color coding, author avatars, fields, and timestamps — rendered as Discord embeds and Telegram HTML
|
||||
- Route to Discord channels/threads and Telegram chats/topics (multi-target routes)
|
||||
- `workflow_run` progress is edited **in place** (single message updated as the workflow advances) on both platforms
|
||||
- `workflow_run` / `check_run` progress is edited **in place** (single message updated as the run advances) on both platforms
|
||||
- GitHub OAuth for user actions (comment, edit comment, delete comment, merge, close, react)
|
||||
- **Web UI config console** (`/admin`) — manage routes and groups with GitHub OAuth + admin whitelist, view send logs
|
||||
- **Discord Interactions Endpoint** (Ed25519-verified) for `/gh` slash commands, message context-menu commands, PR merge/close buttons, and comment modals
|
||||
|
|
@ -333,7 +333,7 @@ npm test # Unit tests (bun test)
|
|||
| `status` | Commit status, context, state |
|
||||
| `deployment` | Environment, ref, task |
|
||||
| `deployment_status` | Environment, status, commit ref |
|
||||
| `check_run` | Status, conclusion, details URL |
|
||||
| `check_run` | Status, conclusion, details URL (edited in place) |
|
||||
| `check_suite` | Suite conclusion, head branch, commit |
|
||||
| `ping` | Webhook confirmation |
|
||||
| `release` | Tag, body, assets |
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ GitHub / Gitea webhook → Discord / Telegram 分发服务。通过 Cloudflare W
|
|||
- 按事件类型、仓库、操作人、操作、分支、关键词(支持正则)过滤
|
||||
- 富消息:颜色编码、作者头像、字段、时间戳——渲染为 Discord embed 与 Telegram HTML
|
||||
- 路由到 Discord 频道/子区与 Telegram 群组/话题(一条路由可多目标)
|
||||
- `workflow_run` 进度**原地编辑**同一条消息(工作流推进时更新),两个平台均支持
|
||||
- `workflow_run` / `check_run` 进度**原地编辑**同一条消息(运行推进时更新),两个平台均支持
|
||||
- GitHub OAuth 用户授权(评论、编辑评论、删除评论、合并、关闭、反应)
|
||||
- **Web 配置控制台**(`/admin`)— 通过 GitHub OAuth + 管理员白名单管理路由与分组、查看发送日志
|
||||
- **Discord Interactions Endpoint**(Ed25519 验签)支持 `/gh` 斜杠命令、消息右键菜单命令、PR 合并/关闭按钮与评论 modal
|
||||
|
|
@ -332,7 +332,7 @@ npm test # 单元测试(bun test)
|
|||
| `status` | 提交状态、上下文、状态值 |
|
||||
| `deployment` | 环境、引用、任务 |
|
||||
| `deployment_status` | 环境、状态、commit ref |
|
||||
| `check_run` | 状态、结论、详情链接 |
|
||||
| `check_run` | 状态、结论、详情链接(原地更新) |
|
||||
| `check_suite` | 套件结论、head 分支、提交 |
|
||||
| `ping` | Webhook 确认 |
|
||||
| `release` | Tag、内容、资产 |
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ WebHooker supports 28 GitHub webhook event types, each with a dedicated formatte
|
|||
| `status` | Commit status updated | Commit status, context, state, commit link |
|
||||
| `deployment` | Deployment created | Environment, ref, task |
|
||||
| `deployment_status` | Deployment status updated | Environment, status, commit ref |
|
||||
| `check_run` | Check run completed | Status, conclusion, details URL |
|
||||
| `check_run` | Check run phase updated | Status, conclusion, details URL; phases update a single message in place |
|
||||
| `check_suite` | Check suite completed | Suite conclusion, head branch, commit link |
|
||||
| `ping` | Webhook confirmation | Webhook confirmation, event types subscribed |
|
||||
| `release` | Release published/edited | Tag, body, assets, pre-release flag |
|
||||
|
|
@ -61,7 +61,7 @@ Any event type without a dedicated formatter falls through to the generic format
|
|||
|
||||
## In-Place Message Updates
|
||||
|
||||
`workflow_run` events (queued → running → success/failure) are sent once and then **edited in place** for each subsequent phase instead of posting a new message. The original message's link preview, author, and field layout are preserved; only the status, conclusion emoji, duration, and title are refreshed. Supported on both Discord (`editMessage`) and Telegram (`editMessageText` / `editMessageCaption`).
|
||||
`workflow_run` and `check_run` events (queued → running → success/failure) are sent once and then **edited in place** for each subsequent phase instead of posting a new message. The original message's link preview, author, and field layout are preserved; only the status, conclusion emoji, duration, and title are refreshed. Supported on both Discord (`editMessage`) and Telegram (`editMessageText` / `editMessageCaption`).
|
||||
|
||||
## Filter Compatibility
|
||||
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ Filters accept either a single string or an array of strings:
|
|||
| `invite:{token}` | `{ groupId, role, expiresAt, createdBy, note? }` | 7 days |
|
||||
| `invite:group:{id}` | Token index per group (keeps invite listing consistent) | Permanent |
|
||||
| `delivery:{id}` | Webhook delivery id (dedup marker) | 300 seconds |
|
||||
| `msg:{routeId}:{key}:{target}` | Message id tracking for in-place updates (e.g. `workflow_run`) | 7 days |
|
||||
| `msg:{routeId}:{key}:{target}` | Message id tracking for in-place updates (e.g. `workflow_run` / `check_run`) | 7 days |
|
||||
| `cmd:guild:{id}` | Guild id whose commands were registered (dedup) | Permanent |
|
||||
| `cmd:registered:global` | Global command registration marker (dedup) | 1 day |
|
||||
| `config:discord-app-id` | Cached Discord application id | Permanent |
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ GitHub / Gitea Webhook → Cloudflare Worker (Hono)
|
|||
4. Worker parses the event type and normalizes the payload to a GitHub-shaped event
|
||||
5. Routes are evaluated against filters (event, repo, actor, action, branch, keyword) and group owner restrictions
|
||||
6. Matching routes trigger formatter functions that produce platform-neutral messages
|
||||
7. Each message is sent to its route's target(s) via the Discord or Telegram REST API with rate-limit retry; `workflow_run` progress is edited in place. Every attempt is recorded in the D1 send log
|
||||
7. Each message is sent to its route's target(s) via the Discord or Telegram REST API with rate-limit retry; `workflow_run` / `check_run` progress is edited in place. Every attempt is recorded in the D1 send log
|
||||
|
||||
## Tech Stack
|
||||
|
||||
|
|
|
|||
|
|
@ -25,5 +25,5 @@ features:
|
|||
- title: Signature Verification
|
||||
details: Provider-aware HMAC-SHA256 webhook signature verification (GitHub X-Hub-Signature-256, Gitea X-Gitea-Signature) and Ed25519 interaction signature verification using the Web Crypto API with timing-safe comparison.
|
||||
- title: In-Place Updates
|
||||
details: workflow_run progress is edited in place on a single message as the run advances, on both Discord and Telegram.
|
||||
details: workflow_run and check_run progress are edited in place on a single message as the run advances, on both Discord and Telegram.
|
||||
---
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ WebHooker 支持 28 种 GitHub webhook 事件类型,每种都有专用的格
|
|||
| `status` | 提交状态更新 | 提交状态、上下文、状态值、提交链接 |
|
||||
| `deployment` | 部署已创建 | 环境、引用、任务 |
|
||||
| `deployment_status` | 部署状态更新 | 环境、状态、提交引用 |
|
||||
| `check_run` | 检查运行完成 | 状态、结论、详情 URL |
|
||||
| `check_run` | 检查运行阶段更新 | 状态、结论、详情 URL;各阶段原地更新同一条消息 |
|
||||
| `check_suite` | 检查套件完成 | 套件结论、head 分支、提交链接 |
|
||||
| `ping` | Webhook 确认 | Webhook 确认、已订阅的事件类型 |
|
||||
| `release` | 发布创建/编辑 | 标签、内容、附件、预发布标记 |
|
||||
|
|
@ -61,7 +61,7 @@ WebHooker 支持 28 种 GitHub webhook 事件类型,每种都有专用的格
|
|||
|
||||
## 原地消息更新
|
||||
|
||||
`workflow_run` 事件(queued → running → success/failure)只发送一条消息,后续每个阶段会**原地编辑**该消息,而不是发送新消息。消息的链接预览、作者和字段布局保持不变,仅刷新状态、结论 emoji、耗时和标题。Discord(`editMessage`)和 Telegram(`editMessageText` / `editMessageCaption`)均支持。
|
||||
`workflow_run` / `check_run` 事件(queued → running → success/failure)只发送一条消息,后续每个阶段会**原地编辑**该消息,而不是发送新消息。消息的链接预览、作者和字段布局保持不变,仅刷新状态、结论 emoji、耗时和标题。Discord(`editMessage`)和 Telegram(`editMessageText` / `editMessageCaption`)均支持。
|
||||
|
||||
## 过滤器兼容性
|
||||
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ owner(及超级管理员)可在分组的「成员」面板创建一次性邀
|
|||
| `invite:{token}` | `{ groupId, role, expiresAt, createdBy, note? }` | 7 天 |
|
||||
| `invite:group:{id}` | 每组的 Token 索引(保证邀请列表一致性) | 永久 |
|
||||
| `delivery:{id}` | Webhook 投递 id(去重标记) | 300 秒 |
|
||||
| `msg:{routeId}:{key}:{target}` | 原地更新用消息 id 追踪(如 `workflow_run`) | 7 天 |
|
||||
| `msg:{routeId}:{key}:{target}` | 原地更新用消息 id 追踪(如 `workflow_run` / `check_run`) | 7 天 |
|
||||
| `cmd:guild:{id}` | 已注册命令的服务器 id(去重标记) | 永久 |
|
||||
| `cmd:registered:global` | 全局命令已注册标记(24h 去重) | 1 天 |
|
||||
| `config:discord-app-id` | Discord 应用 id 缓存 | 永久 |
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ GitHub / Gitea Webhook → Cloudflare Worker (Hono)
|
|||
4. Worker 解析事件类型并将载荷归一化为 GitHub 形状的事件
|
||||
5. 根据过滤器(event、repo、actor、action、branch、keyword)与分组所有者限制评估路由
|
||||
6. 匹配的路由触发格式化器函数生成平台中立消息
|
||||
7. 每条消息通过 Discord 或 Telegram REST API 发送到对应路由的目标,并处理速率限制重试;`workflow_run` 进度原地更新。每次尝试都记录到 D1 发送日志
|
||||
7. 每条消息通过 Discord 或 Telegram REST API 发送到对应路由的目标,并处理速率限制重试;`workflow_run` / `check_run` 进度原地更新。每次尝试都记录到 D1 发送日志
|
||||
|
||||
## 技术栈
|
||||
|
||||
|
|
|
|||
|
|
@ -25,5 +25,5 @@ features:
|
|||
- title: 签名验证
|
||||
details: 使用 Web Crypto API 进行 HMAC-SHA256 webhook 签名验证与 Ed25519 交互签名验证,支持时间安全比较。
|
||||
- title: 原地更新
|
||||
details: workflow_run 进度在运行推进时于同一条消息上原地更新,Discord 与 Telegram 均支持。
|
||||
details: workflow_run / check_run 进度在运行推进时于同一条消息上原地更新,Discord 与 Telegram 均支持。
|
||||
---
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ describe("message title spec", () => {
|
|||
|
||||
it("check_run uses status for queued and running", () => {
|
||||
const checkRun = {
|
||||
id: 42,
|
||||
name: "Lint",
|
||||
html_url: "https://github.com/acme/widget/runs/1",
|
||||
conclusion: null as string | null,
|
||||
|
|
@ -148,6 +149,19 @@ describe("message title spec", () => {
|
|||
);
|
||||
expect(msg.title).toBe("acme/widget: Lint — running");
|
||||
expect(msg.fields![0].value).toBe("🔄 running");
|
||||
expect(msg.updateKey).toBe("check_run:acme/widget:42");
|
||||
});
|
||||
|
||||
it("check_run omits updateKey without a run id", () => {
|
||||
const msg = formatEvent(
|
||||
route,
|
||||
event("check_run", {
|
||||
check_run: { name: "Lint", status: "completed", conclusion: "success" },
|
||||
repository: repo,
|
||||
sender,
|
||||
}),
|
||||
);
|
||||
expect(msg.updateKey).toBeUndefined();
|
||||
});
|
||||
|
||||
it("check_suite shows conclusion, service, branch and commit", () => {
|
||||
|
|
|
|||
|
|
@ -164,6 +164,7 @@ export function formatCheckRun(
|
|||
showEmoji: boolean,
|
||||
): NeutralMessage {
|
||||
const checkRun = payload.check_run as {
|
||||
id?: number;
|
||||
name?: string;
|
||||
conclusion?: string;
|
||||
html_url?: string;
|
||||
|
|
@ -213,6 +214,7 @@ export function formatCheckRun(
|
|||
url: checkRun.html_url,
|
||||
color: GITHUB_COLORS[colorKey],
|
||||
fields,
|
||||
updateKey: repo && checkRun.id != null ? `check_run:${repo}:${checkRun.id}` : undefined,
|
||||
},
|
||||
t,
|
||||
repo,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue