feat: add Telegram push support with multi-target routes

Add a platform-aware route target system so a single route can forward
to several destinations at once (e.g. a Discord channel and a Telegram
group). Route.target becomes Route.targets[] with per-entry platform,
channelId/threadId for Discord and chatId/topicId for Telegram; the
legacy single-target format is normalized on load and accepted by the
admin API.

Implement the Telegram driver with HTML rendering and Bot API
sendMessage (chat_id + message_thread_id for topics, retry on 429/5xx),
plus /gh commands served over POST /telegram/webhook: login, logout,
comment, merge and close. The comment/merge/close commands resolve the
issue or PR from the replied-to notification message. OAuth binding now
stores a D1 telegram_links mapping and replies with a confirmation.

Sync the Telegram webhook from the scheduled trigger via setWebhook.
This commit is contained in:
RhenCloud 2026-08-03 05:17:59 +08:00
parent dcbe93be91
commit bd7a8f2632
No known key found for this signature in database
GPG key ID: A574A617378C4E0B
39 changed files with 1165 additions and 162 deletions

View file

@ -57,7 +57,7 @@ npx wrangler dev # Start local dev server
### Routes
Routes are stored in KV (`config:routes` as JSON). There are **no default routes** — every route (including its target `channelId` / `threadId`) must be defined explicitly, either via the Web UI (`/admin`) or by storing a JSON array in KV:
Routes are stored in KV (`config:routes` as JSON). 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 KV:
```json
[
@ -66,12 +66,19 @@ Routes are stored in KV (`config:routes` as JSON). There are **no default routes
"name": "Push Events",
"enabled": true,
"filters": [{ "type": "event", "match": "push" }],
"target": { "channelId": "CHANNEL_ID" }
"target": { "platform": "discord", "channelId": "CHANNEL_ID" }
},
{
"id": "telegram-issues",
"name": "Issues to Telegram",
"enabled": true,
"filters": [{ "type": "event", "match": "issues" }],
"target": { "platform": "telegram", "chatId": "-1001234567890" }
}
]
```
The `target.channelId` is required and is used as-is; there is no fallback to a default channel.
`target.platform` selects the push target: `discord` (default) or `telegram`. Discord routes require `target.channelId` (optional `threadId` for a thread); Telegram routes require `target.chatId` (the group chat id, optional `topicId` for a topic). There is no fallback to a default channel.
### Web UI (`/admin`)