mirror of
https://github.com/ReCloudStudio/WebHooker.git
synced 2026-09-22 16:11:29 +00:00
docs: fix factual errors, fill coverage gaps and align zh mirror
This commit is contained in:
parent
41ad1a036b
commit
db49e1f01c
35 changed files with 912 additions and 316 deletions
62
docs/guide/commands.md
Normal file
62
docs/guide/commands.md
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
# Bot Commands
|
||||
|
||||
After [linking your GitHub account](#linking-your-account), you can act on GitHub **as yourself** from both Discord and Telegram — comments are posted with your own OAuth token and GitHub enforces permission. If GitHub rejects an action (e.g. editing someone else's comment) the bot tells you so.
|
||||
|
||||
## Linking Your Account
|
||||
|
||||
You must link your GitHub account once before using any command:
|
||||
|
||||
| Platform | Command | Effect |
|
||||
|----------|-----------------------------------|------------------------------------------------------------------|
|
||||
| Discord | `/gh login` | Returns an ephemeral OAuth link to authorize your GitHub account |
|
||||
| Discord | `/gh logout` | Unlinks your GitHub account |
|
||||
| Telegram | `/gh login` (reply to a message) | Returns an OAuth link |
|
||||
| Telegram | `/gh logout` (reply to a message) | Unlinks your GitHub account |
|
||||
|
||||
The link is stored server-side (KV) and mapped to your Discord/Telegram user id in D1.
|
||||
|
||||
## Discord
|
||||
|
||||
Discord commands are **slash commands** and **message context-menu commands**, synced by the scheduled trigger (every 5 minutes): per-guild for instant availability, and globally (24h dedup, ~1h propagation). All replies are ephemeral (only you see them).
|
||||
|
||||
### Comment on an issue / PR
|
||||
|
||||
Two equivalent ways:
|
||||
|
||||
- **Right-click a notification** (recommended): right-click a bot-issued issue / PR / comment notification → **Apps** → **GitHub: 添加评论 / 编辑评论 / 删除评论**. The target is auto-extracted from the notification embed; no link needed.
|
||||
- **Slash command with a link**:
|
||||
|
||||
```
|
||||
/gh comment add link:<issue or PR url> e.g. https://github.com/owner/repo/issues/123
|
||||
/gh comment edit link:<comment url> url must contain #issuecomment-<id>
|
||||
/gh comment del link:<comment url> url must contain #issuecomment-<id>
|
||||
```
|
||||
|
||||
For `edit` / `del`, copy the specific comment link on GitHub (comment ⋯ menu → **Copy link**). `add` / `edit` open a modal to enter/adjust the comment body (prefilled for edit).
|
||||
|
||||
### Merge / close a PR
|
||||
|
||||
Notifications for open PRs include **合并 / 关闭** (merge/close) buttons:
|
||||
|
||||
- Clicking a button merges (squash) or closes the PR as your linked GitHub account; GitHub enforces permission.
|
||||
- On success the buttons are removed from the notification and the result is shown in an ephemeral reply.
|
||||
|
||||
### Requirements
|
||||
|
||||
| Item | How |
|
||||
|--------------|----------------------------------------------------------------------------------------------------|
|
||||
| Public key | `DISCORD_PUBLIC_KEY` set + Interactions Endpoint URL configured |
|
||||
| Invite scope | Bot invited with `applications.commands` (see [Discord Bot Setup](./deployment#discord-bot-setup)) |
|
||||
| OAuth | `GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET` and `BASE_URL` configured |
|
||||
| User linked | Each user runs `/gh login` first |
|
||||
|
||||
## Telegram
|
||||
|
||||
Telegram `/gh` commands work by **replying to a notification message**:
|
||||
|
||||
- `/gh login` — link your GitHub account (returns an OAuth link)
|
||||
- `/gh logout` — unlink
|
||||
- `/gh comment <text>` — reply to an issue/PR notification to comment as yourself
|
||||
- `/gh merge` / `/gh close` — reply to a PR notification to merge/close it
|
||||
|
||||
The target issue/PR is parsed from the message you reply to (the notification embed links). Commands arrive via the Telegram webhook (`POST /telegram/webhook`, optionally verified with `TELEGRAM_WEBHOOK_SECRET`).
|
||||
|
|
@ -7,7 +7,7 @@ WebHooker requires several secrets to function. For local development, store the
|
|||
### Required Secrets
|
||||
|
||||
| Variable | Description |
|
||||
| ----------------------- | ------------------------------------------------------------------------ |
|
||||
|-------------------------|--------------------------------------------------------------------------|
|
||||
| `GITHUB_WEBHOOK_SECRET` | Webhook secret from your GitHub App settings |
|
||||
| `GITEA_WEBHOOK_SECRET` | Webhook secret from your Gitea instance (only to receive Gitea webhooks) |
|
||||
| `GITHUB_CLIENT_ID` | OAuth client ID from App settings |
|
||||
|
|
@ -16,14 +16,16 @@ WebHooker requires several secrets to function. For local development, store the
|
|||
| `TELEGRAM_TOKEN` | Telegram bot token (from BotFather) — required for Telegram routes |
|
||||
|
||||
> [!NOTE]
|
||||
> `GITHUB_APP_ID` and `GITHUB_PRIVATE_KEY` are not currently used by the code — the
|
||||
> OAuth flow only needs `GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET`. They are kept
|
||||
> in the schema for compatibility in case GitHub App authentication is added later.
|
||||
> `GITHUB_APP_ID` and `GITHUB_PRIVATE_KEY` (PKCS#8 PEM) are used by the GitHub App
|
||||
> **install flow** (`/auth/github/install`) to resolve the installing account's login
|
||||
> via an App JWT. They are optional — when unset, the install page still works but
|
||||
> shows an anonymous `inst-{installationId}` group without the account name. The
|
||||
> OAuth flow itself only needs `GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET`.
|
||||
|
||||
### Optional Secrets
|
||||
|
||||
| Variable | Description | Default |
|
||||
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
|
||||
|-----------------------------|-----------------------------------------------------------------------------------------------------------------------------|-----------------------------------|
|
||||
| `DISCORD_PUBLIC_KEY` | Discord application public key (Developer Portal) — required for interactions | Unset → interactions return `401` |
|
||||
| `DISCORD_APPLICATION_ID` | Discord application id; auto-resolved when omitted | Auto-resolved |
|
||||
| `TELEGRAM_WEBHOOK_SECRET` | Secret token for `POST /telegram/webhook` verification (X-Telegram-Bot-Api-Secret-Token) | Disabled (no verification) |
|
||||
|
|
@ -32,16 +34,21 @@ WebHooker requires several secrets to function. For local development, store the
|
|||
| `ADMIN_USER_IDS` | Comma-separated GitHub user IDs (or logins) allowed to access the Web UI | Disabled |
|
||||
| `ALLOW_SELF_SIGNUP` | When enabled (`1`/`true`), GitHub users without any group access get a personal group on first login instead of `403` | Disabled |
|
||||
| `AUDIT_RETENTION_DAYS` | Audit-log retention in days for the scheduled cleanup | `90` |
|
||||
| `NUXT_PUBLIC_DOCS_URL` | Docs site URL used by the landing page (client-side runtime config) | Landing page defaults |
|
||||
| `NUXT_PUBLIC_REPO_URL` | GitHub repo URL used by the landing page | Landing page defaults |
|
||||
| `NUXT_PUBLIC_LEGAL_CONTACT` | Contact shown on `/terms` and `/privacy` | Unset → placeholder text |
|
||||
|
||||
## Webhook Providers
|
||||
|
||||
WebHooker ingests webhooks from multiple forges through the same `POST /webhook` endpoint; the provider is auto-detected from the request headers, so point every forge's webhook at `{BASE_URL}/webhook`.
|
||||
|
||||
| Provider | Event header | Signature header | Signature format | Secret |
|
||||
| -------- | ---------------- | --------------------- | -------------------------- | ----------------------- |
|
||||
|----------|------------------|-----------------------|----------------------------|-------------------------|
|
||||
| GitHub | `X-GitHub-Event` | `X-Hub-Signature-256` | `sha256=<hex>` HMAC-SHA256 | `GITHUB_WEBHOOK_SECRET` |
|
||||
| Gitea | `X-Gitea-Event` | `X-Gitea-Signature` | plain hex HMAC-SHA256 | `GITEA_WEBHOOK_SECRET` |
|
||||
|
||||
Delivery-id dedup uses `X-GitHub-Delivery` (GitHub) or `X-Gitea-Delivery` (Gitea) when present.
|
||||
|
||||
Gitea payloads are normalized to the same internal shape as GitHub events, so routes, filters, and the 28 formatters work unchanged. Unknown or unmapped Gitea events fall back to the generic formatter. Repository/commit/user links are derived from the payload's `repository.html_url`, so they point at your Gitea instance.
|
||||
|
||||
## Web UI
|
||||
|
|
@ -59,7 +66,7 @@ WebHooker ships with a built-in config console at `/admin` for managing routes i
|
|||
The console is served as an SPA at `/admin`; its tabs are deep-linkable via the URL path (`/admin/groups`, `/admin/logs`, `/admin/audit`). URLs outside `/admin` that do not match an endpoint below return a plain `404` instead of the console.
|
||||
|
||||
| Endpoint | Description |
|
||||
| ----------------------------------------------- | ----------------------------------------------------------------- |
|
||||
|-------------------------------------------------|-------------------------------------------------------------------|
|
||||
| `GET /admin` | Config console UI |
|
||||
| `GET /admin/login` | Start GitHub OAuth sign-in |
|
||||
| `GET /admin/logout` | Destroy session |
|
||||
|
|
@ -82,7 +89,7 @@ The console is served as an SPA at `/admin`; its tabs are deep-linkable via the
|
|||
| `POST /admin/api/groups/:id/webhook/regenerate` | Generate/regenerate the group webhook secret (owner) |
|
||||
| `DELETE /admin/api/groups/:id/webhook` | Disable the group webhook ingress (owner) |
|
||||
|
||||
The console lets you add, edit, delete, and toggle routes. Saved routes are written to KV `config:routes` immediately and the config cache is invalidated so the webhook pipeline picks them up on the next run.
|
||||
The console lets you add, edit, delete, and toggle routes. Saved routes are written to KV `config:routes` immediately and the config cache is invalidated so the webhook pipeline picks them up on the next run. Limits: at most **200 routes** and **100 groups** per instance.
|
||||
|
||||
## Webhook Endpoints
|
||||
|
||||
|
|
@ -124,7 +131,7 @@ Payload schema:
|
|||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
| ------------- | -------- | ---------------------------------------------------------------------------------------------------------------- |
|
||||
|---------------|----------|------------------------------------------------------------------------------------------------------------------|
|
||||
| `title` | string | Message title (falls back to "Custom message") |
|
||||
| `description` | string | Optional message body |
|
||||
| `color` | string | Optional embed color: a word (`red`, `green`, `yellow`, `blue`, `purple`, `orange`, `cyan`, `gray`) or `#rrggbb` |
|
||||
|
|
@ -139,7 +146,7 @@ Payload schema:
|
|||
|
||||
When the GitHub App is installed, its events arrive at the global endpoint for **every** installation. To keep tenants apart, bind each group to the installation id that should feed it: `"installationId": 12345678`. The id is visible in the App's installation webhook payload (`installation.id`) or on the GitHub App installation page URL. Events from any other installation are rejected for that group even if its `owners` list is empty. Groups without `installationId` keep the legacy behavior (`owners` filtering).
|
||||
|
||||
Binding is **auto-configured** — the GitHub App's _Setup URL_ should point to `{BASE_URL}/auth/github/install`. Right after a user installs the App, the browser lands there and they choose where the installation binds: a **new group** (`inst-{installationId}`, default) or any **existing group they own** (owner role checked again on submit; `POST /auth/github/install/bind` performs the provisioning). No manual id entry is needed. As a fallback (e.g. when the Setup URL is not configured), the `installation.created` webhook event creates/binds the group automatically — existing groups whose `owners` match the installing account are bound, otherwise a dedicated `inst-{installationId}` group is created. Then just add routes/members in the console.
|
||||
Binding is **auto-configured** — the GitHub App's _Setup URL_ should point to `{BASE_URL}/auth/github/install`. Right after a user installs the App, the browser lands there (the page requires a signed-in admin session — not signed in users are redirected through the OAuth flow first) and they choose where the installation binds: a **new group** (`inst-{installationId}`, default) or any **existing group they own** (owner role checked again on submit; `POST /auth/github/install/bind` performs the provisioning). No manual id entry is needed. As a fallback (e.g. when the Setup URL is not configured), the `installation.created` webhook event creates/binds the group automatically — existing groups whose `owners` match the installing account are bound, otherwise a dedicated `inst-{installationId}` group is created. Then just add routes/members in the console.
|
||||
|
||||
## Routes
|
||||
|
||||
|
|
@ -195,7 +202,7 @@ You can add role ids in the admin console under _Discord role mentions_.
|
|||
Other route fields:
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| ---------------- | -------- | -------- | ----------------------------------------------------------------------------------------------- |
|
||||
|------------------|----------|----------|-------------------------------------------------------------------------------------------------|
|
||||
| `groupId` | string | Yes | Id of the [group](#groups) this route belongs to |
|
||||
| `fallback` | boolean | No | When `true`, fires only if no non-fallback route matched the event; its own filters are ignored |
|
||||
| `stop` | boolean | No | When `true` and this route matches, no further routes are evaluated for this event |
|
||||
|
|
@ -249,7 +256,7 @@ Routes belong to groups. Groups scope admin access and can restrict which events
|
|||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| ---------------- | -------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|------------------|----------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | string | Yes | Lowercase id (`a-z0-9`, `-`); referenced by each route's `groupId`. Editable: renaming a group re-points its routes, per-group webhook secret and pending invites |
|
||||
| `name` | string | Yes | Human-readable group name |
|
||||
| `members` | object[] | No | `{ login, role }` entries; role is `owner`, `admin`, or `viewer` |
|
||||
|
|
@ -266,7 +273,7 @@ Routes belong to groups. Groups scope admin access and can restrict which events
|
|||
Every group member has one of three roles. Super admins (`ADMIN_USER_IDS`) always bypass them.
|
||||
|
||||
| Role | View routes/logs | Edit routes | Manage members & invites | Edit group settings |
|
||||
| -------- | ---------------- | ----------- | ------------------------ | ------------------- |
|
||||
|----------|------------------|-------------|--------------------------|---------------------|
|
||||
| `owner` | ✓ | ✓ | ✓ | ✓ (except `owners`) |
|
||||
| `admin` | ✓ | ✓ | ✗ | ✗ |
|
||||
| `viewer` | ✓ (read-only) | ✗ | ✗ | ✗ |
|
||||
|
|
@ -282,7 +289,7 @@ Every group member has one of three roles. Super admins (`ADMIN_USER_IDS`) alway
|
|||
|
||||
### Webhook Log Channel
|
||||
|
||||
A group may set `logTarget` to a Discord channel/thread or Telegram chat/topic. Whenever the group's routes dispatch a webhook, a single summary message is sent there: the event type/action, the repo, the delivery id, and one line per route×target with an ✅/❌ outcome (including the error for failed sends). The message is green when every dispatch succeeded and red when any failed. The summary uses the group's message language. Log messages are sent best-effort and are not themselves recorded in the D1 send log.
|
||||
A group may set `logTarget` to a Discord channel/thread or Telegram chat/topic. Whenever the group's routes dispatch a webhook, a single summary message is sent there: the event type/action, the repo, the delivery id, and one line per route×target with an ✅/❌ outcome (including the error for failed sends; at most the first 10 lines are listed, the rest is summarized as `+N`). The message is green when every dispatch succeeded and red when any failed. The summary uses the group's message language. Log messages are sent best-effort and are not themselves recorded in the D1 send log.
|
||||
|
||||
### Invites
|
||||
|
||||
|
|
@ -297,7 +304,7 @@ With `ALLOW_SELF_SIGNUP=1`, a GitHub user who has no group access gets a persona
|
|||
See the [Filter Tutorial](./filters) for a hands-on guide with worked examples.
|
||||
|
||||
| Type | Matches | Example |
|
||||
| --------- | -------------------- | ---------------------------------- |
|
||||
|-----------|----------------------|------------------------------------|
|
||||
| `event` | GitHub event name | `push`, `pull_*`, `pull_request` |
|
||||
| `repo` | Repository full name | `org/repo`, `org/*` |
|
||||
| `actor` | Sender login | `username`, `[bot]`, `*[bot]` |
|
||||
|
|
@ -326,7 +333,7 @@ Filters accept either a single string or an array of strings:
|
|||
## KV Storage Layout
|
||||
|
||||
| Key Pattern | Value | TTL |
|
||||
| ------------------------------ | ----------------------------------------------------------------------------- | ------------------ |
|
||||
|--------------------------------|-------------------------------------------------------------------------------|--------------------|
|
||||
| `config:routes` | JSON array of routes | Permanent |
|
||||
| `config:groups` | JSON array of groups | Permanent |
|
||||
| `session:{id}` | Admin session `{ userId, login }` | 7 days |
|
||||
|
|
@ -336,6 +343,8 @@ 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 |
|
||||
| `delivery:{groupId}:{id}` | Tenant-scoped delivery dedup for the per-group webhook ingress | 300 seconds |
|
||||
| `tenant:{groupId}` | Per-group webhook secret (64-char hex, generated from the console) | Permanent |
|
||||
| `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 |
|
||||
|
|
@ -347,7 +356,7 @@ Filters accept either a single string or an array of strings:
|
|||
The D1 database (`DB` binding, database `webhooker`) holds four tables:
|
||||
|
||||
| Table | Purpose |
|
||||
| ---------------- | ---------------------------------------------------------------------------------------------- |
|
||||
|------------------|------------------------------------------------------------------------------------------------|
|
||||
| `send_logs` | One row per dispatch attempt (route id, event, target, ok/error, duration, error code, detail) |
|
||||
| `audit_logs` | One row per admin operation (login/logout, group/route/member/invite changes) |
|
||||
| `discord_links` | Maps `discord_user_id` → `github_user_id` for `/gh` Discord commands |
|
||||
|
|
|
|||
|
|
@ -37,10 +37,8 @@ bunx wrangler secret put ADMIN_USER_IDS # comma-separated GitHub IDs/login
|
|||
There is no global channel secret. Each route in the [Web UI](/guide/configuration#web-ui) declares its own target channel (and optional thread), so `DISCORD_CHANNEL_ID` is not needed.
|
||||
:::
|
||||
|
||||
::: tip GitHub App ID / private key are unused
|
||||
`GITHUB_APP_ID` and `GITHUB_PRIVATE_KEY` are not currently used by the code — the
|
||||
OAuth flow only needs `GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET`. You do not need to
|
||||
set them (no PKCS#8 conversion required).
|
||||
::: tip GitHub App ID / private key are optional
|
||||
`GITHUB_APP_ID` + `GITHUB_PRIVATE_KEY` (PKCS#8 PEM) are only used by the [App install flow](#github-app-setup) to resolve the installing account's login on the post-install choice page. You can skip them — the page then shows an anonymous `inst-{installationId}` group. The OAuth flow only needs `GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET`.
|
||||
:::
|
||||
|
||||
Discord interactions arrive via the HTTPS Interactions Endpoint, so set `DISCORD_PUBLIC_KEY` and point the **Interactions Endpoint URL** at `https://your-domain/discord/interactions`. See [Interactions Endpoint](#interactions-endpoint) below.
|
||||
|
|
@ -124,7 +122,7 @@ Your worker is now live at `https://webhooker.<your-subdomain>.workers.dev`.
|
|||
- **Organization permissions**: Members (read) — if needed
|
||||
4. Subscribe to events (all 28 supported):
|
||||
- Push, Pull request, Issues, Issue comment, Workflow run, Workflow job, Status, Deployment, Deployment status, Ping, Release, Create, Delete, Star, Fork, Check run, Check suite, Pull request review, Pull request review comment, Commit comment, Member, Label, Milestone, Discussion, Discussion comment, Repository, Code scanning alert, Dependabot alert
|
||||
5. Generate private key → save contents to `GITHUB_PRIVATE_KEY` env var
|
||||
5. Generate private key — optional; set `GITHUB_APP_ID` + `GITHUB_PRIVATE_KEY` to show the installing account's login on the post-install page (see the tip above).
|
||||
|
||||
### 2. Install App
|
||||
|
||||
|
|
@ -161,7 +159,7 @@ Messages are sent via the Discord **REST API**, so pushing works with just `DISC
|
|||
|
||||
The `/gh` slash command and the `GitHub: 添加/编辑/删除评论` message commands are synced by the scheduled trigger (every 5 minutes): per-guild for instant availability, plus a global registration (24h dedup, ~1h propagation). The bot never connects to the Discord Gateway, so it shows as **offline** — messaging is unaffected (always REST).
|
||||
|
||||
Users run `/gh login` to link their GitHub account and can then comment on issues/PRs as themselves. See the [README](https://github.com/ReCloudStudio/WebHooker#bot-commands-comment-on-github-as-yourself) for the full command reference.
|
||||
Users run `/gh login` to link their GitHub account and can then comment on issues/PRs as themselves. See the [Bot Commands](/guide/commands) page for the full command reference.
|
||||
|
||||
## Telegram Bot Setup
|
||||
|
||||
|
|
@ -170,12 +168,7 @@ Users run `/gh login` to link their GitHub account and can then comment on issue
|
|||
3. The worker syncs the webhook from the scheduled trigger (`setWebhook` to `{BASE_URL}/telegram/webhook`), so no manual `setWebhook` call is needed — just make sure `BASE_URL` is set.
|
||||
4. Add the bot to a group (or enable topics) and route events to `chatId` / `topicId` in the route config.
|
||||
|
||||
In Telegram, `/gh` commands work by replying to a notification message:
|
||||
|
||||
- `/gh login` — link your GitHub account (returns an OAuth link)
|
||||
- `/gh logout` — unlink
|
||||
- `/gh comment <text>` — reply to an issue/PR notification to comment as yourself
|
||||
- `/gh merge` / `/gh close` — reply to a PR notification to merge/close it
|
||||
In Telegram, `/gh` commands (`/gh login`, `/gh logout`, `/gh comment <text>`, `/gh merge`, `/gh close`) work by replying to a notification message — see the [Bot Commands](/guide/commands) page.
|
||||
|
||||
Avatars are rendered as a link-preview card using the built-in `GET /api/richheader` (overridable with `TELEGRAM_RICH_HEADER_HOST`).
|
||||
|
||||
|
|
|
|||
37
docs/guide/faq.md
Normal file
37
docs/guide/faq.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# FAQ & Troubleshooting
|
||||
|
||||
## Why is the Discord bot showing as offline?
|
||||
|
||||
The bot never connects to the Discord Gateway — it always sends messages via the REST API and receives interactions through the HTTPS Interactions Endpoint. **Offline is normal** and does not affect messaging.
|
||||
|
||||
## My webhooks are not being forwarded
|
||||
|
||||
Check in order:
|
||||
|
||||
1. `GET /health` returns `{"status":"ok"}`.
|
||||
2. The webhook URL points at `{BASE_URL}/webhook` and the secret matches `GITHUB_WEBHOOK_SECRET` / `GITEA_WEBHOOK_SECRET`.
|
||||
3. At least one **enabled** route exists for the event (`event` filter), and its group accepts the sender (see `owners` / `providers` / `installationId` on the group).
|
||||
4. The route has at least one target with a valid channel/chat id.
|
||||
5. Look at the console **Logs** tab — every dispatch attempt is recorded with the error.
|
||||
|
||||
## The Discord bot does not reply to commands / buttons
|
||||
|
||||
- `DISCORD_PUBLIC_KEY` must be set and the **Interactions Endpoint URL** must point at `{BASE_URL}/discord/interactions`.
|
||||
- The user must run `/gh login` first and the OAuth secrets (`GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET`, `BASE_URL`) must be configured.
|
||||
- Slash commands sync from the scheduled trigger every 5 minutes; global registration can take ~1 hour to propagate.
|
||||
|
||||
## I deleted a branch but got a "0 commits" push message
|
||||
|
||||
Branch deletions via `git push --delete` arrive as push events with `deleted: true` — they are rendered as a normal delete message. If you still see "0 commits", the payload's `deleted` flag was absent (e.g. an old delivery).
|
||||
|
||||
## How do I point a group at its own webhook endpoint?
|
||||
|
||||
See [Per-group endpoint](./configuration#per-group-endpoint) — generate a secret from the group's **Webhook endpoint** panel (owner role), then use `POST /webhook/{groupId}` with the group secret.
|
||||
|
||||
## Can I run this outside Cloudflare Workers?
|
||||
|
||||
No — the worker requires the KV and D1 bindings declared in `wrangler.jsonc` and runs on the `cloudflare_module` Nitro preset.
|
||||
|
||||
## Where is data stored?
|
||||
|
||||
Configuration lives in Cloudflare KV (`config:routes`, `config:groups`); send/audit logs and platform↔GitHub links live in D1. See [Storage Layout](./configuration#kv-storage-layout).
|
||||
|
|
@ -38,7 +38,7 @@ BASE_URL=http://localhost:8787
|
|||
```
|
||||
|
||||
::: tip
|
||||
`GITHUB_APP_ID` / `GITHUB_PRIVATE_KEY` are not used by the code (the OAuth flow only needs the client ID/secret), so you can omit them. Target channels are set per route in the Web UI, so no `DISCORD_CHANNEL_ID` is needed. To enable `/gh` commands locally, copy the **Public Key** from the Developer Portal into `DISCORD_PUBLIC_KEY` and set the Interactions Endpoint URL to `http://localhost:8787/discord/interactions`.
|
||||
`GITHUB_APP_ID` / `GITHUB_PRIVATE_KEY` are optional and only used by the App install flow to resolve the installing account's login (the OAuth flow only needs the client ID/secret). Target channels are set per route in the Web UI, so no `DISCORD_CHANNEL_ID` is needed. To enable `/gh` commands locally, copy the **Public Key** from the Developer Portal into `DISCORD_PUBLIC_KEY` and set the Interactions Endpoint URL to `http://localhost:8787/discord/interactions`.
|
||||
:::
|
||||
|
||||
::: warning
|
||||
|
|
|
|||
24
docs/guide/i18n.md
Normal file
24
docs/guide/i18n.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# Message Language (i18n)
|
||||
|
||||
Messages are rendered in the language configured per group. WebHooker ships with **English** (default) and **Simplified Chinese** dictionaries.
|
||||
|
||||
## Per-Group Language
|
||||
|
||||
Set `Group.lang` (e.g. `"zh"`) to choose the message language for every route in a group — see [Groups → Group Schema](./configuration#group-schema). The group's webhook log-channel summaries use the same language.
|
||||
|
||||
## Custom Overrides
|
||||
|
||||
Translation overrides are merged from KV under the key `i18n:<lang>` as a flat JSON object of key → text. Any key from the built-in dictionaries (en/zh) can be overridden; unknown keys fall back to English.
|
||||
|
||||
```jsonc
|
||||
// KV key: i18n:zh
|
||||
{
|
||||
"events.push.title": "{repo}: 推送了 {count} 个提交到 {ref}"
|
||||
}
|
||||
```
|
||||
|
||||
Overrides apply to group messages (and console UI where applicable). To add a brand-new language, store a full dictionary at `i18n:<lang>` — every key you do not provide falls back to English.
|
||||
|
||||
## Emoji Toggle
|
||||
|
||||
`Group.emoji` (default `true`) controls whether event emoji appear in that group's messages. When disabled, all emoji are stripped from titles, descriptions, fields, and links. Milestone progress bars (🟢🟡🟠⬜) are data visualization and stay regardless of the toggle.
|
||||
37
docs/guide/logs.md
Normal file
37
docs/guide/logs.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Logs
|
||||
|
||||
## Send Logs (`send_logs`)
|
||||
|
||||
Every dispatch attempt is recorded in the D1 `send_logs` table and browsable in the console (**Logs** tab). Fields:
|
||||
|
||||
| Field | Meaning |
|
||||
|--------------|-------------------------------------------------------------------------|
|
||||
| `routeId` | Route that matched |
|
||||
| `groupId` | Route's group |
|
||||
| `event` | Event type (e.g. `push`, `pull_request`, `custom`) |
|
||||
| `repo` | Repository full name (when present) |
|
||||
| `target` | Target id the message was sent to |
|
||||
| `platform` | `discord` or `telegram` |
|
||||
| `ok` | Whether the send succeeded |
|
||||
| `status` | HTTP status from the platform API (when applicable) |
|
||||
| `error` | Error message (when failed) |
|
||||
| `errorCode` | Stable error code (e.g. `NO_TARGET`, `NO_TOKEN`, `RATE_LIMITED`) |
|
||||
| `attempts` | Send attempts including retries |
|
||||
| `durationMs` | Time spent sending |
|
||||
| `deliveryId` | Webhook delivery id (when provided) |
|
||||
| `messageId` | Platform message id (used for in-place edits) |
|
||||
| `actor` | Sender login |
|
||||
| `action` | Event action (when present) |
|
||||
| `detail` | Extra JSON details (when present) |
|
||||
|
||||
The console's **Logs** tab lists recent entries (filterable by group) and shows full details for a single entry. Entries are written best-effort — a failed insert never breaks dispatch.
|
||||
|
||||
## Audit Log (`audit_logs`)
|
||||
|
||||
Every admin operation is recorded in D1 `audit_logs` and browsable in the console (**Audit** tab): logins/logouts, group/route/member/invite changes, token revocations, installation bindings. Fields: timestamp, actor (GitHub id + login), action, target type/id, group id, ip, and detail JSON.
|
||||
|
||||
Entries are pruned automatically by the scheduled trigger after `AUDIT_RETENTION_DAYS` (default 90). Like send logs, writes are best-effort.
|
||||
|
||||
## Webhook Log Channel
|
||||
|
||||
Groups can additionally receive a per-webhook summary message in a Discord channel/thread or Telegram chat/topic — see [Groups → Webhook Log Channel](./configuration#webhook-log-channel). These summaries are best-effort and are **not** recorded in `send_logs`.
|
||||
32
docs/guide/message-format.md
Normal file
32
docs/guide/message-format.md
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# Message Format
|
||||
|
||||
Every event formatter produces a platform-neutral message (`NeutralMessage`), rendered by the platform drivers into a Discord embed or Telegram HTML message.
|
||||
|
||||
## Title
|
||||
|
||||
Every title must start with the repo, then an optional `#number`, then `: subject`:
|
||||
|
||||
```
|
||||
{repo}{#number}: {subject} e.g. acme/widget#7: Add feature
|
||||
```
|
||||
|
||||
The repo comes from `payload.repository.full_name` (falling back to a generic "repository" label when missing). Comments, reviews, and inline comments use the same `{repo}{#number}: {title}` title as their parent object — never `"Comment on org/repo"` prefixes.
|
||||
|
||||
## Links
|
||||
|
||||
Only the repo head is hyperlinked — never the whole title:
|
||||
|
||||
- **Discord** (embed titles cannot contain partial links): the title is the repo head `{repo}{#number}`, linked to the repository; the `: {subject}` text is rendered as the first line of the description, unlinked.
|
||||
- **Telegram** (HTML supports inline links): the one-line title keeps the subject, with only the repo head wrapped in a link.
|
||||
|
||||
Messages whose title has no colon separator (a `:` followed by a space) keep the legacy whole-title link behavior.
|
||||
|
||||
Commit hashes, branches, and tags render as inline code wrapped in a hyperlink (e.g. ``[`abc123d`](https://…/commit/abc123def456)``), falling back to plain inline code when the repo base URL is unavailable.
|
||||
|
||||
## Emoji
|
||||
|
||||
Event-specific emoji are added by the formatters; per-group `Group.emoji` (default true) strips them all when disabled. Milestone progress bars are exempt. See [Message Language](./i18n).
|
||||
|
||||
## In-Place Updates
|
||||
|
||||
`workflow_run` and `check_run` messages are sent once and edited in place as the run progresses (queued → running → success/failure) — no duplicate messages. Tracking uses KV `msg:*` with a stable `updateKey` per run.
|
||||
11
docs/guide/tasks.md
Normal file
11
docs/guide/tasks.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Scheduled Tasks
|
||||
|
||||
WebHooker runs three maintenance tasks on the scheduled trigger (`*/5 * * * *`, every 5 minutes). They only run on the deployed worker (Cloudflare cron); local `wrangler dev` runs them when triggered via `wrangler dev --test-scheduled`.
|
||||
|
||||
| Task | Purpose |
|
||||
|-----------------|------------------------------------------------------------------------------------------------------------------|
|
||||
| `discord-sync` | Registers the Discord slash/context-menu commands: per-guild (instant) and globally (24h dedup, ~1h propagation) |
|
||||
| `telegram-sync` | Calls `setWebhook` to `{BASE_URL}/telegram/webhook` (with `TELEGRAM_WEBHOOK_SECRET` as `secret_token` when set) |
|
||||
| `audit-prune` | Deletes `audit_logs` entries older than `AUDIT_RETENTION_DAYS` (default 90) |
|
||||
|
||||
There is nothing to configure beyond the secrets the tasks use (`DISCORD_TOKEN`, `DISCORD_APPLICATION_ID`, `TELEGRAM_TOKEN`, `BASE_URL`, `AUDIT_RETENTION_DAYS`).
|
||||
Loading…
Add table
Add a link
Reference in a new issue