WebHooker/docs/guide/routes.md
RhenCloud c090281cb2
feat(filters): JSONPath field filters, operators, AST groups, fragments, and test-match
Add a field filter type reading any payload value by JSONPath with array expansion, 12 comparison operators (eq/ne/contains/startsWith/endsWith/regex/gt/gte/lt/lte/in/exists), a visual AST builder (all/any/not) in the route editor, chip-based multi-value input, a stateless POST /admin/api/test-match dry-run, and named filter fragments stored in D1 (d1_fragments, migration 0010) inlined into route ASTs on insert.
2026-08-24 22:51:20 +08:00

4.1 KiB

Routes & Targets

Routes define which events get forwarded to which channel (Discord or Telegram). They are stored in D1 (d1_routes, seeded from the legacy KV config:routes key on first load), managed via the Web UI, the Admin API, or config.example.yaml.

There are no default routes — each route must define its own target. If no routes are configured, no events are forwarded. At most 200 routes can be saved per instance.

Route Schema

{
  "id": "unique-route-id",
  "name": "Human-readable name",
  "enabled": true,
  "groupId": "my-group",
  "fallback": false,
  "stop": false,
  "discordRoleIds": ["111111111111111111"],
  "filters": [
    { "type": "event", "match": "push" },
    { "type": "repo", "match": "org/repo", "exclude": false }
  ],
  "targets": [
    {
      "platform": "discord",
      "channelId": "REQUIRED_CHANNEL_ID",
      "threadId": "OPTIONAL_THREAD_ID"
    }
  ]
}

Each entry of targets is a push destination, so one route can forward to several channels at once (e.g. a Discord channel and a Telegram group). target.platform selects the platform: discord (default) or telegram. For Discord, target.channelId is required (a thread in target.threadId is optional). For Telegram, target.chatId (the group/supergroup chat id, e.g. -1001234567890) is required and target.topicId (the message_thread_id of a topic, equivalent of a Discord thread) is optional. There is no fallback to a default channel.

Field Type Required Description
groupId string Yes Id of the group 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
discordRoleIds string[] No Discord role ids to ping when this route fires; applied to Discord targets only
ast object No Boolean filter tree ({all:[...]} / {any:[...]} / {not:{...}}); takes precedence over filters when present

Discord Role Mentions

Set discordRoleIds on a route to ping one or more Discord roles (身份组) whenever that route fires. The mention (<@&roleId>) is prepended to the message content of every Discord target of the route; Telegram targets ignore this field. Mentions only trigger notifications when the bot has the Mention Everyone permission (or the role is marked mentionable), and the bot must be able to see the role.

{
  "id": "release-notify",
  "name": "Notify on Release",
  "enabled": true,
  "groupId": "default",
  "discordRoleIds": ["111111111111111111", "222222222222222222"],
  "filters": [{ "type": "event", "match": "release" }],
  "targets": [{ "platform": "discord", "channelId": "REQUIRED_CHANNEL_ID" }]
}

You can add role ids in the admin console under Discord role mentions.

Filters

Every route carries a filters array (all must match — AND logic). When the route has an ast field (a nested all/any/not tree), it is evaluated instead of filters, so it can express arbitrary boolean combinations. See the Filter Types reference and the Filter Tutorial.

Custom Route Example

[
  {
    "id": "backend-prs",
    "name": "Backend PRs",
    "enabled": true,
    "groupId": "backend-team",
    "filters": [
      { "type": "repo", "match": "myorg/backend" },
      { "type": "event", "match": "pull_request" },
      { "type": "actor", "match": "[bot]", "exclude": true }
    ],
    "targets": [
      {
        "platform": "telegram",
        "chatId": "-1001234567890",
        "topicId": "9876543210"
      }
    ]
  }
]