Skip to content

Latest commit

 

History

154 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WebHooker

GitHub / Gitea webhook → Discord / Telegram dispatcher. Receives webhook events via Cloudflare Workers, applies filters, and routes formatted messages to Discord channels/threads and Telegram chats/topics. Forge-specific adapters live under server/lib/providers/ (GitHub + Gitea today; GitLab etc. can be added later).

Features

  • 28 event formatters — push, pull_request, issues, issue_comment, workflow_run, workflow_job, status, deployment, deployment_status, check_run, check_suite, ping, release, create, delete, star, fork, pull_request_review, pull_request_review_comment, commit_comment, member, label, milestone, discussion, discussion_comment, repository, code_scanning_alert, dependabot_alert (+ generic fallback, + custom webhooks)
  • Multi-provider webhooks — GitHub (X-Hub-Signature-256) and Gitea (X-Gitea-Signature) share one /webhook endpoint; the provider is auto-detected from headers
  • Per-group webhook ingress — every group can get its own POST /webhook/{groupId} URL + secret (Gitea, classic GitHub webhooks, and arbitrary custom JSON posts signed with X-WebHooker-Signature, with optional timestamp+nonce replay protection)
  • GitHub App tenant isolation — bind a group to a GitHub App installation id so only that org/user's events enter it
  • HMAC-SHA256 signature verification (Web Crypto API)
  • Filter by event type, repo, actor, action, branch, keyword (supports */? globs and /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 / check_run progress is edited in place (single message updated as the run advances) on both platforms
  • Per-group webhook log channel — point a group at a Discord channel/thread or Telegram chat/topic and every webhook the group's routes dispatch is summarized there (✅/❌ per route × target)
  • 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
  • Telegram /gh commands (login/logout/comment/merge/close) via the Telegram webhook, with avatar link-preview cards
  • Cloudflare D1 for config (routes/groups), send logs, platform account links, dedup, delivery state and message tracking + KV for ephemeral state/cache/security tokens, + optional R2 for oversized payloads
  • Async delivery via Cloudflare Queues — when the QUEUE binding is present, verified webhooks are enqueued to webhooker-delivery and dispatched by a consumer with exponential retry backoff (5s/30s/2m/10m) and a dead-letter queue (webhooker-delivery-dlq); oversized payloads are parked in R2 (PAYLOAD binding, falling back to KV queue:payload:*). Without the binding, dispatch stays inline
  • Graceful degradation (webhook-only mode if Discord unavailable)

Architecture

GitHub Webhook → Cloudflare Worker (Nuxt 4 / Nitro)
                 ├── POST /webhook → verify → dedup → enqueue (Queue) → dispatch → Discord (REST) / Telegram (Bot API)
                 ├── POST /discord/interactions → verify (Ed25519) → handle command/button/modal
                 ├── POST /telegram/webhook → verify (secret token) → handle /gh commands
                 ├── GET  /auth/github → OAuth flow
                 ├── GET  /api/richheader → Telegram avatar link-preview card
                 ├── POST /api/* → user actions (Bearer token auth)
                 ├── /admin → routes, groups & send logs Web UI
                 └── GET  /health → status check
  • Cloudflare Worker — HTTP ingress, signature verification, routing, platform dispatch
  • Interactions Endpoint — HTTPS callback (no Discord Gateway connection, no Durable Object); the bot stays offline and commands are registered via the API
  • KV — cache + ephemeral state: token storage (token:{userId}), OAuth state (state:{hex}), admin sessions (session:{id}), per-group webhook secrets (tenant:{groupId}), invites, config cache, delivery dedup/state/message-tracking fallback (delivery:*, delivery-state:*, msg:* used only when D1 is unavailable) and message-update locks (msg:lock:*)
  • D1 — source of truth for routes/groups (d1_routes/d1_groups), send logs (send_logs), audit logs (audit_logs), dedup (dedup_keys), delivery state (delivery_state), message-update tracking (message_tracking), Discord↔GitHub links (discord_links), Telegram↔GitHub links (telegram_links)
  • Queue — async delivery when QUEUE is bound: webhooker-delivery (exponential retry) + DLQ webhooker-delivery-dlq; oversized payloads parked in R2 (PAYLOAD binding, webhooks/YYYY/MM/DD/*.json, falling back to KV queue:payload:*)

Quick Start

bun install          # package manager is bun (lockfile: bun.lock)
cp .env.example .dev.vars   # Fill in secrets for local dev
bun run build        # Production build first (wrangler dev serves the built worker)
bunx wrangler dev    # Start local dev server

Configuration

Secrets (.dev.vars for local, Worker Secrets for production)

Variable Description
GITHUB_WEBHOOK_SECRET Webhook secret from GitHub
GITEA_WEBHOOK_SECRET Webhook secret from Gitea (required only to receive Gitea webhooks)
GITHUB_APP_ID GitHub App ID (used by the App install flow to resolve the installing account)
GITHUB_PRIVATE_KEY App private key (PKCS#8 PEM; used by the App install flow; optional)
GITHUB_CLIENT_ID OAuth client ID
GITHUB_CLIENT_SECRET OAuth client secret
DISCORD_TOKEN Bot token
DISCORD_PUBLIC_KEY Discord application public key (from the Developer Portal) — required for interactions
DISCORD_APPLICATION_ID Discord application id (optional; auto-resolved via GET /oauth2/applications/@me if omitted)
TELEGRAM_TOKEN Telegram bot token (from BotFather) — required for Telegram routes
TELEGRAM_WEBHOOK_SECRET Optional secret token for POST /telegram/webhook verification
TELEGRAM_RICH_HEADER_HOST Optional base URL overriding the built-in GET /api/richheader for Telegram avatar cards
BASE_URL Public URL for OAuth callbacks and the Telegram webhook sync
ADMIN_USER_IDS Comma-separated GitHub user IDs (or logins) allowed to access /admin
ALLOW_SELF_SIGNUP 1 to give access-less GitHub users a personal group on first login (default off)
AUDIT_RETENTION_DAYS Audit-log retention in days for the scheduled cleanup (default 90)
NUXT_PUBLIC_DOCS_URL Optional docs site URL used by the landing page
NUXT_PUBLIC_REPO_URL Optional GitHub repo URL used by the landing page
NUXT_PUBLIC_LEGAL_CONTACT Optional contact shown on /terms and /privacy

Routes

Routes are stored in D1 (d1_routes, seeded from legacy KV config:routes on first load). There are no default routes — every route (including its target) must be defined explicitly, either via the Web UI (/admin) or by storing a JSON array in D1. A route may carry multiple targets, so one rule can forward to several channels at once:

[
  {
    "id": "all-push",
    "name": "Push Events",
    "enabled": true,
    "groupId": "default",
    "filters": [{ "type": "event", "match": "push" }],
    "stop": true,
    "targets": [
      { "platform": "discord", "channelId": "CHANNEL_ID" },
      { "platform": "telegram", "chatId": "-1001234567890" }
    ]
  }
]

target.platform selects the push target: discord (default) or telegram. Discord targets require target.channelId (optional threadId for a thread); Telegram targets require target.chatId (optional topicId for a topic). Routes belong to groups (D1 d1_groups, seeded from legacy KV config:groups) that scope admin access and can restrict which org/user events flow in. See the Routes & Targets and Groups & Access Control guides for the full schema.

Web UI (/admin)

The built-in config console lets you manage routes and groups in the browser (add / edit / delete / toggle / reorder), inspect send logs, manage group members and invite links, and read the audit log — no KV access needed:

  1. Set ADMIN_USER_IDS to the GitHub user IDs (or logins) allowed to manage the console, e.g. ADMIN_USER_IDS=12345,RhenCloud.
  2. Visit /admin and sign in with GitHub. Users with no access get 403 — unless ALLOW_SELF_SIGNUP=1 (they receive a personal group) or they follow a group invite link.
  3. Changes are written to D1 immediately, the config cache is invalidated, and the webhook pipeline picks them up on the next run.

Sign out at /admin/logout. Every group has members with a role (owner / admin / viewer); all admin operations are recorded in the D1 audit_logs table.

Filter Types

Every filter supports plain text, */? globs, and /regex/ patterns (case-insensitive); set exclude: true to invert. Filters can also be grouped into an AST via an optional ast on a route (all / any / not nodes) to express boolean combinations beyond the default AND-list. See the Filter Tutorial for the pattern syntax and the full filter reference.

API

Health

  • GET /health — Returns {"status": "ok"}

OAuth

  • GET /auth/github — Start GitHub OAuth flow (redirects to GitHub)
  • GET /auth/github/callback — OAuth callback (exchanges code for token; admin session / Discord link / Telegram link)
  • DELETE /auth/token/:userId — Revoke user token

Actions (require Authorization: Bearer <token> header)

  • POST /api/comment — Create issue comment
  • POST /api/merge — Merge pull request
  • POST /api/close — Close pull request
  • POST /api/react — Add reaction to issue

Admin (require admin OAuth session)

  • GET /admin — Config console UI
  • GET /admin/login — Start admin sign-in (GitHub OAuth)
  • GET /admin/logout — Sign out
  • GET /admin/invite?token=… — Accept a group invite (browser page)
  • GET /admin/api/routes — List routes
  • PUT /admin/api/routes — Replace routes (owner/admin per group)
  • GET /admin/api/groups — List groups + your role in each
  • PUT /admin/api/groups — Replace groups (super: all; owner: own groups only)
  • GET /admin/api/groups/:groupId/routes — List a group's routes
  • PUT /admin/api/groups/:groupId/routes — Replace a group's routes
  • POST /admin/api/groups/:groupId/invites — Create invite link (owner)
  • GET /admin/api/groups/:groupId/invites — List pending invites (owner)
  • DELETE /admin/api/invites/:token — Revoke an invite (owner)
  • GET /admin/api/audit — Audit log (scoped)
  • GET /admin/api/me — Current session / scope / roles
  • GET /admin/api/logs — Send logs (scoped)
  • GET /admin/api/logs/:id — Single send-log entry
  • GET /admin/api/metrics — Delivery stats (totals, failure rate, per-platform/event/status, recent failures); optional ?groupId= scope
  • GET /admin/api/delivery/:deliveryId — All send-log attempts for one delivery

Setup Guides

  • GitHub App — create the app, subscribe to events, configure OAuth, and set the Setup URL for tenant isolation: see GitHub App Setup
  • Discord bot — create the bot, invite it with applications.commands (combined permission integer 274877910016), and configure the Interactions Endpoint: see Discord Bot Setup. The bot never connects to the Discord Gateway, so it shows as offline — messaging is unaffected (always REST).
  • Telegram bot — create the bot with @BotFather, set TELEGRAM_TOKEN (optional TELEGRAM_WEBHOOK_SECRET); the webhook is synced automatically by the scheduled trigger: see Telegram Bot Setup
  • Deployment — KV namespace, D1 database + migrations, optional Queues, secrets, and deploy: see the Deployment guide

Bot Commands (comment on GitHub as yourself)

The bot registers native slash and message context-menu commands, synced by the scheduled trigger (every 5 minutes): per-guild for instant availability, and globally (24h dedup, ~1h propagation). After /gh login you can comment on issues/PRs as yourself, edit/delete your comments, and merge/close PRs via buttons — all replies are ephemeral and GitHub enforces permission.

/gh login  /gh logout
/gh comment add|edit|del  link:<url>      (or right-click a notification → Apps → GitHub: 添加/编辑/删除评论)

See the full reference in the Bot Commands guide.

Development

bun run dev           # Nuxt dev server (HMR)
bun run typecheck     # Type checking
bun run lint          # ESLint
bun test              # Unit tests

Supported Events

28 event formatters (push, pull_request, issues, workflow_run, release, ...) plus custom webhooks; unsupported events fall back to a generic formatter. See the full table with embed highlights in Supported Events.

License

MIT

About

GitHub / Gitea webhook → Discord / Telegram dispatcher. Receives webhook events via Cloudflare Workers, applies filters, and routes formatted messages to Discord channels/threads and Telegram chats/topics.

Resources

Contributing

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages