A Telegram Serverless lehetővé teszi, hogy botja és Mini Alkalmazása (Mini App) háttérkódját közvetlenül a Telegram infrastruktúráján futtassa — nincs szükség szerverek kiépítésére, konténerek életben tartására vagy a skálázás miatti aggodalomra. Egyszerű JavaScript modulokat írhat, amelyeket egyetlen paranccsal telepíthet, a Telegram pedig egy gyors, izolált V8-as homokozóban (sandbox) futtatja őket, közvetlenül a Bot API és a beépített adatbázis mellett.

Ha valaha is konfigurált már botot VPS-en, felhőfüggvényen (cloud function) vagy tárhelykezelő panelen csak azért, hogy válaszoljon egy /start parancsra, ezt a részt már nem kell többé megtennie.

Miért a szerver nélküli (serverless) megoldás?

A Telegram bot lényegében egy olyan program, amely válaszol a frissítésekre. Hagyományosan ezt a programot valahol olyan helyen kellett futtatnia, ami mindig be van kapcsolva, elérhető és biztonságos — és ezt így is kellett tartania. A Telegram Serverless teljesen eltávolítja ezt a réteget:

  • Nincs infrastruktúra. Nem kell gépet bérelni, frissíteni vagy figyelni. A kódja igény szerint fut, és automatikusan skálázódik a bottal.
  • Hitelesítő adatok mellékelve. A Telegram Bot API, az SQLite-alapú adatbázis és a kimenő HTTP minden modul számára azonnal elérhető — nincs mit telepíteni, nincs mit beállítani.
  • Gyors, izolált végrehajtás. Minden hívás egy könnyűsúlyú V8-as izolátumban fut, közel a Telegram saját rendszereihez, így a Bot API-hívások és az adatbázis-lekérdezések gyorsak és megbízhatóak.
  • Valódi fejlesztői munkafolyamat. A projekt egy mappában él a gépén, verziókezelés alatt. Fájlokat szerkeszt, pontosan látja, mi változott, atomi módon telepít, és az adatbázis-sémát áttekintett migrációkkal lépteti előre — pontosan úgy, ahogy más dolgokkal is dolgozik.

A mentális modell: Hogyan működik a projekt

Három helyen dolgozik, amelyek tisztán leképezhetőek egymásra:

Hol Mi található ott
A projektmappa JavaScript modulok — séma, megosztott kód, frissítéskezelők, Mini App végpontok — és ha van, a Mini App kezelőfelülete
A felhő Ezen modulok telepített másolata, a bot adatbázisa és a Mini App fájljai
A tgcloud CLI A híd — megmutatja az eltéréseket és szinkronizálja azokat

Soha nem kell SSH-val csatlakoznia sehová. Helyben szerkeszti a fájlokat, futtatja a npx tgcloud push parancsot, és a platform átveszi az irányítást. A bot forgalmát a telepített modulok kezelik; az adatbázis a hívások között is megőrzi az adatokat.

Egy projektnek mindössze négyféle kódja lehet, mind egy tgcloud/ mappán belül:

tgcloud/
├─ endpoints/  # függvények, amiket a Mini App hív
├─ handlers/   # függvények, amelyek reagálnak a Telegram frissítéstípusaira
├─ lib/        # megosztott kód, amit bárhonnan importálhat
└─ schema.js   # az adatbázis-táblái

Amikor megérkezik egy frissítés — üzenet, gombnyomás, inline lekérdezés —, a Telegram továbbítja azt a megfelelő kezelőnek (handlers/message.js, handlers/callback_query.js, …) és meghívja annak alapértelmezett exportját. Ez a függvény az SDK-n keresztül kommunikál a Bot API-val és az adatbázissal, majd visszatér. Ez a teljes folyamat. A megfelelő kezelő nélküli frissítéseket a rendszer egyszerűen figyelmen kívül hagyja, így csak azokat a kezelőket kell hozzáadnia, amelyekre szüksége van.

A Mini App opcionális. Ha a botjához tartozik ilyen, annak kezelőfelülete ugyanabban a projektben él, a tgcloud/ mellett. A Serverless tárolja a build kimenetét és futtatja az általa hívott endpoints/ mappát. Lásd: Mini App kezelőfelület .

Gyors bemutató

Íme egy teljes, működő demóbot. Minden üzenetre válaszol, és megjegyzi, hány üzenetet látott az adott csevegésből.

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;

  // Beszúrja a számlálót, vagy növeli, ha már létezik ehhez a chathez — és visszaadja
  // az eredményül kapott sort ugyanabban az utasításban a .returning() segítségével.
  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: `Hello! Eddig ${row.seen} üzenetet láttam tőled.`,
  });
}

Telepítés:

npx tgcloud push       # modulok feltöltése
npx tgcloud migrate    # a `counters` tábla létrehozása

Ez egy élő bot, állandó állapottal, szerver nélkül. Minden benne — api, db, a table() DSL — az alábbi részekben van leírva.

A Serverless egy általános háttérrendszer Telegram botok és Mini Appok számára, nem csak egy sablon egyfajta alkalmazáshoz. Ideális:

  • Társalgási AI botokhoz, amelyeknek felhasználónkénti állapotot kell tárolniuk az adatbázisban.
  • Mini App háttérrendszerekhez, amelyek felhasználói adatokat tárolnak és dinamikus tartalmat szolgálnak ki.
  • Játékokhoz és eszközökhöz — beleértve a ranglistákat, kvízeket és egyebeket.
  • Automatizálásokhoz és integrációkhoz, amelyek harmadik féltől származó HTTP API-kat hívnak meg és továbbítják az eredményeket a csevegésekbe.

Első lépések

Ez az útmutató egy üres mappától egy élő botig vezet, amely válaszol az üzenetekre és adatokat tárol. Feltételezi, hogy rendelkezik Node.js 18-as vagy újabb verzióval, és regisztrált botja van a @BotFather segítségével. Mire a végére ér, minden naponta szükséges parancsot használni fog: push, migrate, run, és status.

Mindenekelőtt kapcsolja be a Serverless funkciót. A @BotFather -nél nyissa meg a botját → Serverless és kapcsolja be. Ez aktiválja a funkciót ehhez a bothoz, és feloldja a CLI hozzáférési tokent, a kezelőket, a könyvtárat és az adatbázist.

1. Projekt létrehozása

A leggyorsabb mód a kezdésre a projektkészítő, amely felépíti a projekt szerkezetét és telepíti a CLI-t:

npm create @tgcloud/bot example_bot
cd example_bot

Az argumentum a célmappa: használjon .-ot az aktuális mappába való telepítéshez, vagy bármilyen elérési utat. Meglévő mappában is működik, és soha nem írja felül a már meglévő fájlokat.

Ez egy szerkesztésre kész projektet ad:

example_bot/
├─ tgcloud/             # mindaz, amit a platform futtat
│  ├─ handlers/
│  │  └─ message.js     # egy kezdő üzenetkezelő (visszhangozza a szöveget)
│  ├─ lib/              # ide kerülnek a megosztott modulok (kezdetben üres)
│  └─ schema.js         # az adatbázis-táblái
├─ docs/
│  └─ tgcloud-sdk.md    # SDK referencia (Önnek és az AI eszközeinek)
├─ AGENTS.md            # tájékoztató az AI kódolási asszisztenseknek
├─ package.json
└─ tgcloud.jsonc        # projekt konfiguráció

A létrehozott fájlok önmagukat dokumentálják — mindegyik tartalmaz megjegyzésekkel ellátott példákat a következő lépésekhez.

A CLI helyi fejlesztői függőségként települ a projektbe, így az npx tgcloud <parancs> paranccsal futtathatja (az npx a projekt node_modules mappájában találja meg a másolatot), vagy a package.json-hoz hozzáadott npm run parancsikonokkal (npm run deploy, npm run status). Alapértelmezés szerint nincs globális tgcloud a PATH-on.

Globálisan is telepítheti — npm install -g @tgcloud/cli —, ha inkább egy sima tgcloud parancsot írna bárhonnan. Ez hasznos a tgcloud init futtatásához bármely üres mappában, és ez az, amire a shell tab-kiegészítéséhez szükség van. Akárhogy is dönt, ugyanazt a projektet kapja.

Mini Appot épít? Kezdje a kezelőfelületével. Hozza létre a megszokott eszközével, majd futtassa a projektkészítőt abban a mappában. Lásd: Mini App kezelőfelület .

2. A bot összekapcsolása

Minden projekt egy bothoz van kötve. Kapcsolja össze őket a login paranccsal, amely kéri a CLI hozzáférési tokent ( @BotFather → botja → Serverless → CLI Access → Access token — ez egy külön token a bot API tokenjétől ), és helyben tárolja azt:

npx tgcloud login

A token formátuma app<id>:<secret>. A CLI a .tgcloud/ mappában tárolja, amely git-ignorált, és a titkos részt soha nem írja ki. A bejelentkezés az egyetlen alkalom, amikor ezt kéri — lásd Hitelesítés a tokenek CI-környezetben történő feloldásához.

3. Nézzen körül

Két parancs segít tájékozódni bármikor, mindkettő teljesen offline:

npx tgcloud status     # mi változott helyben vs. a telepített másolat
npx tgcloud diff       # soronkénti változások

Közvetlenül az init után minden új, és még semmi sincs telepítve. A status megmutatja a kezdő fájlokat, amelyek várják a feltöltést.

4. Telepítés

Küldje a moduljait a felhőbe:

npx tgcloud push

A push feltölti az összes megváltozott modult egy atomi kötegben, és frissíti a helyi nyilvántartását arról, mit tartalmaz most a felhő. A botja éles: nyissa meg a Telegramban és küldjön neki egy üzenetet — a kezdő kezelő visszhangozza azt.

A telepítés soha nem érinti az adatbázist. A kód küldése és az adatbázis-séma módosítása szándékosan külön lépések, így a kódtelepítés soha nem okozhat meglepetést adatmigrációval. Erre való a következő lépés.

5. Adatbázis-tábla hozzáadása

Tegyük emlékezővé a botot. Nyissa meg a tgcloud/schema.js fájlt és deklaráljon egy táblát:

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())`) ,
});

Telepítse a sémát, majd alkalmazza az adatbázison:

npx tgcloud push       # feltölti az új sémát
npx tgcloud migrate    # létrehozza a `messages` táblát

A push jelzi, hogy a séma nincs szinkronban, és megmutatja a függő változást, de nem alkalmaz semmit. A migrate végigvezeti Önt a változáson, és megerősítése után létrehozza a táblát. Ez a kétlépcsős modell — és a kockázatosabb változások (mint például törlések) kezelése — a Migrációk részben található.

6. Adatok tárolása és olvasása

Most használja a táblát a kezelőjéből. Szerkessze a tgcloud/handlers/message.js fájlt:

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

export default async function (message) {
  // Az üzenet mentése.
  await db.insert(messages)
    .values({ chatId: message.chat.id, text: message.text })
    .run();

  // Megszámolni, hányat tároltunk ehhez a csevegéshez.
  const count = await db.$count(messages, eq(messages.chatId, message.chat.id));

  await api.sendMessage({
    chat_id: message.chat.id,
    text: `Elmentve. Eddig ${count} üzenet érkezett ebből a csevegésből.`,
  });
}

Telepítse a frissített kezelőt a npx tgcloud push paranccsal, majd küldjön néhány üzenetet a botjának, és figyelje, ahogy a számláló növekszik. Az adatbázis megőrzi az adatokat a hívások között — ez a bot memóriája.

7. Tesztelés telepítés nélkül

Nem kell telepítenie egy változás kipróbálásához. A npx tgcloud run a helyi fájljai használatával futtat egy kezelőt a platformon, közzététel nélkül:

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

Az argumentum az a payload, amelyet a kezelője megkap — a handlers/message esetén egy Message —, JSON5 formátumban (így elhagyhatja a kulcsok idézőjeleit). A parancs kiír mindent, amit a kezelő console.* segítségével naplózott, a visszatérési értéket, és azt, hogy mennyi ideig tartott. Ez a leggyorsabb mód a logika iterálására — nincs telepítés, nincs várakozás valódi üzenetre.

8. Szinkronban maradás

Munka közben néhány parancs segít a helyi projekt és a felhő szinkronban tartásában: a npx tgcloud status mutatja, mi változott, a npx tgcloud push telepít, a npx tgcloud pull a helyi projektet igazítja a felhőhöz, a npx tgcloud fetch ellenőrzi a telepített állapotot anélkül, hogy a fájlokhoz nyúlna, a npx tgcloud reset pedig elveti a helyi változásokat.

Ha két személy (vagy két gép) ugyanahhoz a bothoz telepít, a platform érzékeli a konfliktust, és a push leáll, hogy először pull-ozhasson — soha nem írhat felül véletlenül valaki más munkáját. Lásd: Szinkronban maradás .

Építés AI-jal

Inkább AI asszisztenssel építene — vagy a csapatának az egyetlen kódolója egy AI? Akkor is kiadhat botot. Megtettük az első lépést, hogy egy AI ügynök otthon érezze magát a projektben: minden új projekt AGENTS.md és docs/tgcloud-sdk.md referenciával jön létre, amelyet az ügynök kódoló eszközök automatikusan olvasnak.

Egy kis, önmagában álló futtatókörnyezettel együtt — egy SDK, nincs kezelendő npm csomag —, ez segíti az asszisztenst azokban a konvenciókban, amelyeket az általános kódgenerálás hajlamos elszalasztani: relatív importok .js kiterjesztéssel, nincsenek idegen kulcsok (foreign keys), minden db hívás aszinkron, frissítéstípusonként egy kezelő, és a kétlépcsős push/migrate folyamat.

Próbálja ki:

npm create @tgcloud/bot my-bot
cd my-bot
opencode            # vagy Claude Code, Cursor, … — bármely ügynök, ami olvassa az AGENTS.md fájlt

Ezután csak kérjen, egyszerű nyelven:

Írj egy botot, amely megjegyzi mindenki teendőlistáját — adj hozzá egy elemet, amikor szöveget küldenek, és mutasd az egész listát, amikor /list parancsot küldenek.

Az asszisztens szerkeszti a schema.js fájlt és a kezelőit helyettünk; Ön átnézi, teszteli a változást azonnal a npx tgcloud run paranccsal, majd élesíti a npx tgcloud push és a npx tgcloud migrate parancsokkal. Az AGENTS.md a projekt része — szerkessze, ahogy a bot növekszik, hogy az útmutató pontos maradjon.

Útközben a BotFatherrel

Csak a telefonja van kéznél? A teljes projekt a @BotFather felületen is él — nyissa meg a botját → Serverless és mindent megkap, amit a CLI kezel, érintőképernyőn:

  • Kezelők (Handlers) — frissítéskezelők létrehozása, szerkesztése és tesztfuttatása; a BotFather szinkronban tartja a webhorgot a telepített kezelőkkel (ugyanaz a Szinkronban / Nincs szinkronban állapot, amit a CLI jelent).
  • Végpontok (Endpoints) — a függvények, amiket a Mini Appja hív.
  • Könyvtár (Library) — a megosztott lib/ moduljai.
  • Adatbázis — szerkessze a schema.js fájlt ugyanazzal a Drizzle-szerű szintaxissal, tekintse át a függő változásokat és alkalmazza őket; a Mentés telepít.
  • Mini App fájlok — a kezelőfelület, amelyet a felhasználói megnyitnak a Telegramban.
  • CLI Hozzáférés — szerezze be innen a CLI hozzáférési tokent, ha visszatért a billentyűzethez.

Ez egy és ugyanaz a felhőprojekt, így elkezdhet egy kezelőt a telefonján, és később npx tgcloud pull paranccsal lehúzhatja a laptopjára — semmi sincs egyetlen klienshez kötve. A kezelő futtatása még a Konzol kimenetét is megmutatja közvetlenül a csevegésben, akárcsak a npx tgcloud run esetében.

Projektek és modulok

A szerver nélküli projekt egy szokásos, verziókezelés alatt álló mappa. Minden, amit a platform futtat, egyetlen mappában ül: tgcloud/; a projekt többi része az Öné — egy mini-alkalmazás kezelőfelülete, konfigurációk, dokumentációk. A bothoz nincs build lépés, futásidőben nincsenek node_modules fájlok, és nincs szerver belépési pont.

A projekt anatómiája

example_bot/
├─ tgcloud/             # a telepített rész — semmi más
│  ├─ handlers/         # frissítéskezelők — lapos, csak egy szint
│  │  ├─ message.js
│  │  └─ callback_query.js
│  ├─ endpoints/        # függvények, amiket a Mini App hív — lapos, csak egy szint
│  │  └─ getProfile.js
│  ├─ lib/              # megosztott modulok; alkönyvtárak megengedettek
│  │  ├─ reply.js
│  │  └─ internal/util.js
│  └─ schema.js         # adatbázis séma — egy fájl
└─ .tgcloud/            # CLI állapot, git-ignorált

Csak a tgcloud/ mappán belüli .js fájlok — schema.js, lib/, handlers/, endpoints/ — kerülnek telepítésre. Minden más — Markdown, konfigurációs fájlok, a .tgcloud/ mappa, bármi a tgcloud/ mappán kívül — a gépén marad. A modulnevek a tgcloud/ mappához viszonyítottak. A tgcloud/handlers/message.js fájl a handlers/message modul, és ez az a név, amit az npx tgcloud run elfogad.

Frissítés régebbi projektről? A CLI 0.2.0 előtt létrehozott projektek a schema.js, lib/ és handlers/ fájlokat a projekt gyökerében tartották. Futtassa egyszer az npx tgcloud upgrade parancsot, hogy áthelyezze őket a tgcloud/ mappába.

schema.js — az adatbázisa. A táblákat nevesített exportként deklarálja a séma DSL segítségével, és egyetlen fájlként él a tgcloud/ mappában. Úgy települ, mint bármely más modul, de a telepítése soha nem változtatja meg az adatbázist — a séma változásait külön kell alkalmazni a npx tgcloud migrate paranccsal. Lásd: Az adatbázis .

lib/ — megosztott kód, bármi, amit újra szeretne használni a kezelők között: tiszta segítők, adatbázis-hozzáférési rétegek, formázás, integrációk külső szolgáltatásokkal. A lib/ az egyetlen könyvtár, amely tartalmazhat alkönyvtárakat (lib/internal/util.js, lib/payments/stripe.js), így tetszése szerint rendszerezheti a nagyobb kódbázist. A lib/ moduljait a platform soha nem hívja meg közvetlenül; azért léteznek, hogy a kezelők és egymás importálják őket.

handlers/ — a bot belépési pontjai. Minden fájl egy Telegram frissítéstípusnak felel meg, és a platform minden bejövő frissítést a megfelelő kezelőhöz irányít:

Fájl Kezel
handlers/message.js Új bejövő üzenetek
handlers/inline_query.js Új bejövő inline lekérdezések
handlers/callback_query.js Új bejövő callback lekérdezések
… bármilyen más Bot API frissítéstípus

A handlers/ mappa lapos — nincsenek alkönyvtárak. A kezelő export default függvénye az, amit a platform meghív (lásd: Kezelők ).

Egy frissítéstípust csak akkor kezelünk, ha a kezelőfájlja létezik és nem üres. Ha nincs handlers/<típus>.js — vagy a fájl üres —, akkor az ilyen típusú frissítések figyelmen kívül maradnak, és a platform nem futtat semmit értük. Tehát csak azokat a kezelőket tartsa meg, amelyekre valóban szüksége van: minden további kezelő egy újabb frissítéstípus, amelyre a botja felébred, a többiek kihagyása pedig azt jelenti, hogy a Telegram nem indítja el a kódját olyan frissítésekhez, amelyeket egyébként is eldobna.

Új kezelő létrehozásához futtassa: npx tgcloud add handlers/<típus> .

endpoints/ — a Mini App szerveroldala. Minden fájl egy függvény, amelyet a Mini App név szerint hív meg, így az endpoints/getProfile.js válaszol a POST /api/getProfile hívásra. A platform ellenőrzi, hogy a hívás egy Telegram-felhasználótól érkezik-e a Mini Appon belül, mielőtt a kódja futna, és átadja Önnek az adott felhasználót. Az endpoints/ mappa lapos, akárcsak a handlers/. Lásd: Végpontok .

.tgcloud/ — a CLI által teljes egészében kezelt gépi állapot, beleértve a mentett hitelesítő adatokat. Git-ignorált, soha ne olvassa vagy írja kézzel — használja helyette a CLI parancsokat.

A modulrendszer

Futásidőben egy modul pontosan két dolgot lát: a platform SDK-t és a projekt többi modulját. Nincsenek npm csomagok, nincs fájlrendszer, és nincs hálózat az SDK fetch parancsán kívül.

Saját moduljait úgy importálja, ahogy bármely JavaScript projektben tenné — relatív elérési úttal, .js kiterjesztéssel — az SDK-t pedig a nevével:

// tgcloud/handlers/message.js
import { users } from '../schema.js';              //  a séma modul
import { addItem } from '../lib/cart.js';          //  egy lib modul
import { format } from '../lib/internal/fmt.js';  //  beágyazott lib modul
import { db, api, fetch } from 'sdk';              //  a platform SDK
// tgcloud/lib/cart.js
import { format } from './internal/fmt.js';        //  egy szomszéd
import { users } from '../schema.js';

A relatív importnak .js kiterjesztésre kell végződnie, és nem vezethet a tgcloud/ mappán kívülre:

import { users } from '../schema';       //  adja hozzá a .js-t
import config from '../../config.js';    //  a tgcloud/ mappán kívül
import { z } from 'zod';                 //  nincsenek npm csomagok

Minden modulnak van neve is — ez az elérési útja a tgcloud/ mappán belül, kiterjesztés nélkül, például schema, lib/cart vagy handlers/message. Ezt a nevet használja az npx tgcloud run és a CLI kimenete, és egy modul név szerint is importálható (from 'lib/cart', kiterjesztés nélkül). A kódban részesítse előnyben a relatív importokat. Ezeket érti a szerkesztője — ugrás a definícióra, automatikus kiegészítés, átnevezés.

Futásidőben pontosan két dolog áll rendelkezésre: sdk és annak almoduljai (sdk/db, sdk/api, sdk/fetch) — a teljes platformfelület, lásd Az SDK — és saját moduljai a tgcloud/ mappában. Ez a teljes lista. Ha a kódja bármi mást import-ál, az nem fog feloldódni. Ez a megkötés teszi a modulokat gyorsan betölthetővé és biztonságosan futtathatóvá.

Kezelők (Handlers)

A kezelő egy handlers/ mappában lévő modul, amelynek alapértelmezett exportját a platform meghívja, amikor egy megfelelő frissítés érkezik.

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

export default async function (message) {
  await api.sendMessage({
    chat_id: message.chat.id,
    text: `Azt mondtad: ${message.text ?? '(nincs szöveg)'}`,
  });
}

A kezelő az argumentumaként kapja meg a frissítés payloadját — a platform kicsomagolja Ön helyett a Telegram Update objektumot. A handlers/message.js megkapja a Message objektumot (azaz update.message-t); a handlers/callback_query.js megkapja a CallbackQuery objektumot; és így tovább. A kezelő második argumentuma egy hívásonkénti kontextus objektum, ctx. Tartalmazza a nyers Update objektumot ctx.update néven — használja, ha a payloadon kívül másra is szüksége van, például az update_id-ra.

A kezelő lehet async (általában az is) és visszatérhet egy értékkel. Az SDK-n keresztül éri el a Bot API-t, az adatbázist és a kimenő HTTP-t.

Nincs szükség valódi frissítésre a kezelő teszteléséhez. Az npx tgcloud run végrehajtja azt a platformon az Ön által megadott payloaddal és a jelenlegi helyi kódjával:

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

Az argumentum a payload — ugyanaz az objektum, amit a kezelője megkap — JSON5 formátumban. A kezelő ctx argumentumának (második argumentum) megadásához használja a --ctx kapcsolót, pl. --ctx '{ update: { update_id: 1 } }'. Ez a helyi fájljain fut, így a módosításokat tesztelheti a telepítésük előtt. Lásd run .

Végpontok (Endpoints)

A végpont egy endpoints/ mappában lévő modul, amelyet a Mini Appja hív — ide kerül minden olyan feladat, amit a kezelőfelület önmagában nem tud megtenni, mint például az adatbázis olvasása és írása, a Bot API hívása, vagy külső szolgáltatásokkal való kommunikáció a titkaival.

// 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 nem található', { userId: user.id });
  return profile;
}

Az első argumentum, az input, az a JSON objektum, amit a Mini App küldött. A második, ctx, tartalmazza a ctx.initData objektumot — a Mini App init adatait (user, chat, start_param, auth_date, …), amelyeket a platform már ellenőrzött. Ugyanolyan formájú, mint a kliensen a Telegram.WebApp.initDataUnsafe, de itt megbízhat benne. A érvényes init adatok nélküli hívást a platform 401 hibával utasítja vissza, mielőtt a kódja futna, így a ctx.initData.user valóban az a személy, aki megnyitotta a Mini Appot. A végpontok csak így hívhatók meg — a botja Mini Appjából, egy Telegram-felhasználó által —, tehát nincs védelemre szoruló nyilvános API és nincs kiépítendő bejelentkezés.

Bármit is ad vissza a végpont, az JSON formátumban küldődik vissza. Hívás visszautasításához használja a throw new EndpointError(leírás, paraméterek?) parancsot. A Mini App megkapja a leírást és a paraméterek objektumot úgy, ahogy vannak. Ha a kezelőfelületen a hiba típusa szerint szeretne elágazni, helyezzen el egy kódot a paraméterek között — { code: 'OUT_OF_STOCK' }. Bármely más kivétel általános belső hibaként kerül jelentésre, és a valódi üzenet csak a naplókba kerül, így a belső részletek soha nem szivárognak ki a klienshez.

Végpont hívása a Mini Appból

A Mini App a Telegram.WebApp.Serverless.call paranccsal hívja meg a végpontot név szerint a hivatalos Mini Apps SDK — telegram-web-app.js — segítségével, amelyet a Mini Appja már tartalmaz. Ez elküldi az init adatokat Ön helyett, és visszahívja Önt egy hibával vagy a végpont eredményével:

Telegram.WebApp.Serverless.call('getProfile', { lang: 'en' }, function (err, profile) {
  if (err) {
    // err.message — a leírás; err.parameters — amit az EndpointError vitt
    if (err.type === 'ENDPOINT_ERROR') showMessage(err.message);
    return;
  }
  render(profile);
});

A bemenet opcionális és sima objektumnak kell lennie. Enélkül a hívás Serverless.call('getProfile', callback).

Az err.type akkor 'ENDPOINT_ERROR', ha a végpontja EndpointError kivételt dobott. Bármely más érték azt jelenti, hogy a platform elutasította a hívást vagy a végpont váratlanul meghiúsult, például 'UNAUTHORIZED', ha az init adatok hiányoznak vagy érvénytelenek.

A végpontnevek olyanok, mint a JavaScript azonosítók — betűk, számok és _, nem kezdődhetnek számmal (getProfile, place_order). Hozzon létre egyet az npx tgcloud add endpoints/<név> paranccsal, és tesztelje Mini App nélkül a run paranccsal, az init adatokat saját maga megadva:

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

Mi települ

Amikor npx tgcloud push parancsot futtat, a CLI összegyűjt minden .js fájlt a schema.js, lib/, handlers/ és endpoints/ mappák alatt, és ezt a pontos készletet küldi el a projekt modultereként — együtt a Mini App build mappájával, ha a tgcloud.jsonc megnevez egyet (lásd alább). Minden, ami a felhőben jelen van, de hiányzik a projektjéből, törlődik, így a telepített állapot mindig tükrözi a mappáját — a törléseket is beleértve. Semmi a tgcloud/ mappán kívül nem települ kódként — a projekt gyökerének többi része az Öné. A tgcloud/ mappán belül egy .js fájl, amely nem ezeken a helyeken található (tgcloud/util.js, tgcloud/helpers/x.js), megjelölésre kerül, így nem marad telepítetlenül észrevétlenül. A Markdown fájlok, dotfájlok és a .tgcloud/ soha nem települnek.

Mini App kezelőfelület

A projekt gyökere az Öné, és ez a természetes helye a Mini App kezelőfelületének — amelyet React, Vue, Svelte vagy egyszerű HTML segítségével, Vite-tal vagy bármely más csomagolóval építhet. A Serverless a botja mellett tárolja a build kimenetét. Egy push telepíti a kódot és a statikus fájlokat együtt, és az alkalmazás a https://app<app_id>.tgcloud.ai/ címen érhető el (push kiírja a pontos címet — használja ezt a botja URL-jeként a @BotFather felületen).

Visszaélés jelentése. A tgcloud.ai oldalon lévő Mini Appokat független fejlesztők teszik közzé. Az illegális tartalom vagy visszaélés jelentéséhez egy app<app_id>.tgcloud.ai aldomainen írjon az [email protected] címre.

A kezdéshez hozza létre először a kezelőfelületet, bármilyen eszközzel, amit szeret, majd adja hozzá a Serverless funkciót:

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

A projektkészítő soha nem írja felül a fájljait. Hozzáadja a tgcloud/ mappát a kezelőfelület mellé, felismeri a build mappát, és hozzáad egy npm run deploy szkriptet, amely építi az alkalmazást és telepíti azt a bottal együtt.

A hosztolás módját a tgcloud.jsonc fájlban állíthatja be, amely a projekt gyökerében található:

{
  "$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 }]
  }
}

Csak a "source" kötelező. Ez a build mappa, a projekt gyökeréhez viszonyítva. A "static" többi kulcsa konfigurálja a kiszolgálást (lásd lentebb). Az init létrehozza a tgcloud.jsonc fájlt Önnek. Amikor felismeri a kezelőfelületet, kitölti a "source" mezőt is:

Kezelőfelület init beállítja a "source" értéket erre:
Vite — React, Vue, Svelte, Solid, … dist
Next.js, Nuxt, SvelteKit, React Router, TanStack Start, Angular semmi — konfiguráljon először statikus exportot, majd állítsa be a "source" értéket maga

Bármely más beállításnál állítsa be a "source" értéket a build mappájára maga.

A $schema sor automatikus kiegészítést és érvényesítést ad a szerkesztőjében minden kulcshoz. A fájl JSON megjegyzésekkel és záró vesszőkkel megengedett. Ha egy másik eszköze sima JSON-ként olvassa, nevezze át tgcloud.json fájlra — akkor viszont szigorú JSON-nak kell lennie.

Építsen (build) telepítés előtt. A push úgy tölti fel a mappát, ahogy van a lemezen — kivéve a ponttal kezdődő nevű fájlokat és mappákat, amelyeket a rendszer kihagy —, és soha nem futtatja az Ön buildjét, így egy egyszerű npx tgcloud push a kezelőfelület szerkesztése után a korábbi buildet telepítené. Használja az npm run deploy parancsot (ez futtatja a build szkriptjét, majd a tgcloud push parancsot), vagy építsen először saját maga.

A kiszolgálási lehetőségek:

  • spa — az index.html kiszolgálása olyan útvonalakhoz, amelyek nem léteznek és nincs fájlkiterjesztésük, így az ügyféloldali router újratöltéskor is működik. A kiterjesztéssel rendelkező útvonalak továbbra is 404 hibát kapnak.
  • immutable — minták a tartalom-hashinggel ellátott fájlokhoz (Vite /assets/* mappája), amelyeket a böngészők egy évig gyorsítótárazhatnak. Csak tartalom-hashinggel ellátott fájlokhoz használja. Bármely más fájl itt egy évig elavult maradna a böngészőkben, és nincs mód annak visszahívására.
  • headers — válaszfejlécek azokhoz az útvonalakhoz, amelyekre egy szabály illeszkedik (pontos útvonal, vagy egy /* karakterre végződő előtag). A null érték eltávolít egy fejlécet.
  • redirects — átirányítási szabályok, ahol az első találat nyer. Egy létező fájl mindig felülír egy szabályt. A status alapértelmezett értéke 301, hacsak nem állítja be másképp.
  • "static": "dist" — rövidítés a { "source": "dist" } helyett, amikor másra nincs szüksége.
  • "static": false — a telepített webhely eltávolítása a következő push alkalmával. A kulcs törlése egyszerűen csak megállítja a projektet a statikus fájlok kezelésében, és ami telepítve van, az marad.

A webhely a saját gazdagépének gyökeréből szolgál ki, így a csomagoló alapértelmezett base (/) beállítása helyes. A Telegram Web iframe-ben nyitja meg a Mini Appokat, ezért ne adjon hozzá X-Frame-Options vagy szigorú frame-ancestors szabályt.

Ugyanez a gazdagép válaszol a végpontjaira a /api/ alatt — ide megy a Telegram.WebApp.Serverless.call('getProfile', …). Ne helyezze saját fájljait a /api/ alá a build mappában.

Az adatbázis

Minden bot megkapja a saját adatbázisát — egy SQLite-alapú tárolót, amely megőrzi az állapotot a hívások között, és minden modul számára elérhető a db objektummal. A tábláit egy kis, típusos DSL segítségével írja le a schema.js fájlban; egy gördülékeny lekérdezés-építővel olvassa és írja őket; és áttekintett migrációkkal fejleszti azokat.

Ismeri a Drizzle ORM rendszert? Akkor már tudja, hogyan kommunikáljon itt az adatbázissal. A séma DSL és a lekérdezés-építő a Drizzle-t követi — az oszlop-építők, a select().from().where(), az operátorok, az onConflictDoUpdate, a .returning() és a sql címke mind úgy viselkedik, ahogy elvárná, így az adatok írása és olvasása az ismerős API, amit már használ. Csak importálnia kell az sdk/db csomagból, és néhány platformspecifikus részlet (főleg a nincsenek idegen kulcsok rész) ott van megemlítve, ahol felmerülnek.

Táblák deklarálása

A táblák a schema.js fájlban nevesített exportok. A table() meghívása betöltéskor épít egy leírást (nem érinti az adatbázist); a platform felfedezi az exportált táblákat, amikor telepíti a schema.js fájlt, és migrálni fogja az adatbázist, hogy az megfeleljen az leírásnak.

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év, oszlopok, extrák?):

  • név az SQL tábla neve;
  • oszlopok egy JS tulajdonság → oszlopdefiníció leképezés;
  • extrák egy opcionális visszahívási függvény (t) => ({ … }), ahol a t elérhetővé teszi az oszlopokat (t.created az adott oszlopra való hivatkozás) — deklarálja itt az indexeket és a tábla szintű megkötéseket.

Oszloptípusok

Gyár SQLite típus Megjegyzések
text() TEXT
integer() INTEGER
real() REAL alias float()
numeric() NUMERIC
blob() BLOB olvassa/írja Uint8Array
boolean() INTEGER 0/1-ként tárolva, true/false-ként olvasva
json() TEXT auto JSON.stringify / JSON.parse

Az oszlopnév argumentum opcionális — hagyja el, és a JS kulcsot használja a rendszer. A mode opció szabályozza, hogyan konvertálódnak az értékek az SQLite és a JavaScript között.

mode Tárolva mint JS érték
boolean INTEGER 0/1 boolean
json TEXT (JSON) bármilyen objektum/tömb
timestamp INTEGER (unix másodperc) Date
timestamp_ms INTEGER (unix ms) Date
bytes BLOB Uint8Array

A boolean() és a json() a integer(name, { mode: 'boolean' }) és a text(name, { mode: 'json' }) rövidítései.

A blob() egy Uint8Array-t olvas és ír — a futtatókörnyezetnek nincs Node Buffer objektuma (és a Buffer egy Uint8Array alosztály, tehát ez a hordozható alaptípus). A fenti mode csak azt szabályozza, hogyan van kódolva az érték, függetlenül az oszlop tárolási típusától: így a blob('col', { mode: 'json' }) a BLOB megfelelője a json() függvénynek — ugyanaz a JSON kódolás, de BLOB oszlopban tárolva, nem TEXT-ben.

Összefoglalva: A blob() Uint8Array-t használ. A mode a kódolást vezérli, nem a tárolást, így a blob(..., { mode: 'json' }) JSON-t tárol BLOB-ként, míg a json() TEXT-ként.

Oszlopmódosítók

Az oszlopmódosítók az oszlop után láncolhatók.

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' })  // vagy 'virtual'
text('email').deprecated('lecserélve loginra')   // megjelöli az oszlopot eltávolításra

A .default() egy json() oszlopon kódolja Ön helyett az értéket; egy sql`…` default érték változatlanul átadódik. A .deprecated() terminális — lásd Migrációk .

Indexek és megkötések

Az indexek és megkötések az extrák visszahívási függvényben deklarálhatók, ahol az oszlopok elérhetőek.

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})`) ,          // kifejezésindex
  active: index('idx_active').on(t.userId).where(sql`done = 0`) ,  // részleges index
}));

Tábla szintű módosítók láncolása a table(...) után: .strict(), .withoutRowid(), .deprecated('ok').

Nincsenek idegen kulcsok (foreign keys)

A futtatókörnyezet PRAGMA foreign_keys kikapcsolt állapotban fut. Egy deklarált idegen kulcs csendben hatástalan lenne — nincsenek kaszkádok, nincs árva védelem —, ami rosszabb, mint ha egyáltalán nem lenne, ezért a DSL lehetetlenné teszi ezt. Nevezetesen, a .references() és a tábla szintű foreignKey() hibaüzenetet dob deklaráláskor, és az a séma, amely ezeket használja, nem fog települni.

A kapcsolatokat sima oszlopokkal (userId: integer('user_id')) kell modelleznie, és az integritást az alkalmazáskódjában kell kényszerítenie: szúrja be a szülőket a gyermekek előtt, törölje a gyermekeket a szülők előtt, kezelje megfelelően a hibákat, és tisztítsa meg az árvákat egy LEFT JOIN … WHERE parent.id IS NULL lekérdezéssel, amikor szükséges.

Az idegen kulcsok hiánya egy szándékos korlátozás, nem figyelmetlenség. Tervezze be ezt korán, amikor a botját tervezi.

Lekérdezés

A db egy gördülékeny lekérdezés-építő. Minden lekérdezés aszinkron — a terminális metódusok (.all(), .get(), .values(), .run()) Promise-okat adnak vissza, ezért mindig await használatával dolgozzon.

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();                          // minden sor
await db.select().from(todos).where(eq(todos.id, 1)).get();   // első sor vagy null
await db.select().from(todos).values();                       // sorok mint értéktömbök

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();

// egyedi projekció: { alias: oszlopHivatkozás | sqlKifejezés | aggregátum }
await db.select({ id: todos.id, title: todos.text, n: count() })
  .from(todos).groupBy(todos.userId).having(sql`count(*) > ${1}`).all();

// sorok száma — egy segítő, nem egy lekérdezés-építő terminál:
await db.$count(todos);                        // minden sor
await db.$count(todos, eq(todos.done, false)); // szűrővel
  • Láncolható: .where(), .orderBy(), .limit(), .offset(), .groupBy(), .having(), .distinct().
  • Terminálok: .all(), .get(), .values().
  • Sorok számlálása a db.$count(table, where?) (vagy count() egy projekcióban) segítségével.

Beszúrás, frissítés, törlés:

await db.insert(todos).values({ userId: 1, text: 'Vegyél tejet' }).run();
await db.insert(todos).values([{ text: 'A' }, { text: 'B' }]).run();   // kötegelt
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();

Vegye figyelembe, hogy a kötegelt beszúrás egy utasítás, ezért az SQLite változóhatára korlátozza (sorok × oszlopok); ha ezt túllépi, a beszúrás hibát jelez — darabolja fel Ön.

Az operátorok az sdk/db csomagból importálhatók:

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';

A .where(a, b) több argumentummal ugyanaz, mint az and(a, b). A összehasonlítás második argumentuma alapértelmezés szerint egy érték, de lehet egy másik oszlop vagy sql`…` kifejezés — pl. eq(a.x, b.y). Az aggregátumok (count/sum/avg/min/max) sql töredékek egy .select({ … }) projekcióhoz.

Nyers SQL — amikor az építő nem elég, váltson nyers SQL-re. A mód a metódustól függ: db.run írásokhoz, db.all sok sorhoz, db.get egy sorhoz, db.values sorokhoz pozicionális tömbökként.

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, 'vegyél tejet']]

Az írás egy futtatási eredmény-re oldódik fel — rowsAffected, lastInsertRowid, és rows, amelyek egy RETURNING záradékot is tartalmaznak, ha az SQL-nek volt ilyenje. Használja a db.values metódust, amikor egy lekérdezés két azonos nevű oszlopot vetít ki (egy join, mint a SELECT u.id, o.id): a db.all oszlopnév szerint kulcsolja a sorokat, így a második id felülírná az elsőt.

A sql`…` címke a ${value}-t kötött paraméterré, a ${table.column}-t azonosítóvá alakítja, és összefűzi a beágyazott sql töredékeket. Használja a sql.raw('…') függvényt literálhoz paraméterek nélkül.

A nyers lekérdezések nincsenek egy táblához kötve, így a soraik módkonverzió nélkül érkeznek vissza — a boolean 0/1 lesz, a JSON oszlop egy string, az időbélyeg egy szám. Csak a táblához kötött építő konvertálja az értékeket.

Migrációk

Az adatbázisa változik, ahogy a botja növekszik. A platform ezt biztonságosan kezeli azzal, hogy különválasztja a kód telepítését az adatok módosításától, és azzal, hogy minden séma-változást kockázat alapján osztályoz.

A telepítés soha nem érinti az adatbázist. Amikor npx tgcloud push paranccsal telepít egy megváltozott schema.js fájlt, a platform rögzíti az új sémát és jelzi, mit változtatna az adatbázis — de semmit nem alkalmaz:

npx tgcloud push       # telepíti a schema.js-t; jelzi a függő DB változásokat
npx tgcloud migrate    # áttekintés és alkalmazás

A npx tgcloud migrate kiszámítja a különbséget a sémája és az élő adatbázis között, és végigvezeti Önt rajta. Mindig megkérdezi, mielőtt bármi alkalmazásra kerülne. Ez azt jelenti, hogy egy rutin kódtelepítés soha nem indíthat el véletlenül adatmigrációt.

Minden függő változásnak van egy állapota, amely meghatározza, hogyan kezeli a migrate:

Állapot Jelentés A migrate során
safe Additív és nem blokkoló — új tábla, oszlop vagy index Egy lépésben alkalmazva, megerősítés után
warning Potenciálisan romboló vagy lassú — valami törlése, vagy index óriási táblán Egyenként bemutatva, mindegyik külön megerősítve
manual Nem végezhető el automatikusan — pl. oszlop típusának módosítása Útmutatóval bemutatva; kézzel hajtja végre
undocumented Létezik az adatbázisban, de nincs a sémájában Figyelmeztetésként bemutatva; nem alkalmazva

A biztonságos változások gyorsak és elvileg visszafordíthatók, ezért együtt mennek végbe. Minden figyelmeztetés egy szándékos, egyedi megerősítést igényel — nincs “mindent alkalmaz” opció a romboló változásokhoz.

A kézi változások okkal és javasolt lépéssel érkeznek. A migrate egy összefoglalóval zárul: hány változást alkalmaztunk, hagytunk ki, vár kézi javításra, vagy nem szerepel a sémájában.

Lásd a migrate részt a kapcsolókért (--dry-run, --safe, --yes, --local).

Dolgok eltávolítása

Egy tábla vagy oszlop törlése a schema.js fájlból nem törli azokat — ez egy véletlen törlést katasztrofálissá tenne. Eltávolításhoz jelölje meg az objektumot elavultként:

// oszlop törlése
text('email').deprecated('lecserélve loginra')

// teljes tábla törlése
export const oldSessions = table('old_sessions', { /* … */ }).deprecated('nem használt');

A következő migrate során az elavult objektum egy figyelmeztetés állapotú törlésként jelenik meg, amelyet külön kell megerősítenie. Miután eltűnt, távolítsa el a deklarációt.

Oszlop típusának módosítása

A típusváltoztatások kézi jellegűek — az SQLite nem mindig tudja elvégezni őket helyben, és a meglévő értékek kényszerített konvertálása döntést igényel. A migrate megmutatja a változást és annak okát; végezze el saját maga nyers SQL-lel (db.run(...)), jellemzően új oszlop vagy tábla létrehozásával, az adatok másolásával és a cserével.

Az SDK

Futásidőben egy modulnak egy könyvtára van: sdk. Ez három olyan dolgot tartalmaz, amire egy bot háttérrendszerének szüksége van — adatbázis, Telegram Bot API, és kimenő HTTP —, telepítendő dolgok és konfigurálandó hitelesítő adatok nélkül. Az adatbázis (db) a Az adatbázis részben van leírva; ez a szakasz az api-t, a fetch-et és a globális console objektumot tárgyalja.

import { db, api, fetch, InputFile, BotApiError, EndpointError } from 'sdk';   // a teljes felület
// vagy almodulokból:
import { table, integer, text, eq, sql } from 'sdk/db';
import { api } from 'sdk/api';
import { fetch } from 'sdk/fetch';
Import Mi ez
db Adatbázis — lekérdezés-építő és séma DSL → Az adatbázis
api Telegram Bot API — api.sendMessage(...) → lentebb
fetch Kimenő HTTP → lentebb
InputFile Egy feltöltendő fájl (bájtok + név) — működik api és fetch esetén is → Fájlok
EndpointError Mini App hívásának visszautasítása üzenettel, amit megmutathat → Végpontok

Saját projektmoduljait relatív elérési úttal és .js kiterjesztéssel importálja (from '../schema.js', from '../lib/cart.js'). Lásd A modulrendszer .

A Bot API

Az api megadja Önnek a teljes Telegram Bot API felületet. Hívjon meg bármely metódust api.<metódus>(params) formában. Minden jelenlegi — és jövőbeli — Bot API metódus működik SDK frissítés nélkül.

import { api } from 'sdk';

const me = await api.getMe();                            // → a kicsomagolt eredmény
await api.sendMessage({ chat_id: id, text: 'Hello!' });
await api.editMessageText({ chat_id, message_id, text: 'Frissítve' });
await api.answerCallbackQuery({ callback_query_id, text: 'Kész' });

A válasz borítéka kicsomagolt. A Bot API általában { ok: true, result: … } formában csomagolja az eredményeket. Az api közvetlenül a result értékét adja vissza — a getMe() a felhasználói objektumra oldódik fel, nem egy burkolóra. A paraméterek a Bot API saját snake_case neveit használják (chat_id, message_id, reply_markup, …).

A hibák BotApiError kivételt dobnak. Amikor a Bot API { ok: false } választ küld, a hívás BotApiError kivételt dob az hamis érték visszaadása helyett, így nem hagyhatja ki véletlenül. A hiba tartalmazza a .code (Bot API error_code), .description (emberi olvasható üzenet), .method (melyik metódus hiúsult meg) és .parameters (extra adatok, mint például retry_after egy 429 esetén, vagy migrate_to_chat_id) mezőket. Kapja el a kivételt egy várt hiba kezeléséhez, és dobja tovább a többit:

import { api, BotApiError } from 'sdk';

try {
  await api.deleteMessage({ chat_id, message_id });
} catch (e) {
  if (e instanceof BotApiError && e.code === 400) {
    // 400 = az üzenet már eltűnt; ez itt rendben van.
  } else {
    throw e;
  }
}

Fájlok

Dolgozzon fájl bájtokkal, ne csak file_id-kkal — töltsön fel új fájlokat és töltsön le meglévőket.

Feltöltés — InputFile. Csomagolja a nyers bájtokat egy InputFile objektumba (egy sima tároló: bájtok, egy fájlnév, és egy opcionális MIME típus) és adja át bárhol, ahol a Bot API metódus fájlt vár:

import { api, InputFile } from 'sdk';

const doc = new InputFile(bájtok, 'riport.pdf', { type: 'application/pdf' });
await api.sendDocument({ chat_id, document: doc });

// fájlok paraméterekbe ágyazva is működnek — pl. egy 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' }) },
]});

A bájtok valós multipart feltöltésként kerülnek streamelésre a Telegramhoz — a fájl nem kerül egyetlen nagy bufferbe a platform hívása előtt —, de a Telegram Bot API korlátai továbbra is érvényesek: egy bot jelenleg fájlonként akár 50 MB-ot is feltölthet (és 10 MB-ot fotó esetén).

Letöltés — file_id alapján. Két segítő húzza le a fájl bájtait. A platform feloldja a file_id-t Ön helyett (futtatja a getFile parancsot és a háttérben lehívja a tartalmat), így soha nem nyúl a bot tokenhez vagy letöltési URL-hez:

const bájtok = await api.getFileContent(file_id);          // → Uint8Array (az egész fájl)

const { file, body } = await api.getFileStream(file_id);  // streamelje helyette
file.file_unique_id;                                      // a getFile információ
for await (const chunk of body) { /* Uint8Array */ }      // vagy bufferelje: await stream.bytes()

A Telegram Bot API a getFile letöltéseket 20 MB-ra korlátozza, szóval ez a mennyezet mindkét segítő számára.

HTTP

A fetch egy fetch-szerű kliens a külvilág hívására — harmadik fél API-k, webhorgok, bármi HTTP-n keresztül.

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();

A válasz tükrözi a webes platformot: res.status, res.statusText, res.ok (true 200–299 esetén), res.url, res.headers (.get(), .has(), .keys(), .entries()), és a törzsolvasók await res.json() / await res.text().

A törzset inkrementálisan is olvashatja streamként — for await (const chunk of res.body) { … } — így fogyaszthat szerver által küldött eseményeket vagy tokenenkénti kimenetet AI API-kból.

A törzssegítők beállítják a megfelelő Content-Type értéket Ön helyett:

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('hi') });     // text/plain

A kéréstörzsek fájlokat és nyers bájtokat is tartalmazhatnak: adjon át Uint8Array, InputFile vagy FormData (multipart/form-data esetén) objektumot body-ként, és a bájtok kifelé streamelődnek — így harmadik fél API-jára is feltölthet fájlokat ugyanúgy, ahogy Telegramhoz:

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

const form = new FormData();
form.append('file', new InputFile(bájtok, 'fotó.jpg', { type: 'image/jpeg' }));
form.append('caption', 'a botomtól');
const res = await fetch('https://api.example.com/upload', { method: 'POST', body: form });

Egyébként úgy viselkedik, mint a szabványos fetch, amit már ismer, két korlátozással:

  • A választ szövegként olvassa be — await res.json() / await res.text(), vagy streamelve darabonként a res.body használatával.
  • A teljes válasz mérete 30 MB-ban korlátozott. Ez a limit a teljes válaszra vonatkozik — a res.body streamelése lehetővé teszi nagy válaszok inkrementális feldolgozását, de nem emeli meg a limitet.

Naplózás (Logging)

A szabványos globális console elérhető — semmit nem kell importálni, csak ott van, mint bármely JavaScriptben. A kimenetét az npx tgcloud run rögzíti és mutatja, ami az elsődleges hibakeresési eszközévé teszi a fejlesztés során.

console.log('feldolgozás', { chatId: id });   // log / debug — sima
console.info('elindítva');                     // info  — kék
console.warn('sebességkorlát elérése');        // warn  — sárga
console.error(err);                          // error — piros, stack trace-szel

Minden sort megjelöl az a [fájl:sor], ahonnan származik. A console.error és console.trace teljes stack trace-t fűz hozzá, míg a console.warn nem. Amikor npx tgcloud run paranccsal futtat egy modult, ezek a sorok színes előtaggal, a futás kezdete óta eltelt idővel és a forrással jelennek meg — lásd run .

Parancssori felület (CLI)

A tgcloud a híd a projektmappája és a felhő között. Felépíti a projekt szerkezetét, megmutatja, mi változott, telepít, modulokat futtat, és alkalmazza az adatbázis-migrációkat. Node.js 18 vagy újabb verzió szükséges hozzá. Két módja van a beszerzésének:

# Ajánlott — projekt létrehozása azzal a CLI-vel, amit telepítettünk bele:
npm create @tgcloud/bot example_bot

# Vagy telepítse a CLI-t globálisan és inicializáljon egy üres mappát:
npm install -g @tgcloud/cli
tgcloud init

Az npm csomag neve @tgcloud/cli, a parancs pedig tgcloud. Minden parancs működik a projekt bármely alkönyvtárából.

Parancs Cél
init Új projekt felépítése az aktuális mappában
add Új modul felépítése (kezelő, végpont vagy lib modul)
login Projekt összekapcsolása bottal (elmenti a tokent)
status Helyi változások vs. felhő megjelenítése
diff Soronkénti változások megjelenítése
push Megváltozott modulok és a Mini App build feltöltése a felhőbe
migrate Séma-változások alkalmazása az adatbázisra
run Modul futtatása a platformon telepítés nélkül
fetch Telepített állapot ellenőrzése fájlmódosítás nélkül
pull Helyi fájlok szinkronizálása a felhővel
reset Helyi változások elvetése; visszaállítás felhőállapotból
webhook Platform által kezelt webhorog vizsgálata és újra-szinkronizálása
completion Shell kiegészítő szkript nyomtatása (bash/zsh/fish)
upgrade 0.2.0 előtti projektelrendezés áthelyezése a tgcloud/ mappába

Hitelesítés (Authentication)

A projekt egy bothoz van kötve a tokenje által, amely app<id>:<secret> formátumú. Az app<id> rész nyilvános és kinyomtatható; a titkos rész soha nem jelenik meg naplókban vagy hibákban.

A token feloldása ebben a sorrendben történik:

  1. TGCLOUD_TOKEN környezeti változó — CI-hez; soha nem íródik lemezre.
  2. .tgcloud/credentials — amelyet az npx tgcloud login ír.
  3. Egyik sem → hibaüzenet, amely az npx tgcloud login felé irányít.

A CLI soha nem kér tokent parancs közben. A bejelentkezés mindig az explicit login lépés. Ha egy mentett token érvénytelenné válik, a CLI eltávolítja és kéri, hogy jelentkezzen be újra.

init

npx tgcloud init

Felépíti egy új projekt szerkezetét az aktuális könyvtárban: a tgcloud/ mappát schema.js, lib/, handlers/ fájlokkal és egy kezdő kezelővel, plusz tgcloud.jsonc, AGENTS.md, docs/ és a .tgcloud/ állapotmappával. Futtassa egy meglévő kezelőfelület-projekt gyökerében is — hozzáadja a tgcloud/ mappát és a dokumentációt, és soha nem írja felül a fájljait. Ha felismeri a kezelőfelületet, a tgcloud.jsonc fájlt is a build mappára állítja, és hozzáad egy deploy szkriptet (build, majd push) a package.json fájlhoz — csak ha még nincs deploy szkriptje. Lásd: Mini App kezelőfelület .

Az init megtagadja a projekt létrehozását másik projekten belül. Egy projekt gyökerében újra futtatni rendben van, csak hozzáadja, ami hiányzik.

add

npx tgcloud add <cél>

Felépíti egyetlen új modul szerkezetét, bekötve és készen a szerkesztésre — egy kezelő, egy végpont vagy egy lib/ modul.

npx tgcloud add handlers/callback_query   # új frissítéskezelő
npx tgcloud add endpoints/getProfile      # új végpont a Mini Apphoz
npx tgcloud add lib/cart                  # új megosztott modul

Vegye figyelembe, hogy az add soha nem ír felül létező fájlt.

A <cél> a modul elérési útja (a záró .js opcionális). A handlers/ esetén a névnek Telegram frissítéstípusnak kell lennie. A platform közzéteszi az érvényes készletet, így az érvénytelen nevet előre elutasítja. A handlers/ és endpoints/ mappák laposak, míg a lib/ lehet beágyazott (lib/payments/stripe).

A modulnév kötelező. Csak a könyvtárat megadni hiba — de egy hasznos hiba: handlers/ esetén kilistázza azokat a frissítéstípusokat, amelyekkel még nem rendelkezik, így másolhat egyet.

$ npx tgcloud add handlers
Hiba: Adjon meg egy nevet, pl. "handlers/callback_query". Elérhető handlers/ típusok: callback_query, inline_query, chat_member, …

A <Tab> kiegészítés ugyanazt a készletet kínálja — lásd completion .

A generált fájl rendelkezik egy aktív export default paranccsal, így a kezelő aktív, amint telepíti — nincs mit kikommentezni. Telepítse az új modult a push paranccsal.

login

npx tgcloud login

Kéri a CLI hozzáférési tokent — a @BotFather → botja → Serverless → CLI Access → Access token útvonalról, amely külön token a bot API tokenjétől —, érvényesíti a platformon, és elmenti a .tgcloud/credentials fájlba.

A login az egyetlen parancs, amely tokent kér, és interaktív terminált igényel. CI-környezetben állítsa be helyette a TGCLOUD_TOKEN változót.

status

npx tgcloud status

Fájlonként megmutatja, mi változott a munkakönyvtára és a telepített másolat között: módosított, új, törölt, változatlan. Offline működik, az utolsó push, pull vagy fetch művelet óta lévő felhőállapothoz hasonlítva. Figyelmeztet a tgcloud/ mappán belüli eltévedt .js fájlokra is, amelyek nem lesznek telepítve.

Mini Appot tartalmazó projekteknél a status azt is megmutatja, mi változott a build mappában az utolsó telepítés óta.

diff

npx tgcloud diff

Hasonló a status parancshoz, de megmutatja a tényleges soronkénti különbségeket a megváltozott moduloknál. Szintén offline.

push

npx tgcloud push [fájlok...]

Telepíti a projektjét a felhőbe egy atomi kötegben.

Argumentumok nélkül a teljes projektet telepíti, és a telepített állapotot pontosan a mappájához igazítja — azokat a modulokat, amelyeket helyben törölt, a felhőből is eltávolítja a rendszer.

Fájl vagy könyvtár argumentumokkal (npx tgcloud push tgcloud/handlers/message.js, npx tgcloud push handlers/) csak azokat a változásokat telepíti az adott fájlokban. A célzott push soha nem töröl más modulokat. A cél megadható fájl elérési útként (tgcloud/handlers/message.js), a tgcloud/ mappán belüli útként (handlers/message.js) vagy modulnévként (handlers/message) — mindegyik ugyanazt a modult jelenti.

Egyetlen opciója a --force — a konkurens ellenőrzés kihagyása és a felhőben lévő dolgok felülírása. Csak akkor használja, ha biztos benne (lásd Szinkronban maradás ).

Ha a tgcloud.jsonc megnevez egy statikus mappát, egy teljes push ugyanabban a kötegben telepíti azt — csak a megváltozott fájlok töltődnek fel — és kiírja a webhely címét. Építsen először, mert a push soha nem futtatja a buildjét (npm run deploy mindkettőt elvégzi). Az npx tgcloud push dist/ csak a statikus fájlokat telepíti, és egy olyan push, amely csak modulokat nevez meg, változatlanul hagyja azokat.

Telepítés után, ha a schema.js megváltozott és az adatbázis nincs szinkronban, a push kiír egy összefoglalót a függő változásokról és javasolja az npx tgcloud migrate parancsot. Soha nem alkalmazza ezeket saját maga.

migrate

npx tgcloud migrate

Alkalmazza a séma-változásokat az adatbázisra. Kiszámolja a különbséget a schema.js és az élő adatbázis között, majd egyenként végigvezeti Önt rajta egy folyamatos [N/M] számlálóval:

  • Összefoglaló az összes függő változásról.
  • Biztonságos változások, egyetlen lépésben alkalmazva megerősítés után.
  • Figyelmeztetések (törlések, lassú műveletek), egyenként, mindegyik külön megerősítve.
  • Kézi változások, okkal és javasolt lépéssel bemutatva, nem alkalmazva automatikusan.
  • Dokumentálatlan objektumok (az adatbázisban, de nem a sémájában), figyelmeztetésként bemutatva.

Összefoglalóval zárul: alkalmazva, kihagyva, kézi javításra várva, vagy nem szerepel a sémában.

Lásd a Migrációk modellt.

Opciók: --dry-run (mindent kinyomtat, semmit nem alkalmaz), --safe (automatikus biztonságos változások alkalmazása, figyelmeztetések kihagyása), --yes (automatikus biztonságos változások és minden figyelmeztetés alkalmazása, kézi kihagyása — óvatosan használja), --local (különbség a helyi schema.js vs. telepített ellen). Kapcsoló nélkül a migrate terminált igényel, és nem interaktív környezetben hibát jelez, a találgatás helyett.

run

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

Végrehajt egy kezelőt vagy végpontot a platformon telepítés nélkül, a jelenlegi helyi fájljai használatával. Ez a gyors belső kör a logika tesztelésére.

  • <modul> — egy sima név (keresés handlers/ és endpoints/ alatt) vagy egy út, mint handlers/message. Az olyan sima nevet, amely mindkét könyvtárban létezik, a könyvtárával együtt kell megadni.
  • [args] — a kezelőnek átadott payload, JSON5 formátumban, így elhagyhatja a kulcsok idézőjeleit. Ez az az objektum, amit a kezelője kap (pl. egy Message a handlers/message esetén) vagy egy végpont input értéke, és objektumnak kell lennie — nem tömbnek vagy sima stringnek.
  • --ctx <json5> — a modul kontextus objektuma (második argumentum), szintén JSON5. Használja arra, hogy megadja, mit olvas a kódja a ctx objektumból — a nyers frissítés egy kezelőhöz, --ctx '{ update: { update_id: 1 } }', vagy az init adatok egy végponthoz, --ctx '{ initData: { user: { id: 1 } } }'. A run nem ellenőrzi ezt, mivel a logikáját teszteli, nem a platform ellenőrzéseit.
npx tgcloud run handlers/message '{ chat: { id: 1 }, text: "hi" }'

A platform a helyi projektjéből összeállított modulteret használja a modul futtatásához (így a helyileg megváltoztatott lib/ kód is használatra kerül), és visszatér a visszatérési értékkel, a console.* által naplózott dolgokkal és az eltelt idővel. Olvassa a nagy argumentumokat fájlból: npx tgcloud run handlers/message "$(cat message.json5)".

fetch

npx tgcloud fetch

Ellenőrzi, mi van jelenleg telepítve, a munkafájljai érintése nélkül. Ezután a status és diff a legújabb felhőállapothoz hasonlítanak. Hasznos egy konfliktus megtekintéséhez, mielőtt döntene a megoldás módjáról.

pull

npx tgcloud pull

A helyi projektjét a telepített állapothoz igazítja a fájlok frissítésével.

A pull soha nem írja a Mini Appja build mappáját.

reset

npx tgcloud reset

Elveti a helyi változásokat és visszaállítja a modulokat az utolsó ismert felhőállapotból. Használja egy kísérlet eldobására. A Mini App build mappája érintetlen marad.

webhook

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

A Telegram webhorgon keresztül továbbítja a frissítéseket a bothoz, amit a platform kezel Ön helyett — soha nem kell sehova kézzel mutatnia. A npx tgcloud webhook megmutatja a jelenlegi állapotát: az URL-t, az allowed_updates listát, hány frissítés függő, az utolsó kézbesítési hiba (ha van), és hogy szinkronban van-e a telepített kezelőkkel.

A “Szinkronban” azt jelenti, hogy a webhorog a platformra mutat, és az allowed_updates listája megegyezik a telepített kezelőkkel — így a Telegram pontosan azokat a frissítéstípusokat kézbesíti, amelyeket kezel, és semmi mást. Új kezelő telepítése (vagy egy eltávolítása) a webhorgot szinkronon kívül hagyhatja a frissítésig; ezt az npx tgcloud status is jelzi.

A npx tgcloud webhook sync kijavítja ezt — újra a platformra irányítja a webhorgot és újraépíti az allowed_updates listát a telepített kezelőkből. Adja hozzá a --drop-pending kapcsolót azoknak a frissítéseknek az eldobásához, amelyeket a Telegram már sorba állított a szinkronizálás előtt (egyébként azok kézbesítődnek, amint a webhorog ismét egészséges).

completion

Megjegyzés: a tab-kiegészítés csak akkor működik, ha egy sima tgcloud a PATH útvonalon van — tehát telepítse globálisan (npm install -g @tgcloud/cli) vagy tegye a binárist a PATH útvonalra. Nem tud npx parancsra akaszkodni.

tgcloud completion <bash|zsh|fish>

Kinyomtat egy shell kiegészítő szkriptet a stdout-ra. Engedélyezze egyszer, azután a <Tab> kiegészíti a parancsokat, kapcsolókat, modulkönyvtárakat, azokat a frissítéstípus-kezelőket, amelyekkel még nem rendelkezik, és a helyi futtatható moduljait.

# bash — igényli a bash-completion csomagot:
echo 'eval "$(tgcloud completion bash)"' >> ~/.bashrc

# zsh — győződjön meg róla, hogy az `autoload -U compinit && compinit` fut a ~/.zshrc fájljában:
echo 'eval "$(tgcloud completion zsh)"' >> ~/.zshrc

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

Indítsa újra a shelljét (vagy source-olja újra a fájlt) utána. A tgcloud completion parancsot argumentumok nélkül futtatva újra kinyomtatja ezeket az utasításokat.

upgrade

npx tgcloud upgrade [--dry-run]

Egyszeri migráció a CLI 0.2.0 előtt létrehozott projektekhez, amelyek a schema.js, lib/ és handlers/ fájlokat a projekt gyökerében tartották. Áthelyezi őket a tgcloud/ mappába. A bothoz való kapcsolódása megmarad, és semmit nem kell újratelepíteni. A --dry-run kinyomtatja a tervezett lépéseket érintés nélkül. A többi parancs nem fut a régi elrendezésen az upgrade-ig.

Szinkronban maradás

Minden projektnek van egy monoton módon növekvő revíziója a felhőben, amely minden telepítéskor növekszik. A CLI emlékszik a revízióra, amivel utoljára szinkronizált, és minden push során elküldi. Ha a felhő előrehaladt — mert egy másik gép vagy csapattárs telepített —, a push visszautasításra kerül a munka csendes felülírása helyett, és a CLI három utat kínál előre:

npx tgcloud fetch           # nézze meg, mi változott a felhőben, majd ellenőrizze újra
npx tgcloud pull            # hozza a legújabbakat a munkafájljaiba
npx tgcloud push --force    # írja felül a felhő állapotát (veszélyes)

Ez az optimista-konkurens ellenőrzés az oka annak, hogy megoszthat egy botot egy csapattal anélkül, hogy kötött telepítési folyamat kellene. A parancsok sikertelen kimenet esetén nem nullát adnak vissza — visszautasított telepítés, sikertelen migráció, hitelesítési hiba, modul, amely hibát dobott run alatt —, így tisztán működnek szkriptekben és CI pipeline-okban.