Telegram Serverless vám umožňuje spouštět backendový kód pro vaše boty a Mini Aplikace přímo na infrastruktuře Telegramu — žádné servery k provisioningu, žádné kontejnery k udržování při životě, žádné škálování k řešení. Píšete jednoduché JavaScriptové moduly, nasadíte je jediným příkazem a Telegram je spustí v rychlém, izolovaném V8 sandboxu, který je přímo vedle Bot API a vestavěné databáze.

Pokud jste někdy zapojili bota do VPS, cloudové funkce nebo panelu hostingu jen proto, abyste odpověděli na /start, toto je část, kterou už nemusíte dělat.

Proč serverless

Telegram bot je v srdci program, který reaguje na aktualizace. Tradičně jste museli tento program hostovat někde, co je vždy zapnuté, dostupné a zabezpečené — a pak to tak udržovat. Telegram Serverless tuto vrstvu zcela odstraňuje:

  • Žádná infrastruktura. Neexistuje žádný stroj k pronájmu, patchování nebo monitorování. Váš kód se spouští na vyžádání a automaticky se škáluje s vaším botem.
  • Přihlašovací údaje zahrnuty. Telegram Bot API, databáze založená na SQLite a odchozí HTTP jsou dostupné každému modulu hned po instalaci — nic k instalaci, žádné přihlašovací údaje k zapojení.
  • Rychlé, izolované provedení. Každé spuštění běží v lehkém V8 izolátu, blízko vlastních systémů Telegramu, takže volání Bot API a vaší databáze jsou rychlá a spolehlivá.
  • Skutečný vývojářský workflow. Projekt žije ve složce na vašem stroji pod správou verzí. Upravujete soubory, přesně vidíte, co se změnilo, nasazujete atomicky a posouváte schéma databáze vpřed pomocí zkontrolovaných migrací — tak, jak už pracujete se vším ostatním.

Mentální model: Jak projekt funguje

Pracujete na třech místech, a ta se na sebe čistě mapují:

Kde Co tam žije
Vaše projektová složka JavaScriptové moduly — schéma, sdílený kód, obsluhy aktualizací, koncové body Mini Aplikace — a pokud nějakou máte, frontend Mini Aplikace
Cloud Nasazená kopie těchto modulů, databáze vašeho bota a soubory Mini Aplikace
tgcloud CLI Most — zobrazuje rozdíly a synchronizuje je

Nikdy se k ničemu nepřihlašujete přes SSH. Soubory upravujete lokálně, spustíte npx tgcloud push a platforma se o to postará. Provoz vašeho bota je řízen nasazenými moduly; vaše databáze přetrvává mezi spuštěními.

Projekt má pouze čtyři druhy kódu, vše uvnitř jedné složky tgcloud/:

tgcloud/
├─ endpoints/  # funkce, které volá vaše Mini Aplikace
├─ handlers/   # funkce, které reagují na typy aktualizací Telegramu
├─ lib/        # sdílený kód, který importujete odkudkoli
└─ schema.js   # tabulky vaší databáze

Když přijde aktualizace — zpráva, stisk tlačítka, inline dotaz — Telegram ji nasměruje na odpovídající obsluhu (handlers/message.js, handlers/callback_query.js, …) a zavolá její výchozí export. Tato funkce komunikuje s Bot API a databází přes SDK a vrací se. To je celý cyklus. Aktualizace bez odpovídající obsluhy je jednoduše ignorována, takže přidáváte jen ty obsluhy, které potřebujete.

Mini Aplikace je volitelná. Pokud ji váš bot má, její frontend žije ve stejném projektu, vedle tgcloud/. Serverless hostuje její výstupy sestavení a spouští endpoints/, které volá. Viz Frontend Mini Aplikace .

Rychlá ukázka

Zde je kompletní, funkční demo bot. Odpovídá na každou zprávu a pamatuje si, kolik jich viděl z každého chatu.

tgcloud/schema.js

import { table, integer } from 'sdk/db';

export const counters = table('counters', {
  chatId: integer('chat_id').primaryKey() ,
  seen:   integer('seen').notNull().default(0) ,
});

tgcloud/handlers/message.js

import { api, db } from 'sdk';
import { sql } from 'sdk/db';
import { counters } from '../schema.js';

export default async function (message) {
  const chatId = message.chat.id;

  // Vloží počítadlo nebo ho zvýší, pokud tento chat už nějaké má — a získá
  // výsledný řádek ve stejném příkazu přes .returning().
  const [row] = await db.insert(counters)
    .values({ chatId, seen: 1 })
    .onConflictDoUpdate({
      target: counters.chatId,
      set: { seen: sql`${counters.seen} + 1` },
    })
    .returning()
    .run();

  await api.sendMessage({
    chat_id: chatId,
    text: `Ahoj! Viděl jsem od tebe ${row.seen} zprávu/zprávy.`,
  });
}

Nasaďte to:

npx tgcloud push       # nahraje moduly
npx tgcloud migrate    # vytvoří tabulku `counters`

To je živý bot s perzistentním stavem a bez serveru. Všechno v něm — api, db, DSL table() — je popsáno v níže uvedených sekcích.

Serverless je obecný backend pro Telegram boty a Mini Aplikace, ne šablona pro jeden druh aplikace. Je ideální pro:

  • Konverzační AI boti , kteří potřebují ukládat stav pro každého uživatele v databázi.
  • Backendy Mini Aplikací , které ukládají uživatelská data a obsluhují dynamický obsah.
  • Hry a nástroje — včetně žebříčků, kvízů a dalšího.
  • Automatizace a integrace , které volají externí HTTP API a posílají výsledky do chatů.

Začínáme

Tento průvodce vás provede od prázdné složky k živému botovi, který odpovídá na zprávy a ukládá data. Předpokládá, že máte nainstalovaný Node.js 18 nebo novější a zaregistrovaného bota u @BotFather . Na konci použijete každý příkaz, který budete denně potřebovat: push, migrate, run a status.

Než cokoliv jiného, zapněte Serverless. V @BotFather otevřete svého bota → Serverless a zapněte ho. Tím se funkce zapne pro tohoto bota a odemkne jeho CLI přístupový token, obsluhy, knihovnu a databázi.

1. Vytvořte projekt

Nejrychlejší způsob, jak začít, je tvůrce projektů, který připraví strukturu projektu a nainstaluje do něj CLI:

npm create @tgcloud/bot example_bot
cd example_bot

Argumentem je cílová složka: pro vytvoření v aktuální složce použijte ., nebo libovolnou cestu. Funguje to i v existující složce a nikdy nepřepisuje existující soubory.

To vám poskytne připravený k úpravám projekt:

example_bot/
├─ tgcloud/             # vše, co platforma spouští
│  ├─ handlers/
│  │  └─ message.js     # startovací obsluha zpráv (echuje text zpět)
│  ├─ lib/              # vaše sdílené moduly zde (prázdné pro začátek)
│  └─ schema.js         # tabulky vaší databáze
├─ docs/
│  └─ tgcloud-sdk.md    # reference SDK (pro vás a vaše AI nástroje)
├─ AGENTS.md            # orientace pro AI asistenty kódování
├─ package.json
└─ tgcloud.jsonc        # konfigurace projektu

Vygenerované soubory jsou samopopisné — každý z nich obsahuje komentované příklady toho, co můžete dál dělat.

CLI se instaluje do projektu jako lokální vývojová závislost, takže ho spouštíte s npx tgcloud <příkaz> (npx najde kopii ve složce node_modules vašeho projektu) nebo prostřednictvím zkratek npm run, které generátor přidá do package.json (npm run deploy, npm run status). Ve výchozím nastavení není na vašem PATH žádné globální tgcloud.

Můžete jej také nainstalovat globálně — npm install -g @tgcloud/cli — pokud dáváte přednost psaní holého tgcloud odkudkoli. To je užitečné pro spuštění tgcloud init v jakékoli prázdné složce a je to to, co potřebuje shell tab-completion. V obou případech získáte stejný projekt.

Stavíte Mini Aplikaci? Začněte místo toho jejím frontendem. Vytvořte ji svým obvyklým nástrojem, pak spusťte tvůrce projektů v této složce. Viz Frontend Mini Aplikace .

2. Propojte svého bota

Každý projekt je svázán s jedním botem. Propojte je pomocí login, který se zeptá na váš CLI přístupový token ( @BotFather → váš bot → Serverless → CLI Access → Access token — jiný token než váš API token bota ) a uloží ho lokálně:

npx tgcloud login

Token má formát app<id>:<secret>. CLI ho ukládá do .tgcloud/, který je ignorován v git, a nikdy nevypisuje tajnou část. Login je jediný čas, kdy se na něj ptáte — viz Autentizace pro způsob, jak jsou tokeny řešeny v CI.

3. Podívejte se kolem

Dva příkazy vám kdykoli řeknou, jak věci stojí, oba plně offline:

npx tgcloud status     # co se změnilo lokálně oproti nasazené kopii
npx tgcloud diff       # rozdíly řádek po řádku

Hned po init je všechno nové a nic není ještě nasazeno. status zobrazí startovací soubory čekající na nahrání.

4. Nasaďte

Nahrajte své moduly do cloudu:

npx tgcloud push

push nahraje všechny změněné moduly v jedné atomické dávce a aktualizuje váš lokální záznam o tom, co cloud nyní drží. Váš bot je živý: otevřete ho v Telegramu a pošlete mu zprávu — startovací obsluha ji odrazí zpět.

Nasazení nikdy nezasahuje do vaší databáze. Nahrávání kódu a změna schématu databáze jsou záměrně oddělené kroky, takže nasazení kódu vás nikdy nemůže překvapit migrací dat. K tomu je určen další krok.

5. Přidejte tabulku databáze

Ať bot něco pamatuje. Otevřete tgcloud/schema.js a deklarujte tabulku:

import { table, integer, text, sql } from 'sdk/db';

export const messages = table('messages', {
  id:      integer('id').primaryKey({ autoIncrement: true }) ,
  chatId:  integer('chat_id').notNull() ,
  text:    text('text') ,
  created: integer('created_at', { mode: 'timestamp' }).default(sql`(unixepoch())`) ,
});

Nasaďte schéma, pak ho aplikujte na databázi:

npx tgcloud push       # nahraje nové schéma
npx tgcloud migrate    # vytvoří tabulku `messages`

push nahlásí, že schéma je nekonzistentní a ukáže vám čekající změnu, ale nic neaplikuje. migrate vás provede změnou a po vaší potvrzení vytvoří tabulku. Tento dvouhodinový model — a co se stane s rizikovějšími změnami, jako jsou dropy — je pokryt v Migracích .

6. Ukládejte a čtěte data

Nyní použijte tabulku ze své obsluhy. Upravte tgcloud/handlers/message.js:

import { api, db } from 'sdk';
import { eq } from 'sdk/db';
import { messages } from '../schema.js';

export default async function (message) {
  // Uložte tuto zprávu.
  await db.insert(messages)
    .values({ chatId: message.chat.id, text: message.text })
    .run();

  // Spočítejte, kolik jich máme uložených pro tento chat.
  const count = await db.$count(messages, eq(messages.chatId, message.chat.id));

  await api.sendMessage({
    chat_id: message.chat.id,
    text: `Uloženo. To je ${count} zpráva/zprávy z tohoto chatu zatím.`,
  });
}

Nasaďte aktualizovanou obsluhu s npx tgcloud push, pak pošlete svému botovi několik zpráv a sledujte, jak se počítadlo zvyšuje. Databáze přetrvává mezi spuštěními — to je paměť vašeho bota.

7. Testujte bez nasazení

Nemusíte nasazovat, abyste vyzkoušeli změnu. npx tgcloud run spustí obsluhu na platformě pomocí vašich lokálních souborů, bez jejich publikování:

npx tgcloud run handlers/message '{ chat: { id: 1 }, text: "ahoj" }'

Argumentem je payload, který obsluha obdrží — pro handlers/message to je Message — napsaný v JSON5 (takže můžete vynechat uvozovky u klíčů). Příkaz vypisuje cokoli, co obsluha zalogovala přes console.*, návratovou hodnotu a čas, který to trvalo. Toto je nejužší cyklus pro iteraci logiky — žádné nasazení, žádné čekání na skutečnou zprávu.

8. Udržujte synchronizaci

Jak pracujete, hrstka příkazů udržuje váš lokální projekt a cloud v souladu: npx tgcloud status ukazuje, co se změnilo, npx tgcloud push nasazuje, npx tgcloud pull přivádí váš lokální projekt do souladu s cloudem, npx tgcloud fetch kontroluje, co je nasazeno, bez úpravy vašich souborů, a npx tgcloud reset zahazuje lokální změny.

Pokud dva lidé (nebo dva stroje) nasadí na stejného bota, platforma detekuje konflikt a push se zastaví, aby vám umožnil nejprve pull — nikdy nemůžete tiše přepsat práci někoho jiného. Viz Zůstat v synchronizaci .

Tvorba s AI

Preferujete tvorbu s AI asistentem — nebo je jediným kodérem ve vašem týmu AI? Stále můžete dodat bota. Udělali jsme první krok k tomu, aby se AI agent cítil jako doma v projektu: každý nový je generován s AGENTS.md a referencí docs/tgcloud-sdk.md, které nástroje pro agentní kódování čtou automaticky.

Spolu s malým, samostatným runtime — jedním SDK, žádnými npm balíčky k řešení — to dává asistentovi dobrou startovní pozici k zvyklostem, které generický kódovací nástroj zde obvykle vynechává: relativní importy s .js, žádné cizí klíče, každý db hovor je asynchronní, jedna obsluha na typ aktualizace a dvouhodinový tok push/migrate.

Zkuste to:

npm create @tgcloud/bot my-bot
cd my-bot
opencode            # nebo Claude Code, Cursor, … — jakýkoli agent, který čte AGENTS.md

Pak se jen zeptejte prostým jazykem:

Napiš bota, který si pamatuje seznam úkolů pro každou osobu — přidej položku, když pošle text, a zobraz celý seznam, když pošle /list.

Asistent upraví schema.js a vaše obsluhy za vás; vy zkontrolujete, okamžitě otestujete změnu pomocí npx tgcloud run, pak ji pustíte do produkce pomocí npx tgcloud push a npx tgcloud migrate. AGENTS.md je součástí vašeho projektu — upravte ho, jak bot roste, aby pokyny zůstaly přesné.

Na cestách s BotFather

Pouze s telefonem? Celý projekt žije také v @BotFather — otevřete svého bota → Serverless a dostanete vše, co CLI spravuje, na dotykové obrazovce:

  • Obsluhy — vytvářejte, upravujte a testujte spouštěním obsluh aktualizací; BotFather udržuje webhook v synchronizaci s obsluhami, které máte (stejný stav Synchronizováno / Nesynchronizováno, který hlásí CLI).
  • Koncové body — funkce, které volá vaše Mini Aplikace.
  • Knihovna — vaše sdílené moduly lib/.
  • Databáze — upravte schema.js ve stejném syntaxi podobném Drizzle, zkontrolujte čekající změny a aplikujte je; Uložit nasazuje.
  • Soubory Mini Aplikace — frontend, který vaši uživatelé otevírají v Telegramu.
  • CLI přístup — získejte CLI přístupový token zde, když se vrátíte ke klávesnici.

Je to jeden a ten samý cloudový projekt, takže můžete začít obsluhu na telefonu a později ji npx tgcloud pull do svého notebooku — nic není svázáno s jedním klientem. Spuštění obsluhy dokonce zobrazí její výstup Konzole přímo v chatu, stejně jako npx tgcloud run .

Projekty a moduly

Serverless projekt je běžná složka pod správou verzí. Všechno, co platforma spouští, sedí v jedné složce, tgcloud/; zbytek projektu je váš — frontend mini-aplikace, konfigurace, dokumentace. Pro bota není žádný build step, žádný node_modules za běhu a žádný vstupní bod serveru.

Anatomie projektu

example_bot/
├─ tgcloud/             # nasazená část — nic jiného není
│  ├─ handlers/         # obsluhy aktualizací — ploché, jen jedna úroveň
│  │  ├─ message.js
│  │  └─ callback_query.js
│  ├─ endpoints/        # funkce, které volá vaše Mini Aplikace — ploché, jen jedna úroveň
│  │  └─ getProfile.js
│  ├─ lib/              # sdílené moduly; podadresáře povoleny
│  │  ├─ reply.js
│  │  └─ internal/util.js
│  └─ schema.js         # schéma databáze — jeden soubor
└─ .tgcloud/            # stav CLI, ignorováno v git

Pouze soubory .js uvnitř tgcloud/ — schema.js, lib/, handlers/, endpoints/ — jsou nasazeny. Všechno ostatní — Markdown, konfigurační soubory, složka .tgcloud/, cokoli mimo tgcloud/ — zůstává na vašem stroji. Názvy modulů jsou relativní k tgcloud/. Soubor tgcloud/handlers/message.js je modul handlers/message, a to je název, který npx tgcloud run bere.

Upgradujete starší projekt? Projekty vytvořené před verzí CLI 0.2.0 držely schema.js, lib/ a handlers/ v kořeni projektu. Spusťte npx tgcloud upgrade jednou k jejich přesunutí do tgcloud/.

schema.js — vaše databáze. Deklaruje tabulky jako pojmenované exporty pomocí schema DSL a žije v tgcloud/ jako jediný soubor. Nasazuje se jako každý jiný modul, ale jeho nasazení nikdy nemění databázi — změny schématu se aplikují odděleně pomocí npx tgcloud migrate. Viz Databáze .

lib/ — sdílený kód, cokoli chcete znovu použít napříč obsluhami: čisté pomocné funkce, vrstvy přístupu k databázi, formátování, integrace s externími službami. lib/ je jediná složka, která může obsahovat podadresáře (lib/internal/util.js, lib/payments/stripe.js), takže můžete organizovat větší kódovou základnu, jak se vám líbí. Moduly v lib/ se nikdy nespouštějí přímo platformou; existují, aby byly importovány obsluhami a navzájem.

handlers/ — vstupní body vašeho bota. Každý soubor odpovídá jednomu typu aktualizace Telegramu, a platforma směruje každou příchozí aktualizaci na odpovídající obsluhu:

Soubor Obsluhuje
handlers/message.js Nově příchozí zprávy
handlers/inline_query.js Nově příchozí inline dotazy
handlers/callback_query.js Nově příchozí callback dotazy
… jakýkoli jiný typ aktualizace Bot API

handlers/ je ploché — žádné podadresáře. export default obsluhy je funkce, kterou platforma volá (viz Obsluhy ).

Typ aktualizace je obsloužen pouze pokud jeho soubor obsluhy existuje a není prázdný. Pokud neexistuje handlers/<typ>.js — nebo je soubor prázdný — aktualizace tohoto typu jsou ignorovány a platforma pro ně nic nespustí. Takže udržujte pouze ty obsluhy, které skutečně potřebujete: každá, kterou přidáte, je dalším typem aktualizace, na který se váš bot probudí, aby zpracoval, a vynecháním ostatních znamená, že Telegram nespustí váš kód pro aktualizace, které byste stejně zahodili.

K vygenerování nové obsluhy spusťte npx tgcloud add handlers/<typ> .

endpoints/ — serverová část vaší Mini Aplikace. Každý soubor je funkce, kterou Mini Aplikace volá jménem, takže endpoints/getProfile.js odpovídá POST /api/getProfile. Platforma ověřuje, že volání přichází od uživatele Telegramu uvnitř vaší Mini Aplikace, než se spustí váš kód, a předává vám tohoto uživatele. endpoints/ je ploché, stejně jako handlers/. Viz Koncové body .

.tgcloud/ — lokální stav stroje spravovaný výhradně CLI, včetně vašich uložených přihlašovacích údajů. Je ignorován v git a nikdy byste z něj neměli číst ani do něj ručně zapisovat — místo toho použijte příkazy CLI.

Systém modulů

Za běhu modul vidí přesně dvě věci: platformní SDK a ostatní moduly ve vašem projektu. Neexistují žádné npm balíčky, žádný souborový systém a žádná síť kromě fetch SDK.

Importujte své vlastní moduly způsobem, jakým byste to dělali v jakémkoli JavaScriptovém projektu — relativní cestou, s příponou .js — a SDK svým jménem:

// tgcloud/handlers/message.js
import { users } from '../schema.js';              //  modul schématu
import { addItem } from '../lib/cart.js';          //  modul lib
import { format } from '../lib/internal/fmt.js';  //  vnořený modul lib
import { db, api, fetch } from 'sdk';              //  platformní SDK
// tgcloud/lib/cart.js
import { format } from './internal/fmt.js';        //  sousední modul
import { users } from '../schema.js';

Relativní import musí končit na .js a nemůže vést mimo tgcloud/:

import { users } from '../schema';       //  přidejte .js
import config from '../../config.js';    //  mimo tgcloud/
import { z } from 'zod';                 //  žádné npm balíčky

Každý modul má také název — jeho cestu uvnitř tgcloud/ bez přípony, jako schema, lib/cart nebo handlers/message. Tento název je to, co používá npx tgcloud run a výstup CLI, a modul lze importovat i tímto názvem (from 'lib/cart', bez přípony). V kódu upřednostňujte relativní importy. Jsou to ty, které váš editor rozumí — skok na definici, automatické doplňování, přejmenování.

Co je k dispozici za běhu, jsou přesně dvě věci: sdk a jeho podmoduly (sdk/db, sdk/api, sdk/fetch) — celá povrch platformy, viz SDK — a vaše vlastní moduly v tgcloud/. To je kompletní seznam. Pokud váš kód importuje cokoli jiného, nerozřeší se. Toto omezení je to, co udržuje moduly rychlé k načtení a bezpečné ke spuštění.

Obsluhy

Obsluha je modul v handlers/, jehož výchozí export platforma vyvolává, když dorazí odpovídající aktualizace.

// handlers/message.js
import { api } from 'sdk';

export default async function (message) {
  await api.sendMessage({
    chat_id: message.chat.id,
    text: `Řekl jsi: ${message.text ?? '(žádný text)'}`,
  });
}

Obsluha přijímá jako argument payload aktualizace — platforma vám rozbalí Telegram Update . handlers/message.js dostane Message (tj. update.message); handlers/callback_query.js dostane CallbackQuery ; a tak dále. Druhým argumentem obsluhy je kontextový objekt pro každé spuštění, ctx. Nese syrový Update jako ctx.update — sáhněte po něm, když potřebujete něco mimo payload, jako update_id.

Obsluhu můžete testovat bez skutečné aktualizace. npx tgcloud run ji spustí na platformě s poskytnutým payloadem a vaším aktuálním lokálním kódem:

npx tgcloud run handlers/message '{ chat: { id: 1 }, text: "ahoj" }'

Argumentem je payload — stejný objekt, který obsluha přijímá — v JSON5. Pro dodání ctx obsluhy (její druhý argument) přidejte --ctx, např. --ctx '{ update: { update_id: 1 } }'. Toto běží proti vašim lokálním souborům, takže můžete vyzkoušet změny před jejich nasazením. Viz run .

Koncové body

Koncový bod je modul v endpoints/, který volá vaše Mini Aplikace — místo pro cokoli, co frontend nemůže udělat sám, jako je čtení a zápis do databáze, volání Bot API nebo komunikace s externími službami s vašimi tajemstvími.

// endpoints/getProfile.js
import { db, EndpointError } from 'sdk';
import { eq } from 'sdk/db';
import { profiles } from '../schema.js';

export default async function (input, ctx) {
  const user = ctx.initData.user;
  const profile = await db.select().from(profiles).where(eq(profiles.userId, user.id)).get();
  if (!profile) throw new EndpointError('Profil nenalezen', { userId: user.id });
  return profile;
}

První argument, input, je JSON objekt, který Mini Aplikace poslala. Druhý, ctx, nese ctx.initData — init data Mini Aplikace (user, chat, start_param, auth_date, …), již ověřená platformou. Má stejný tvar jako Telegram.WebApp.initDataUnsafe na klientovi, ale zde jí můžete důvěřovat. Volání bez platných init dat je odmítnuto s 401 před spuštěním vašeho kódu, takže ctx.initData.user je opravdu osoba, která otevřela vaši Mini Aplikaci. Koncové body lze volat pouze tímto způsobem — z Mini Aplikace vašeho bota, uživatelem Telegramu — takže neexistuje žádné veřejné API k ochraně a žádné přihlášení k vytvoření.

Cokoli koncový bod vrátí, je odesláno zpět jako JSON. Pro odmítnutí volání throw new EndpointError(popis, parametry?). Mini Aplikace získá popis a objekt parametry tak, jak jsou. Pokud potřebujete větvení na základě druhu chyby na frontendu, vložte kód do parametry — { code: 'OUT_OF_STOCK' }. Jakákoli jiná výjimka je hlášena jako obecná interní chyba a skutečná zpráva jde pouze do vašich logů, takže interní chyby neuniknou na klienta.

Volání koncového bodu z Mini Aplikace

Mini Aplikace volá koncový bod jménem pomocí Telegram.WebApp.Serverless.call z oficiálního Mini Apps SDK , telegram-web-app.js — skript, který vaše Mini Aplikace již zahrnuje. Posílá za vás init data a zavolá zpět s chybou nebo výsledkem koncového bodu:

Telegram.WebApp.Serverless.call('getProfile', { lang: 'en' }, function (err, profile) {
  if (err) {
    // err.message — popis; err.parameters — co nesl váš EndpointError
    if (err.type === 'ENDPOINT_ERROR') showMessage(err.message);
    return;
  }
  render(profile);
});

Vstup je volitelný a musí být prostý objekt. Bez něj je volání Serverless.call('getProfile', callback).

err.type je 'ENDPOINT_ERROR', když vaše obsluha hodila EndpointError. Jakákoli jiná hodnota znamená, že platforma odmítla volání nebo obsluha neočekávaně selhala, například 'UNAUTHORIZED', když chybí nebo jsou neplatná init data.

Názvy koncových bodů jsou jako JavaScriptové identifikátory — písmena, čísla a _, nezačínající číslicí (getProfile, place_order). Vygenerujte jeden pomocí npx tgcloud add endpoints/<název> , a vyzkoušejte ho bez Mini Aplikace pomocí run , sami si dodáte init data:

npx tgcloud run endpoints/getProfile '{}' --ctx '{ initData: { user: { id: 1, first_name: "Ann" } } }'

Co se nasazuje

Když spustíte npx tgcloud push, CLI shromáždí všechny soubory .js pod schema.js, lib/, handlers/ a endpoints/, a pošle přesně tento soubor jako prostor modulů vašeho projektu — spolu se složkou sestavení Mini Aplikace, pokud je pojmenována v tgcloud.jsonc (viz níže). Vše, co je přítomno v cloudu, ale chybí ve vašem projektu, je odstraněno, takže nasazený stav vždy zrcadlí vaši složku — včetně smazání. Nic mimo tgcloud/ není nasazeno jako kód — zbytek kořene projektu je váš. Uvnitř tgcloud/ je soubor .js, který není v jedné z těchto lokací (tgcloud/util.js, tgcloud/helpers/x.js), označen, takže se tiše nenasadí. Markdown, tečkové soubory a .tgcloud/ se nikdy nenasazují.

Frontend Mini Aplikace

Kořen projektu je váš, a je to přirozené místo pro frontend vaší Mini Aplikace — postavený s React, Vue, Svelte nebo prostým HTML, pomocí Vite nebo jakéhokoli jiného bundleru. Serverless hostuje jeho výstupy sestavení vedle vašeho bota. Jedno push nasadí kód a statické soubory dohromady, a aplikace je obsluhována na adrese https://app<app_id>.tgcloud.ai/ (push vypíše přesnou adresu — použijte ji jako URL vaší Mini Aplikace v @BotFather ).

Hlášení zneužití. Mini Aplikace na tgcloud.ai jsou publikovány nezávislými vývojáři. Pro hlášení nelegálního obsahu nebo zneužití na subdoméně app<app_id>.tgcloud.ai emailem na [email protected] .

Pro začátek vytvořte nejprve frontend, s jakýmkoli nástrojem chcete, a pak na něj aplikujte Serverless:

npm create vite@latest example_app   # nebo create-vue, …
cd example_app
npm create @tgcloud/bot .

Tvůrce projektů nikdy nepřepisuje vaše soubory. Přidá složku tgcloud/ vedle frontendu, rozpozná složku sestavení a přidá skript npm run deploy, který sestaví aplikaci a nasadí ji spolu s botem.

Co se hostuje, je nastaveno v tgcloud.jsonc, konfiguračním souboru projektu v jeho kořeni:

{
  "$schema": "./node_modules/@tgcloud/cli/schema/tgcloud.json",
  "static": {
    "source": "dist",
    "spa": true,
    "immutable": ["/assets/*"],
    "headers":   [{ "path": "/*", "set": { "Referrer-Policy": "no-referrer" } }],
    "redirects": [{ "from": "/old", "to": "/new", "status": 301 }]
  }
}

Pouze "source" je vyžadováno. Je to složka sestavení, relativně k kořeni projektu. Další klíče "static" konfigurují, jak je obsluhována (níže). init vám vytvoří tgcloud.jsonc. Když rozpozná frontend, také vyplní "source":

Frontend init nastaví "source" na
Vite — React, Vue, Svelte, Solid, … dist
Next.js, Nuxt, SvelteKit, React Router, TanStack Start, Angular nic — nejprve nakonfigurujte statický export, pak si sami nastavte "source" na jeho složku

S jakýmkoli jiným nastavením si sami nastavte "source" na vaši složku sestavení.

Řádek $schema poskytuje vašemu editoru automatické doplňování a validaci pro každý klíč. Soubor je JSON s komentáři a povolenými koncové čárky. Pokud jiný nástroj potřebuje číst jako prostý JSON, pojmenujte ho místo toho tgcloud.json — pak musí být striktní JSON.

Sestavte před nasazením. push nahraje složku přesně tak, jak je na disku — kromě souborů a složek, jejichž název začíná tečkou, které jsou přeskočeny — a nikdy nespustí váš build, takže prostý npx tgcloud push po úpravě frontendu by nasadil předchozí build. Použijte npm run deploy (spustí váš build skript, pak tgcloud push) , nebo sestavte sami nejprve.

Možnosti obsluhy:

  • spa — obsluhuje index.html pro cesty, které neexistují a nemají příponu souboru, takže router na straně klienta funguje při obnovení. Cesty s příponou stále dostanou 404.
  • immutable — vzory pro soubory hashované obsahem (Vite /assets/*), které prohlížeče mohou cachovat po dobu jednoho roku. Používejte je pouze pro soubory hashované obsahem. Jakýkoli jiný soubor, který se zde shoduje, by v prohlížečích zůstal zastaralý po dobu jednoho roku a neexistuje způsob, jak ho získat zpět.
  • headers — hlavičky odpovědí pro cesty, které pravidlo odpovídá (přesná cesta, nebo prefix končící na /*). Hodnota null odstraní hlavičku.
  • redirects — pravidla přesměrování, kde první shoda vyhrává. Existující soubor vždy předchází pravidlu. status je 301, pokud ho nenastavíte.
  • "static": "dist" — zkratka pro { "source": "dist" }, když nic jiného nepotřebujete.
  • "static": false — při příštím nasazení odstraní nasazené stránky. Vymazání klíče místo toho pouze zastaví správu statických souborů tímto projektem a cokoli nasazeného zůstane.

Stránka je obsluhována z kořene svého vlastního hostitele, takže výchozí base bundleru (/) je správný. Telegram Web otevírá Mini Aplikace v iframe, takže nepřidávejte X-Frame-Options nebo restriktivní politiku frame-ancestors.

Stejný hostitel odpovídá vašim koncovým bodům pod /api/ — tam jde Telegram.WebApp.Serverless.call('getProfile', …). Nepokládejte tam do složky sestavení vlastní soubory pod /api/.

Databáze

Každý bot dostane svou vlastní databázi — úložiště založené na SQLite, které přetrvává mezi spuštěními a je dostupné každému modulu přes db. Své tabulky popisujete v schema.js pomocí malé, typované DSL; čtete a zapisujete je pomocí plynulého stavitele dotazů; a vyvíjíte je pomocí zkontrolovaných migrací.

Znát Drizzle ORM ? Pak už víte, jak komunikovat s databází zde. Schema DSL a stavitel dotazů sledují Drizzle — stavitelé sloupců, select().from().where(), operátory, onConflictDoUpdate, .returning() a sql tag — všechno funguje tak, jak byste čekali, takže čtení a zápis dat je známé API, které už používáte. Stačí ho importovat z sdk/db a několik specifických platformních záležitostí (zejména žádné cizí klíče ) jsou upozorněny tam, kde se vyskytnou.

Deklarace tabulek

Tabulky jsou pojmenované exporty v schema.js. Volání table() sestavuje popis při načtení (nedotýká se databáze); platforma objeví exportované tabulky, když nasadíte schema.js a migruje databázi, aby odpovídala.

import { table, integer, text, boolean, json, index, sql } from 'sdk/db';

export const users = table('users', {
  id:      integer('id').primaryKey({ autoIncrement: true }) ,
  tgId:    integer('tg_id').unique() ,
  name:    text('name').notNull() ,
  lang:    text('lang').default('en') ,
  isAdmin: boolean('is_admin').default(false) ,
  prefs:   json('prefs') ,
  created: integer('created_at', { mode: 'timestamp' }).default(sql`(unixepoch())`) ,
}, (t) => ({
  createdIdx: index('idx_users_created').on(t.created) ,
}));

table(název, sloupce, doplňky?):

  • název je název SQL tabulky;
  • sloupce je mapa JS vlastnost → definice sloupce;
  • doplňky je volitelná callback funkce (t) => ({ … }) kde t zpřístupňuje sloupce (t.created je odkaz na daný sloupec) — deklarujte zde indexy a omezení na úrovni tabulky.

Typy sloupců

Továrna SQLite typ Poznámky
text() TEXT
integer() INTEGER
real() REAL alias float()
numeric() NUMERIC
blob() BLOB čte/píše Uint8Array
boolean() INTEGER ukládáno jako 0/1, čteno jako true/false
json() TEXT automatické JSON.stringify / JSON.parse

Argument názvu sloupce je volitelný — vynechte ho a použije se JS klíč. Možnost mode řídí, jak se hodnoty převádějí mezi SQLite a JavaScriptem.

mode Uloženo jako JS hodnota
boolean INTEGER 0/1 boolean
json TEXT (JSON) jakýkoli objekt/pole
timestamp INTEGER (unix sekundy) Date
timestamp_ms INTEGER (unix ms) Date
bytes BLOB Uint8Array

boolean() a json() jsou zkratky pro integer(název, { mode: 'boolean' }) a text(název, { mode: 'json' }).

blob() čte a zapisuje Uint8Array — runtime nemá Node Buffer (a Buffer je podtřída Uint8Array, takže toto je přenosný základní typ). mode výše řídí pouze to, jak je hodnota kódována, nezávisle na typu úložiště sloupce: takže blob('col', { mode: 'json' }) je BLOB protějšek json() — stejné JSON kódování, uložené ve sloupci BLOB, nikoli TEXT.

STRUČNĚ ŘEČENO: blob() používá Uint8Array. mode řídí kódování, ne úložiště, takže blob(..., { mode: 'json' }) ukládá JSON jako BLOB, zatímco json() ho ukládá jako TEXT.

Modifikátory sloupců

Modifikátory sloupců se řetězí na sloupec.

integer('id').primaryKey({ autoIncrement: true })
text('name').notNull()
text('tg').unique()
text('lang').default('en')
integer('created_at', { mode: 'timestamp' }).default(sql`(unixepoch())`)
text('slug').generatedAlwaysAs(sql`lower(name)`, { mode: 'stored' })  // nebo 'virtual'
text('email').deprecated('nahrazeno login')   // označí sloupec k odstranění

.default() na sloupci json() vám zakóduje hodnotu; sql…``default je předán beze změny..deprecated()` je terminální — viz Migrace .

Indexy a omezení

Indexy a omezení jsou deklarovány v callbacku extras, kde jsou sloupce v rozsahu.

table('t', { /* … */ }, (t) => ({
  uq:     unique('uq_email').on(t.email) ,
  chk:    check('chk_done', sql`${t.done} in (0, 1)`) ,
  idx:    index('idx_name').on(t.col) ,
  uidx:   uniqueIndex('uidx_email').on(t.email) ,
  lower:  index('idx_lower').on(sql`lower(${t.email})`) ,          // index výrazu
  active: index('idx_active').on(t.userId).where(sql`done = 0`) ,  // částečný index
}));

Modifikátory na úrovni tabulky řetězí po table(...): .strict(), .withoutRowid(), .deprecated('důvod').

Žádné cizí klíče

Runtime běží s PRAGMA foreign_keys vypnutým. Deklarovaný cizí klíč by byl tiše nečinný — žádné kaskády, žádná ochrana proti sirotkům — což je horší než nemít žádný vůbec, takže DSL to znemožňuje. Konkrétně .references() a foreignKey() na úrovni tabulky vyhodí výjimku při deklaraci a schéma, které je používá, se nenasadí.

Měli byste modelovat vztahy s prostými sloupci (userId: integer('user_id')) a vynucovat integritu ve svém aplikačním kódu: nejprve vkládat rodiče před potomky, mazat potomky před rodiči, řádně zpracovávat chyby a zametat sirotky pomocí LEFT JOIN … WHERE parent.id IS NULL, když je potřebujete.

Absence cizích klíčů je záměrné omezení, nikoli opomenutí. Počítejte s ním brzy při plánování vašeho bota.

Dotazování

db je plynulý stavitel dotazů. Každý dotaz je asynchronní — koncové metody (.all(), .get(), .values(), .run()) vrací Promises, takže vždy await.

import { db } from 'sdk';
import { eq, and, desc, asc, count, sql } from 'sdk/db';
import { users, todos } from '../schema.js';

await db.select().from(todos).all();                          // všechny řádky
await db.select().from(todos).where(eq(todos.id, 1)).get();   // první řádek nebo null
await db.select().from(todos).values();                       // řádky jako pole hodnot

await db.select().from(todos)
  .where(and(eq(todos.userId, uid) , eq(todos.done, false)))
  .orderBy(desc(todos.priority) , asc(todos.id))
  .limit(10).offset(20)
  .all();

// vlastní projekce: { alias: columnRef | sqlExpr | aggregate }
await db.select({ id: todos.id, title: todos.text, n: count() })
  .from(todos).groupBy(todos.userId).having(sql`count(*) > ${1}`).all();

// počet řádků — pomocník, ne koncové zařízení stavitele:
await db.$count(todos);                        // všechny řádky
await db.$count(todos, eq(todos.done, false)); // s filtrem
  • Řetězitelné: .where(), .orderBy(), .limit(), .offset(), .groupBy(), .having(), .distinct().
  • Koncové: .all(), .get(), .values().
  • Počítání řádků s db.$count(tabulka, kde?) (nebo count() v projekci).

Vložení, aktualizace, smazání:

await db.insert(todos).values({ userId: 1, text: 'Koupit mléko' }).run();
await db.insert(todos).values([{ text: 'A' }, { text: 'B' }]).run();   // dávka
await db.insert(todos).values({ text: 'X' }).returning().run();        // RETURNING *

await db.insert(users).values({ tgId: 42, name: 'Ann' })
  .onConflictDoUpdate({ target: users.tgId, set: { name: 'Ann' } }).run();

await db.update(todos).set({ done: true }).where(eq(todos.id, 1)).run();
await db.delete(todos).where(eq(todos.id, 1)).run();

Všimněte si, že dávkové vložení je jeden příkaz, takže je omezeno limitem proměnných SQLite (řádky × sloupce); pokud ho překročíte, vložení selže — rozdělte ho sami.

Operátory se importují z sdk/db:

import {
  eq, ne, gt, gte, lt, lte,
  like, notLike,
  isNull, isNotNull, and, or, not,
  between, notBetween, inArray, notInArray,
  count, sum, avg, min, max,
  asc, desc,
} from 'sdk/db';

.where(a, b) s více argumenty je stejné jako and(a, b). Druhý argument srovnání je hodnota ve výchozím nastavení, ale může být jiný sloupec nebo sql…`` — např.eq(a.x, b.y). Agregace (count/sum/avg/min/max) jsou fragmenty sql pro projekci .select({ … })`.

Syrové SQL — když stavitel nestačí, přejděte na syrové SQL. Metoda je vybrána podle metody: db.run pro zápisy, db.all pro mnoho řádků, db.get pro jeden řádek, db.values pro řádky jako poziční pole.

await db.run('UPDATE todos SET done = 1 WHERE id = :id', { ':id': 5 });
// → { rowsAffected: 3, lastInsertRowid: 0, rows: [] }

await db.all(sql`SELECT * FROM todos WHERE done = ${false}`);
await db.get(sql`SELECT count(*) AS c FROM todos`);
await db.values(sql`SELECT id, text FROM todos`);   // → [[1, 'koupit mléko']]

Zápis se vyřeší do výsledku spuštění — rowsAffected, lastInsertRowid a rows, které nesou klauzuli RETURNING, pokud SQL měla. Sáhněte po db.values, když dotaz promítá dva sloupce se stejným názvem (spojení jako SELECT u.id, o.id): db.all klíčuje řádky podle názvu sloupce, takže druhé id by přepsalo první.

Tag sql`…` přemění ${hodnota} na vázaný parametr a ${tabulka.sloupec} na identifikátor a vloží vnořené sql fragmenty. Pro literál bez parametrů použijte sql.raw('…').

Syrové dotazy nejsou vázány na tabulku, takže jejich řádky se vrací bez konverze režimu — boolean je 0/1, JSON sloupec je řetězec, timestamp je číslo. Pouze stavitel vázaný na tabulku konvertuje hodnoty.

Migrace

Vaše databáze se při růstu vašeho bota mění. Platforma to udržuje bezpečné oddělením nasazení kódu od změny dat a klasifikací každé změny schématu podle toho, jak je riziková.

Nasazení nikdy nezasahuje do databáze. Když spustíte npx tgcloud push změněného schema.js, platforma zaznamená nové schéma a řekne vám, jak by se databáze změnila — ale nic neaplikuje:

npx tgcloud push       # nasadí schema.js; hlásí čekající změny DB
npx tgcloud migrate    # zkontrolujte a aplikujte je

npx tgcloud migrate vypočítá rozdíl mezi vaším schématem a živou databází a provede vás tím krok za krokem s běžícím čítačem [N/M]:

  • Krátké shrnutí všech čekajících změn.
  • Bezpečné změny, aplikované dohromady v jednom kroku po vaší potvrzení.
  • Varování (dropy, pomalé operace), jedna po druhé, každé samostatně potvrzené.
  • Manuální změny, zobrazené s důvodem a navrhovaným postupem, automaticky neaplikované.
  • Nedokumentované objekty (v databázi, ale ne ve vašem schématu), zobrazené pro informaci.

Na konci se zobrazí shrnutí: aplikováno, přeskočeno, čeká na manuální opravu, není ve schématu.

Viz Migrace pro model.

Mazání věcí

Smazání tabulky nebo sloupce ze schema.js neodstraní — to by učinilo náhodné smazání katastrofálním. Chcete-li něco odstranit, označte to jako zastaralé:

// smazat sloupec
text('email').deprecated('nahrazeno login')

// smazat celou tabulku
export const oldSessions = table('old_sessions', { /* … */ }).deprecated('nepoužito');

Při dalším migrate se zastaralý objekt objeví jako varovný status drop, který potvrdíte individuálně. Jakmile je pryč, deklaraci odstraňte.

Změna typu sloupce

Změny typu jsou manuální — SQLite je nemůže vždy provést na místě a převod existujících hodnot je otázkou úsudku. migrate zobrazí změnu a její zdůvodnění; proveďte ji sami pomocí syrového SQL (db.run(...)), obvykle vytvořením nového sloupce nebo tabulky, kopírováním dat a prohozením.

SDK

Za běhu má modul jednu knihovnu: sdk. Seskupuje tři věci, které backend bota potřebuje — databázi, Telegram Bot API a odchozí HTTP — bez nutnosti instalace a bez konfigurace přihlašovacích údajů. Databáze (db) je popsána v Databáze ; tato sekce pokrývá api, fetch a globální console.

import { db, api, fetch, InputFile, BotApiError, EndpointError } from 'sdk';   // celá plocha
// nebo z podmodulů:
import { table, integer, text, eq, sql } from 'sdk/db';
import { api } from 'sdk/api';
import { fetch } from 'sdk/fetch';
Import Co to je
db Databáze — stavitel dotazů a schema DSL → Databáze
api Telegram Bot API — api.sendMessage(...) → níže
fetch Odchozí HTTP → níže
InputFile Soubor (byty + název) k nahrání — funguje s api i fetch → Soubory
EndpointError Odmítnutí volání Mini Aplikace s chybou, kterou může zobrazit → Koncové body

Importujte své vlastní projektové moduly relativní cestou s příponou .js (from '../schema.js', from '../lib/cart.js'). Viz Systém modulů .

Bot API

api vám poskytuje celé Telegram Bot API . Voljte jakoukoli metodu jako api.<metoda>(parametry). Každá současná — i budoucí — metoda Bot API funguje bez aktualizace SDK nutná.

import { api } from 'sdk';

const me = await api.getMe();                            // → odvinutý výsledek
await api.sendMessage({ chat_id: id, text: 'Ahoj!' });
await api.editMessageText({ chat_id, message_id, text: 'Aktualizováno' });
await api.answerCallbackQuery({ callback_query_id, text: 'Hotovo' });

Obal odpovědi je odvinut. Bot API normálně obaluje výsledky do { ok: true, result: … }. api vrací přímo result — getMe() se vyřeší na objekt uživatele, ne na obal. Parametry používají vlastní názvy Bot API se snake_case (chat_id, message_id, reply_markup, …).

Chyby vyvolávají BotApiError. Když Bot API vrátí { ok: false }, volání místo vrácení nulové hodnoty vyvolá BotApiError, takže ji nemůžete náhodně ignorovat. Chyba nese .code (chybový kód Bot API) , .description (lidsky čitelná zpráva) , .method (která metoda selhala) a .parameters (další data, jako retry_after u 429, nebo migrate_to_chat_id). Zachyťte ji pro zpracování očekávané chyby a zbytek znovu vyvolejte:

import { api, BotApiError } from 'sdk';

try {
  await api.deleteMessage({ chat_id, message_id });
} catch (e) {
  if (e instanceof BotApiError && e.code === 400) {
    // 400 = zpráva je už pryč; tady je to v pořádku.
  } else {
    throw e;
  }
}

Soubory

Pracujte s bajty souborů, nejen s file_id — nahrávejte nové soubory a stahujte existující.

Nahrání — InputFile. Obalte syrové bajty do InputFile (prostý držák: bajty, název souboru a volitelný MIME type) a předejte je kamkoli metoda Bot API očekává soubor:

import { api, InputFile } from 'sdk';

const doc = new InputFile(bytes, 'report.pdf', { type: 'application/pdf' });
await api.sendDocument({ chat_id, document: doc });

// soubory vnořené uvnitř parametrů také fungují — např. album:
await api.sendMediaGroup({ chat_id, media: [
  { type: 'photo', media: new InputFile(a, 'a.jpg', { type: 'image/jpeg' }) },
  { type: 'photo', media: new InputFile(b, 'b.jpg', { type: 'image/jpeg' }) },
]});

Bajty jsou streamovány do Telegramu jako skutečný multipart upload — soubor není buffrovaný celý do jednoho volání platformy — ale limity Bot API Telegramu stále platí: bot může momentálně nahrát až 50 MB na soubor (a 10 MB na fotografii).

Stáhnutí — podle file_id. Dva pomocníci stáhnou bajty souboru zpět. Platforma za vás přeloží file_id (běží getFile a stahuje obsah na pozadí), takže se nikdy nedotknete tokenu bota ani URL pro stažení:

const bytes = await api.getFileContent(file_id);          // → Uint8Array (celý soubor)

const { file, body } = await api.getFileStream(file_id);  // streamujte místo toho
file.file_unique_id;                                      // informace getFile
for await (const chunk of body) { /* Uint8Array */ }      // nebo vybufferujte: await stream.bytes()

Bot API Telegramu omezuje stažení getFile na 20 MB, takže to je horní limit pro oba pomocníky.

HTTP

fetch je fetch-like klient pro volání vnějšího světa — externích API, webhooků, čehokoli přes HTTP.

import { fetch } from 'sdk';

const res = await fetch('https://api.example.com/users', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'Pavel' }) ,
});
if (!res.ok) throw new Error(res.statusText);
const data = await res.json();

Odpověď zrcadlí webovou platformu: res.status, res.statusText, res.ok (true pro 200–299) , res.url, res.headers (.get(), .has(), .keys(), .entries()) a čtečky těla await res.json() / await res.text().

Tělo odpovědi můžete také číst inkrementálně jako stream — for await (const chunk of res.body) { … } — takto konzumujete události posílané serverem nebo výstup token po tokenu z AI API.

Pomocníci těla za vás nastaví odpovídající Content-Type:

await fetch(url, { method: 'POST', body: fetch.body.json({ a: 1 }) }); // application/json
await fetch(url, { method: 'POST', body: fetch.body.form({ a: 1 }) }); // x-www-form-urlencoded
await fetch(url, { method: 'POST', body: fetch.body.text('ahoj') });     // text/plain

Těla požadavků mohou také nést soubory a syrové bajty: předejte Uint8Array, InputFile, nebo FormData (pro multipart/form-data) jako body, a bajty jsou streamovány ven — takže můžete nahrát na externí API stejným způsobem, jakým posíláte soubory do Telegramu:

import { fetch, FormData, InputFile } from 'sdk';

const form = new FormData();
form.append('file', new InputFile(bytes, 'photo.jpg', { type: 'image/jpeg' }));
form.append('caption', 'z mého bota');
const res = await fetch('https://api.example.com/upload', { method: 'POST', body: form });

Jinak se chová jako standardní fetch, který už znáte, s dvěma omezeními:

  • Odpověď se čte jako text — await res.json() / await res.text(), nebo streamována kus po kusu pomocí res.body.
  • Celková odpověď je omezena na 30 MB. Toto omezení pokrývá celou odpověď — streamování s res.body vám umožňuje zpracovávat velké tělo inkrementálně, ale nezvyšuje limit.

Logování

Standardní globální console je k dispozici — nic k importování, je tam stejně jako v jakémkoli JavaScriptu. Jeho výstup je zachycen a zobrazen pomocí npx tgcloud run, což z něj činí váš primární nástroj pro ladění během vývoje.

console.log('zpracovávám', { chatId: id });   // log / debug — prosté
console.info('spuštěno');                     // info  — modré
console.warn('omezená rychlost');                // varování  — žluté
console.error(err);                          // chyba — červené, se sledem volání

Každý řádek je označen [soubor:řádek], odkud pochází. console.error a console.trace přidávají plný sled volání, zatímco console.warn ne. Když spustíte npx tgcloud run modul, tyto řádky se vypisují s barevným prefixem podle úrovně, časem od spuštění a původem — viz run .

Příkazový řádek

tgcloud je most mezi vaší projektovou složkou a cloudem. Generuje projekty, ukazuje vám, co se změnilo, nasazuje, spouští moduly a aplikuje migrace databáze. Potřebuje Node.js 18 nebo novější. Dva způsoby, jak ho získat:

# Doporučeno — vytvořte projekt s nainstalovaným CLI:
npm create @tgcloud/bot example_bot

# Nebo nainstalujte CLI globálně a inicializujte prázdnou složku:
npm install -g @tgcloud/cli
tgcloud init

NPM balíček je @tgcloud/cli a příkaz, který instaluje, je tgcloud. Každý příkaz funguje z jakékoli podsložky vašeho projektu.

Příkaz Účel
init Vygeneruje nový projekt v aktuální složce
add Vygeneruje nový modul (obsluha, koncový bod nebo modul knihovny)
login Propojí projekt s botem (uloží token)
status Zobrazí, co se změnilo lokálně oproti cloudu
diff Zobrazí rozdíly řádek po řádku
push Nasadí změněné moduly a sestavení Mini Aplikace do cloudu
migrate Aplikuje změny schématu na databázi
run Spustí modul na platformě bez nasazení
fetch Zkontroluje, co je nasazeno, bez úpravy lokálních souborů
pull Svede lokální soubory do souladu s cloudem
reset Zahodí lokální změny; obnoví ze stavu cloudu
webhook Zkontroluje a synchronizuje webhook spravovaný platformou
completion Vytiskne skript pro doplňování shellu (bash/zsh/fish)
upgrade Přesune projektové rozložení před verzí 0.2.0 do tgcloud/

Autentizace

Projekt je svázán s jedním botem pomocí jeho tokenu, který má formát app<id>:<secret>. Část app<id> je veřejná a může být vypsána; secret se nikdy neobjeví v logech ani chybách.

Token je řešen v tomto pořadí:

  1. Proměnná prostředí TGCLOUD_TOKEN — pro CI; nikdy není zapsána na disk.
  2. .tgcloud/credentials — zapsáno pomocí npx tgcloud login .
  3. Ani jedno — chyba vás odkáže na npx tgcloud login.

CLI nikdy nepožaduje token během příkazu. Přihlášení je vždy explicitním krokem login. Pokud se uložený token stane neplatným, CLI ho odstraní a požádá vás o opětovné login.

init

npx tgcloud init

Vygeneruje nový projekt v aktuálním adresáři: složku tgcloud/ s schema.js, lib/, handlers/ a startovací obsluhou, plus tgcloud.jsonc, AGENTS.md, docs/ a složku stavu .tgcloud/. Spusťte ho v kořeni existujícího frontendového projektu — přidá složku tgcloud/ a dokumentaci, a nikdy nepřepíše vaše soubory. Pokud rozpozná frontend, také ukáže na složku sestavení v tgcloud.jsonc a přidá skript deploy (sestavení, pak push) do vašeho package.json — pouze pokud již nemáte skript deploy. Viz Frontend Mini Aplikace .

init odmítá vytvořit projekt uvnitř jiného projektu. Opakované spuštění v kořeni samotného projektu je v pořádku a pouze přidá cokoli chybějícího.

add

npx tgcloud add <cíl>

Vygeneruje jeden nový modul, připojený a připravený k úpravám — obsluhu, koncový bod nebo modul lib/.

npx tgcloud add handlers/callback_query   # nová obsluha aktualizací
npx tgcloud add endpoints/getProfile      # nový koncový bod pro Mini Aplikaci
npx tgcloud add lib/cart                  # nový sdílený modul

Všimněte si, že add nikdy nepřepisuje existující soubor.

Cíl je cesta modulu (koncovka .js je volitelná). Pro handlers/ musí být název typ aktualizace Telegramu. Platforma inzeruje platnou sadu, takže neplatný název je odmítnut okamžitě. handlers/ a endpoints/ jsou ploché, zatímco lib/ může být vnořené (lib/payments/stripe).

Název modulu je povinný. Dodání pouze adresáře je chyba — ale užitečná: pro handlers/ vypíše typy aktualizací, které ještě nemáte, takže si můžete jeden zkopírovat.

$ npx tgcloud add handlers
Error: Specify a name, e.g. "handlers/callback_query". Available handlers/ types: callback_query, inline_query, chat_member, …

<Tab> doplňování nabízí stejnou sadu — viz completion .

Vygenerovaný soubor má živý export default, takže obsluha je aktivní hned, jak ji nasadíte — není nic k odkomentování. Nasaďte nový modul pomocí push .

login

npx tgcloud login

Zeptá se na váš CLI přístupový token — z @BotFather → váš bot → Serverless → CLI Access → Access token, jiný token než váš API token bota — ověří ho proti platformě a uloží ho do .tgcloud/credentials.

login je jediný příkaz, který se ptá na token, a vyžaduje interaktivní terminál. V CI nastavte místo toho TGCLOUD_TOKEN.

status

npx tgcloud status

Zobrazuje, pro každý soubor, co se změnilo mezi vaším pracovním adresářem a nasazenou kopií: změněné, nové, smazané, nezměněné. Funguje offline, porovnává se stavem cloudu od vašeho posledního push, pull nebo fetch. Také varuje před zbloudilými .js soubory uvnitř tgcloud/, které nebudou nasazeny.

Pro projekt s Mini Aplikací status také zobrazí, co se změnilo v jeho složce sestavení od posledního nasazení.

diff

npx tgcloud diff

Jako status, ale zobrazí skutečné rozdíly řádek po řádku pro změněné moduly. Také offline.

push

npx tgcloud push [soubory...]

Nasazuje váš projekt do cloudu v jedné atomické dávce.

Bez argumentů nasadí celý projekt a nasazený stav přesně zrcadlí vaši složku — moduly, které jste lokálně smazali, budou v cloudu odstraněny.

S argumenty souborů nebo adresářů (npx tgcloud push tgcloud/handlers/message.js, npx tgcloud push handlers/) nasadí pouze změny v těchto souborech. Cílený push nikdy nesmaže jiné moduly. Cíl může být zapsán jako cesta souboru (tgcloud/handlers/message.js) , cesta uvnitř tgcloud/ (handlers/message.js) nebo název modulu (handlers/message) — vše znamená stejný modul.

Jeho jediná možnost je --force — přeskočit kontrolu souběžnosti a přepsat cokoli v cloudu. Používejte ji pouze tehdy, když jste si jisti (viz Zůstat v synchronizaci ).

Pokud tgcloud.jsonc pojmenuje statickou složku, plný push ji nasadí ve stejné dávce — nahrají se pouze změněné soubory — a vypíše adresu stránky. Sestavte nejprve, protože push nikdy nespustí váš build (npm run deploy udělá obojí). npx tgcloud push dist/ nasadí pouze statické soubory a push, který pojmenuje pouze moduly, je nedotkne.

Po nasazení, pokud se schema.js změnil a databáze je nekonzistentní, push vypíše shrnutí čekajících změn a navrhne npx tgcloud migrate. Nikdy je sám neaplikuje.

migrate

npx tgcloud migrate

Aplikuje změny schématu na databázi. Vypočítá rozdíl mezi schema.js a živou databází, pak vás provede krok za krokem s běžícím čítačem [N/M]:

  • Krátké shrnutí všech čekajících změn.
  • Bezpečné změny, aplikované dohromady v jednom kroku po vaší potvrzení.
  • Varování (dropy, pomalé operace), jedna po druhé, každé samostatně potvrzené.
  • Manuální změny, zobrazené s důvodem a navrhovaným postupem, automaticky neaplikované.
  • Nedokumentované objekty (v databázi, ale ne ve vašem schématu), zobrazené pro informaci.

Na konci se zobrazí shrnutí: aplikováno, přeskočeno, čeká na manuální opravu, není ve schématu.

Viz Migrace pro model.

Možnosti: --dry-run (vytiskne vše, neaplikuje nic) , --safe (automaticky aplikuje bezpečné změny, přeskočí varování) , --yes (automaticky aplikuje bezpečné změny a každé varování, přeskočí manuální — použijte opatrně) , --local (porovná s vaším lokálním schema.js namísto nasazeného). Bez příznaku migrate vyžaduje terminál a v neinteraktivním prostředí selže místo aby hádal.

run

npx tgcloud run <modul> [argumenty] [--ctx <json5>]

Spustí obsluhu nebo koncový bod na platformě bez nasazení, pomocí vašich aktuálních lokálních souborů. Toto je rychlá vnitřní smyčka pro testování logiky.

  • <modul> — holý název (hledá se ve handlers/ a endpoints/) nebo cesta jako handlers/message. Holý název, který existuje v obou adresářích, musí být zapsán s jeho adresářem.
  • [argumenty] — payload předaný obsluze, napsaný v JSON5 , takže můžete vynechat uvozovky u klíčů. Je to objekt typu aktualizace, který vaše obsluha obdrží (např. Zpráva pro handlers/message), nebo input koncového bodu, a musí to být objekt — ne pole nebo holý řetězec.
  • --ctx <json5> — kontextový objekt modulu (jeho druhý argument), také JSON5. Použijte ho pro dodání toho, co váš kód čte z ctx — syrová aktualizace pro obsluhu, --ctx '{ update: { update_id: 1 } }', nebo init data pro koncový bod, --ctx '{ initData: { user: { id: 1 } } }'. run ho neověřuje, protože testujete svou logiku, ne kontroly platformy.
npx tgcloud run handlers/message '{ chat: { id: 1 }, text: "ahoj" }'

Platforma spustí modul proti prostoru modulů sestavenému z vašeho lokálního projektu (takže se použije i lokálně změněný lib/ kód) a vrátí návratovou hodnotu, cokoli zalogováno pomocí console.* a uplynulý čas. Velké argumenty čtěte ze souboru pomocí npx tgcloud run handlers/message "$(cat message.json5)".

fetch

npx tgcloud fetch

Zkontroluje, co je aktuálně nasazeno, bez úpravy vašich pracovních souborů. Poté status a diff porovnají s nejnovějším stavem cloudu. Užitečné pro zkoumání konfliktu, než se rozhodnete, jak ho vyřešit.

pull

npx tgcloud pull

Převede váš lokální projekt do souladu s cloudem aktualizací vašich pracovních souborů na nasazený stav.

pull nikdy nezapisuje složku sestavení vaší Mini Aplikace.

reset

npx tgcloud reset

Zahodí vaše lokální změny a obnoví moduly z posledního známého stavu cloudu. Použijte ho pro zahazení experimentu. Složka sestavení Mini Aplikace není dotčena.

webhook

npx tgcloud webhook
npx tgcloud webhook sync [--drop-pending]

Telegram doručuje aktualizace vašemu botovi prostřednictvím webhooku, který platforma spravuje za vás — nikdy ho nenastavujete ručně. npx tgcloud webhook zobrazí jeho aktuální stav: URL, seznam allowed_updates, kolik aktualizací je čekajících, poslední chybu doručení (pokud nějaká je) a zda je synchronizovaný s vašimi nasazenými obsluhami.

„Synchronizovaný“ znamená, že webhook ukazuje na platformu a jeho allowed_updates odpovídá obsluhám, které jste nasadili — takže Telegram doručuje přesně ty typy aktualizací, které obsluhujete, a nic jiného. Nasazení nové obsluhy (nebo její odstranění) může nechat webhook nesynchronizovaný, dokud nebude obnoven; npx tgcloud status to také označí.

npx tgcloud webhook sync to opraví — znovu ukáže webhook na platformu a znovu sestaví allowed_updates z vašich nasazených obsluh. Přidejte --drop-pending pro zahazení aktualizací, které Telegram již zařadil do fronty před synchronizací (jinak budou doručeny, jakmile bude webhook opět zdravý).

completion

Poznámka: tab-completion funguje pouze tehdy, když je holý tgcloud na vašem PATH — takže ho nainstalujte globálně (npm install -g @tgcloud/cli) nebo jinak umístěte binární soubor na svůj PATH. Nemůže se napojit na npx.

tgcloud completion <bash|zsh|fish>

Vytiskne skript pro doplňování shellu na stdout. Povolte ho jednou, pak <Tab> doplní příkazy, příznaky, adresáře modulů, typy aktualizací obsluh, které ještě nemáte, a vaše lokální spustitelné moduly.

# bash — vyžaduje balíček bash-completion:
echo 'eval "$(tgcloud completion bash)"' >> ~/.bashrc

# zsh — ujistěte se, že `autoload -U compinit && compinit` běží ve vašem ~/.zshrc:
echo 'eval "$(tgcloud completion zsh)"' >> ~/.zshrc

# fish:
tgcloud completion fish > ~/.config/fish/completions/tgcloud.fish

Po tom restartujte shell (nebo znovu načtěte soubor). Spuštění tgcloud completion bez shellu znovu vytiskne tyto pokyny.

upgrade

npx tgcloud upgrade [--dry-run]

Jednorázová migrace pro projekty vytvořené před CLI 0.2.0, které držely schema.js, lib/ a handlers/ v kořeni projektu. Přesune je do tgcloud/. Vaše spojení s botem zůstane, a nic není třeba znovu nasazovat. --dry-run vytiskne plánované přesuny, aniž by cokoli měnil. Ostatní příkazy neběží na starém rozložení, dokud neprovedete upgrade.

Zůstat v synchronizaci

Každý projekt má v cloudu monotónně rostoucí revizi, která se na každém nasazení zvýší. CLI si pamatuje revizi, s kterou se naposledy synchronizoval, a posílá ji při každém push. Pokud se cloud pohnul dál — protože jiný stroj nebo kolega nasadil — push je odmítnut, místo aby tiše přepsal jejich práci, a CLI nabídne tři cesty vpřed:

npx tgcloud fetch           # zobrazí, co se změnilo v cloudu, pak znovu zkontroluje
npx tgcloud pull            # přenese nejnovější do vašich pracovních souborů
npx tgcloud push --force    # přepíše stav cloudu (nebezpečné)

Tato kontrola optimistické souběžnosti je důvod, proč můžete sdílet bota napříč týmem bez procesu nasazení se synchronizací kroků. Příkazy končí nenulovou hodnotou při chybě — odmítnuté nasazení, selhaná migrace, chyba autentizace, modul, který vyhodil výjimku během run — takže se čistě skládají ve skriptech a CI pipeline.