From fbeb5bf29ee5bef7637e92f2f8c04a8d0051f8d1 Mon Sep 17 00:00:00 2001 From: skulidropek <66840575+skulidropek@users.noreply.github.com> Date: Mon, 23 Mar 2026 06:34:04 +0000 Subject: [PATCH] feat(api): expose telegram tma link in bootstrap claim --- src/__tests__/server.test.ts | 3 +++ src/config.ts | 2 ++ src/routes/projects.ts | 15 +++++++++++++++ 3 files changed, 20 insertions(+) diff --git a/src/__tests__/server.test.ts b/src/__tests__/server.test.ts index 5d7ed0a..8650b1c 100644 --- a/src/__tests__/server.test.ts +++ b/src/__tests__/server.test.ts @@ -22,6 +22,9 @@ import { createApp } from "../server.js"; const now = new Date().toISOString(); const botHeaders = { "x-spawndock-bot-secret": "spawndock-dev-bot-secret" }; +process.env.TELEGRAM_BOT_USERNAME ??= "rustgpt_bot"; +process.env.TELEGRAM_MINI_APP_SHORT_NAME ??= "tma"; + function createRuntime(): Runtime { return { state: { projects: [], pairingTokens: [], deviceCredentials: [], tunnelSessions: [] }, diff --git a/src/config.ts b/src/config.ts index 11e306d..623b142 100644 --- a/src/config.ts +++ b/src/config.ts @@ -13,6 +13,8 @@ function readContainerRuntime(): "docker" | "podman" { export const config = { port: parseInt(process.env.PORT || "3000", 10), botSecret: process.env.SPAWNDOCK_BOT_SECRET || "spawndock-dev-bot-secret", + telegramBotUsername: process.env.TELEGRAM_BOT_USERNAME || "", + telegramMiniAppShortName: process.env.TELEGRAM_MINI_APP_SHORT_NAME || "tma", rateLimitRps: parseInt(process.env.RATE_LIMIT_RPS || "10", 10), get qwenMode() { return readQwenMode(); diff --git a/src/routes/projects.ts b/src/routes/projects.ts index 85ccecc..b0a6ccf 100644 --- a/src/routes/projects.ts +++ b/src/routes/projects.ts @@ -4,6 +4,7 @@ import type { Runtime } from "../types.js"; import { createProject, claimPairingToken, inspectPairingToken, resolveOwnedProjectLaunchInfo, buildLaunchUrl } from "../projects.js"; import { saveState } from "../store.js"; import { config } from "../config.js"; +import { buildTelegramMiniAppUrl } from "../bot/links.js"; export function projectRoutes(runtime: Runtime, stateFilePath: string): Router { const router = Router(); @@ -93,6 +94,7 @@ export function projectRoutes(runtime: Runtime, stateFilePath: string): Router { saveState(stateFilePath, runtime.state); const project = result.project; + const telegramMiniAppUrl = resolveTelegramMiniAppUrl(project.slug); res.status(201).json({ project, projectId: project.id, @@ -104,6 +106,7 @@ export function projectRoutes(runtime: Runtime, stateFilePath: string): Router { tunnelWsUrl: `${config.publicOrigin.replace(/^http/, "ws")}${config.tunnelPath}?token=${result.deviceCredential.secret}`, controlPlaneUrl: config.publicOrigin, localPort: config.tmaLocalPort, + ...(telegramMiniAppUrl ? { telegramMiniAppUrl } : {}), }); }; @@ -187,3 +190,15 @@ function readOwnerTelegramId(value: unknown): number | null { return typeof value === "number" && Number.isFinite(value) ? value : null; } + +function resolveTelegramMiniAppUrl(projectSlug: string): string | null { + if (config.telegramBotUsername.length === 0) { + return null; + } + + return buildTelegramMiniAppUrl( + config.telegramBotUsername, + config.telegramMiniAppShortName, + projectSlug, + ); +}