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

@ -1,4 +1,4 @@
import type { Route, Env, NeutralMessage } from "../../types";
import type { RouteTarget, Env, NeutralMessage } from "../../types";
import type { PlatformDriver, SendResult } from "../types";
import { sendMessage } from "./rest";
import { renderNeutralMessage } from "./render";
@ -6,8 +6,12 @@ import { renderNeutralMessage } from "./render";
export class DiscordDriver implements PlatformDriver {
readonly id = "discord";
async send(message: NeutralMessage, target: Route["target"], env: Env): Promise<SendResult> {
async send(message: NeutralMessage, target: RouteTarget, env: Env): Promise<SendResult> {
const channelId = target.channelId ?? "";
if (!channelId) {
return { ok: false, error: "target.channelId is required", errorCode: "NO_TARGET" };
}
const token = env.DISCORD_TOKEN ?? "";
return sendMessage(token, target.channelId, renderNeutralMessage(message), target.threadId);
return sendMessage(token, channelId, renderNeutralMessage(message), target.threadId);
}
}