Telegram Serverless vam omogućuje pokretanje backend koda za vaš bot i Mini Aplikaciju izravno na Telegramovoj infrastrukturi — bez potrebe za konfiguriranjem poslužitelja, održavanjem spremnika (kontejnera) ili razmišljanjem o skaliranju. Pišete obične JavaScript module, raspoređujete ih jednom naredbom, a Telegram ih pokreće u brzom, izoliranom V8 sandboxu koji se nalazi odmah uz Bot API i ugrađenu bazu podataka.
Ako ste ikada povezivali bot s VPS-om, cloud funkcijom ili hosting pločom samo kako biste odgovorili na /start, ovo je dio koji više ne morate raditi.
Zašto serverless
Telegram bot je, u svojoj srži, program koji reagira na ažuriranja. Tradicionalno ste taj program morali hostirati negdje gdje je uvijek uključen, dostupan i siguran — i potom ga takvim održavati. Telegram Serverless u potpunosti uklanja taj sloj:
- Bez infrastrukture. Nema stroja za iznajmljivanje, ažuriranje (patchiranje) ili nadzor. Vaš se kod pokreće na zahtjev i automatski se skalira s vašim botom.
- Vjerodajnice uključene. Telegram Bot API, baza podataka temeljena na SQLite-u i odlazni HTTP dostupni su svakom modulu odmah bez ikakvih dodatnih postavki — nema ničega za instalaciju, nema vjerodajnica za povezivanje.
- Brzo, izolirano izvršavanje. Svaki poziv pokreće se u laganom V8 izolatu, blizu Telegramovih vlastitih sustava, tako da su pozivi prema Bot API-ju i vašoj bazi podataka brzi i pouzdani.
- Stvarni tijek rada programera. Projekt živi u mapi na vašem stroju pod kontrolom verzija. Uređujete datoteke, vidite točno što se promijenilo, raspoređujete atomski i ažurirate shemu baze podataka s pregledanim migracijama — na način na koji već radite sa svime ostalim.
Mentalni model: Kako projekt funkcionira
Radite na tri mjesta, koja se jasno mapiraju jedno na drugo:
| Gdje | Što tamo živi |
|---|---|
| Mapa vašeg projekta | JavaScript moduli — shema, zajednički kod, rukovatelji ažuriranjima, krajnje točke Mini Aplikacije — i, ako ga imate, front-end Mini Aplikacije |
| Cloud | Raspoređena kopija tih modula, baza podataka vašeg bota i datoteke Mini Aplikacije |
tgcloud CLI |
Most — pokazuje vam razlike i sinkronizira ih |
Nikada se ne prijavljujete putem SSH-a. Uređujete datoteke lokalno, pokrećete npx tgcloud push i platforma preuzima ostalo. Promet vašeg bota obrađuju raspoređeni moduli; vaša baza podataka ostaje sačuvana između poziva.
Projekt ima samo četiri vrste koda, sve unutar jedne tgcloud/ mape:
tgcloud/
├─ endpoints/ # funkcije koje poziva vaša Mini Aplikacija
├─ handlers/ # funkcije koje reagiraju na vrste ažuriranja Telegrama
├─ lib/ # zajednički kod koji uvozite od bilo kuda
└─ schema.js # vaše tablice baze podataka
Kada stigne ažuriranje — poruka, pritisak na gumb, inline upit — Telegram ga usmjerava na odgovarajućeg rukovatelja (handlers/message.js, handlers/callback_query.js, …) i poziva njegov zadani izvoz (default export). Ta funkcija komunicira s Bot API-jem i bazom podataka putem SDK-a i vraća rezultat. To je cijela petlja. Ažuriranje bez odgovarajućeg rukovatelja jednostavno se ignorira, tako da dodajete samo rukovatelje koji su vam potrebni.
Mini Aplikacija je opcionalna. Ako vaš bot ima jednu, njezin front-end živi u istom projektu, pokraj tgcloud/. Serverless hostira njezin izlaz izrade (build output) i pokreće endpoints/ koje ona poziva. Vidi
Front-end Mini Aplikacije
.
Brza demonstracija
Ovdje je kompletan, funkcionalan demo bot. Odgovara na svaku poruku i pamti koliko ih je vidio iz svakog chata.
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;
// Umetnite brojač ili ga povećajte ako ovaj chat već ima jedan — i vratite
// rezultirajući redak u istoj naredbi putem .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: `Pozdrav! Vidio sam ${row.seen} poruku/a od tebe.`,
});
}
Rasporedite ga:
npx tgcloud push # učitajte module
npx tgcloud migrate # kreirajte `counters` tablicu
To je bot uživo s trajnim stanjem i bez poslužitelja. Sve u njemu — api, db, table() DSL — opisano je u odjeljcima ispod.
Serverless je opći backend za Telegram botove i Mini Aplikacije, a ne predložak za samo jednu vrstu aplikacije. Idealan je za:
- Razgovorne AI botove koji trebaju pohraniti stanje po korisniku u bazi podataka.
- Backendove za Mini Aplikacije koji pohranjuju korisničke podatke i poslužuju dinamički sadržaj.
- Igre i alate — uključujući ljestvice poretka, kvizove i još mnogo toga.
- Automatizacije i integracije koje pozivaju HTTP API-je trećih strana i guraju rezultate u chatove.
Početak rada
Ovaj vodič vas vodi od prazne mape do bota uživo koji odgovara na poruke i pohranjuje podatke. Pretpostavlja se da imate instaliran
Node.js
18 ili noviji i bot registriran kod
@BotFather
. Do kraja ćete koristiti svaku naredbu koja vam je svakodnevno potrebna: push, migrate, run i status.
Prije svega, uključite Serverless. U @BotFather , otvorite svoj bot → Serverless i uključite ga. To uključuje značajku za ovaj bot i otključava njegov pristupni token za CLI, rukovatelje, biblioteku i bazu podataka.
1. Kreirajte projekt
Najbrži način za početak je kreator projekta, koji stvara strukturu projekta i instalira CLI u njega:
npm create @tgcloud/bot example_bot
cd example_bot
Argument je ciljna mapa: proslijedite . za stvaranje u trenutnoj mapi ili bilo koju putanju. Radi i u postojećoj mapi i nikada ne prepisuje datoteke koje već imate.
Ovo vam daje projekt spreman za uređivanje:
example_bot/
├─ tgcloud/ # sve što platforma pokreće
│ ├─ handlers/
│ │ └─ message.js # početni rukovatelj porukama (vraća tekst natrag)
│ ├─ lib/ # ovdje idu vaši zajednički moduli (početno prazno)
│ └─ schema.js # vaše tablice baze podataka
├─ docs/
│ └─ tgcloud-sdk.md # SDK referenca (za vas i vaše AI alate)
├─ AGENTS.md # orijentacija za AI pomoćnike pri kodiranju
├─ package.json
└─ tgcloud.jsonc # konfiguracija projekta
Generirane datoteke su same sebi dokumentacija — svaka sadrži komentirane primjere onoga što možete sljedeće učiniti.
CLI se instalira u projekt kao lokalna razvojna ovisnost, tako da ga pokrećete s npx tgcloud <command> (npx pronalazi kopiju u node_modules vašeg projekta) ili putem npm run prečaca koje kreator dodaje u package.json (npm run deploy, npm run status). Prema zadanim postavkama, ne postoji globalni tgcloud na vašem PATH-u.
Također ga možete instalirati globalno — npm install -g @tgcloud/cli — ako želite tipkati običan tgcloud s bilo kojeg mjesta. To je zgodno za pokretanje tgcloud init u bilo kojoj praznoj mapi, a to je i ono što treba shell tab-completion. U oba slučaja dobivate isti projekt.
Gradite Mini Aplikaciju? Počnite od njezinog front-enda. Kreirajte ga svojim uobičajenim alatom, a zatim pokrenite kreator projekta u toj mapi. Vidi Front-end Mini Aplikacije .
2. Povežite svoj bot
Svaki projekt je vezan za jedan bot. Povežite ih s login, koji traži vaš CLI pristupni token (
@BotFather
→ vaš bot → Serverless → CLI Access → Access token — odvojen token od vašeg
API tokena bota
) i sprema ga lokalno:
npx tgcloud login
Token je oblika app<id>:<secret>. CLI ga drži u .tgcloud/, koji je ignoriran u git-u, i nikada ne ispisuje tajni dio. Prijavu (login) radite samo jednom — vidi
Autentifikacija
za to kako se tokeni razrješavaju u CI okruženju.
3. Razgledajte
Dvije naredbe vam govore gdje stvari stoje u bilo kojem trenutku, obje potpuno offline:
npx tgcloud status # što se promijenilo lokalno u odnosu na raspoređenu kopiju
npx tgcloud diff # promjene linija po linija
Odmah nakon init, sve je novo i ništa još nije raspoređeno. status prikazuje početne datoteke koje čekaju na prijenos.
4. Rasporedite (Deploy)
Pošaljite svoje module u cloud:
npx tgcloud push
push učitava svaki promijenjeni modul u jednoj atomskoj seriji i ažurira vaš lokalni zapis o tome što cloud sada sadrži. Vaš bot je aktivan: otvorite ga u Telegramu i pošaljite mu poruku — početni rukovatelj će vam je vratiti natrag.
Deploy nikada ne dira vašu bazu podataka. Prijenos koda i promjena sheme baze podataka su namjerno odvojeni koraci, tako da vas deploy koda nikada ne može iznenaditi migracijom podataka. To je ono za što služi sljedeći korak.
5. Dodajte tablicu u bazu podataka
Dajmo botu da nešto zapamti. Otvorite tgcloud/schema.js i deklarirajte tablicu:
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())`) ,
});
Rasporedite shemu, a zatim je primijenite na bazu podataka:
npx tgcloud push # učitava novu shemu
npx tgcloud migrate # kreira `messages` tablicu
push izvještava da shema nije sinkronizirana i prikazuje vam promjenu na čekanju, ali ne primjenjuje ništa. migrate vas vodi kroz promjenu i, uz vašu potvrdu, kreira tablicu. Ovaj model u dva koraka — i što se događa s rizičnijim promjenama poput brisanja — pokriven je u
Migracije
.
6. Pohranite i čitajte podatke
Sada koristite tablicu iz svog rukovatelja. Uredite tgcloud/handlers/message.js:
import { api, db } from 'sdk';
import { eq } from 'sdk/db';
import { messages } from '../schema.js';
export default async function (message) {
// Spremite ovu poruku.
await db.insert(messages)
.values({ chatId: message.chat.id, text: message.text })
.run();
// Izbrojite koliko smo ih pohranili za ovaj chat.
const count = await db.$count(messages, eq(messages.chatId, message.chat.id));
await api.sendMessage({
chat_id: message.chat.id,
text: `Spremljeno. To je zasad ${count} poruka iz ovog chata.`,
});
}
Rasporedite ažurirani rukovatelj s npx tgcloud push, zatim pošaljite botu nekoliko poruka i promatrajte kako broj raste. Baza podataka ostaje sačuvana između poziva — to je memorija vašeg bota.
7. Testirajte bez raspoređivanja
Ne morate raditi deploy da biste isprobali promjenu. npx tgcloud run izvršava rukovatelj na platformi koristeći vaše lokalne datoteke, bez da ih objavljuje:
npx tgcloud run handlers/message '{ chat: { id: 1 }, text: "hello" }'
Argument je payload koji vaš rukovatelj prima — za handlers/message,
Message
— napisan u
JSON5
formatu (tako da možete preskočiti navodnike oko ključeva). Naredba ispisuje sve što je rukovatelj zapisao s console.*, povratnu vrijednost i koliko je vremena trebalo. Ovo je najbrža petlja za iteraciju logike — bez deploya, bez čekanja na pravu poruku.
8. Održavajte sinkronizaciju
Kako radite, šačica naredbi održava vaš lokalni projekt i cloud usklađenima: npx tgcloud status pokazuje što se promijenilo, npx tgcloud push raspoređuje, npx tgcloud pull dovodi vaš lokalni projekt u liniju s cloudom, npx tgcloud fetch provjerava što je raspoređeno bez diranja vaših datoteka, a npx tgcloud reset odbacuje lokalne promjene.
Ako dvoje ljudi (ili dva stroja) raspoređuje na isti bot, platforma otkriva sukob i
pushstaje kako biste prvo moglipull-ati — nikada ne možete tiho prepisati tuđi rad. Vidi Održavanje sinkronizacije .
Gradnja s AI-jem
Preferirate li graditi s AI pomoćnikom — ili je jedini programer u vašem timu AI? I dalje možete isporučiti bot. Napravili smo prvi korak kako bi se AI agent osjećao kao kod kuće u projektu: svaki novi projekt generira se s AGENTS.md i docs/tgcloud-sdk.md referencom koju agentski alati za kodiranje automatski čitaju.
Zajedno s malim, samoodrživim runtime-om — jedan SDK, nema npm paketa za slaganje — to daje pomoćniku brz start s konvencijama koje generički kod obično propušta: relativni uvozi s .js, bez stranih ključeva (foreign keys), svaki db poziv je asinkron, jedan rukovatelj po vrsti ažuriranja i push/migrate tijek rada u dva koraka.
Pokušajte:
npm create @tgcloud/bot my-bot
cd my-bot
opencode # ili Claude Code, Cursor, … — bilo koji agent koji čita AGENTS.md
Zatim samo pitajte, običnim jezikom:
Napiši bot koji pamti listu zadataka svake osobe — dodaj stavku kada pošalju tekst i prikaži cijelu listu kada pošalju /list.
Pomoćnik uređuje schema.js i vaše rukovatelje umjesto vas; vi pregledate, odmah testirate promjenu s npx tgcloud run, a zatim idete uživo s npx tgcloud push i npx tgcloud migrate. AGENTS.md je dio vašeg projekta — uređujte ga kako bot raste kako bi upute ostale točne.
U pokretu s BotFatherom
Imate samo telefon? Cijeli projekt živi i u @BotFather — otvorite bot → Serverless i dobivate sve čime upravlja CLI, na dodirnom zaslonu:
- Rukovatelji (Handlers) — kreirajte, uredite i testirajte rukovatelje ažuriranjima; BotFather održava webhook u sinkronizaciji s rukovateljima koje imate (isto ono In sync / Out of sync što CLI izvještava).
- Krajnje točke (Endpoints) — funkcije koje poziva vaša Mini Aplikacija.
- Biblioteka (Library) — vaši zajednički
lib/moduli. - Baza podataka (Database) — uredite
schema.jsistom Drizzle-like sintaksom, pregledajte promjene na čekanju i primijenite ih; Save vrši deploy. - Datoteke Mini Aplikacije — front-end koji vaši korisnici otvaraju u Telegramu.
- CLI Access — dohvatite CLI pristupni token ovdje kada se vratite za tipkovnicu.
To je isti onaj cloud projekt, tako da možete započeti rukovatelj na telefonu i kasnije ga npx tgcloud pull-ati na laptop — ništa nije vezano za jednog klijenta. Pokretanje rukovatelja čak prikazuje njegov Console izlaz izravno u chatu, baš kao
npx tgcloud run
.
Projekti i moduli
Serverless projekt je obična mapa pod kontrolom verzija. Sve što platforma pokreće nalazi se u jednoj mapi, tgcloud/; ostatak projekta je vaš — front-end mini-aplikacije, konfiguracija, dokumentacija. Ne postoji korak izrade za bot, nema node_modules u runtime-u i nema ulazne točke poslužitelja.
Anatomija projekta
example_bot/
├─ tgcloud/ # raspoređeni dio — ništa drugo nije
│ ├─ handlers/ # rukovatelji ažuriranjima — ravno, samo jedna razina
│ │ ├─ message.js
│ │ └─ callback_query.js
│ ├─ endpoints/ # funkcije koje poziva Mini Aplikacija — ravno, jedna razina
│ │ └─ getProfile.js
│ ├─ lib/ # zajednički moduli; dopuštene podmape
│ │ ├─ reply.js
│ │ └─ internal/util.js
│ └─ schema.js # shema baze podataka — jedna datoteka
└─ .tgcloud/ # stanje CLI-ja, ignorirano u git-u
Samo .js datoteke unutar tgcloud/ — schema.js, lib/, handlers/, endpoints/ — se raspoređuju. Sve ostalo — Markdown, konfiguracijske datoteke, .tgcloud/ mapa, bilo što izvan tgcloud/ — ostaje na vašem stroju. Nazivi modula su relativni u odnosu na tgcloud/. Datoteka tgcloud/handlers/message.js je modul handlers/message, i to je naziv koji koristi npx tgcloud run.
Nadograđujete stariji projekt? Projekti kreirani prije CLI 0.2.0 držali su
schema.js,lib/ihandlers/u korijenu projekta. Pokrenitenpx tgcloud upgradejednom kako biste ih premjestili utgcloud/.
schema.js — vaša baza podataka. Deklarira tablice kao imenovane izvoze koristeći shema DSL i živi u tgcloud/ kao jedna datoteka. Raspoređuje se kao i svaki drugi modul, ali njezin deploy nikada ne mijenja bazu podataka — promjene sheme primjenjuju se odvojeno s npx tgcloud migrate. Vidi
Baza podataka
.
lib/ — zajednički kod, bilo što što želite ponovno koristiti u rukovateljima: čisti pomoćnici, slojevi za pristup bazi podataka, formatiranje, integracije s vanjskim servisima. lib/ je jedini direktorij koji može sadržavati podmape (lib/internal/util.js, lib/payments/stripe.js), tako da veću bazu koda možete organizirati kako god želite. Moduli u lib/ nikada se ne pozivaju izravno od strane platforme; oni postoje kako bi ih uvozili rukovatelji i drugi moduli.
handlers/ — ulazne točke vašeg bota. Svaka datoteka odgovara jednoj vrsti ažuriranja Telegrama, a platforma usmjerava svako dolazno ažuriranje na odgovarajućeg rukovatelja:
| Datoteka | Rukuje sa |
|---|---|
handlers/message.js |
Nove dolazne poruke |
handlers/inline_query.js |
Novi dolazni inline upiti |
handlers/callback_query.js |
Novi dolazni callback upiti |
| … | bilo koja druga vrsta ažuriranja Bot API-ja |
handlers/ je ravna — nema podmapa. export default rukovatelja je funkcija koju platforma poziva (vidi
Rukovatelji
).
Vrsta ažuriranja obrađuje se samo ako datoteka rukovatelja postoji i nije prazna. Ako nema handlers/<type>.js — ili je datoteka prazna — ažuriranja te vrste se ignoriraju, a platforma ne pokreće ništa za njih. Dakle, zadržite samo rukovatelje koji su vam stvarno potrebni: svaki koji dodate je još jedna vrsta ažuriranja koju vaš bot mora obraditi, a izostavljanje ostalih znači da Telegram ne pokreće vaš kod za ažuriranja koja biste ionako odbacili.
Za generiranje novog rukovatelja pokrenite
npx tgcloud add handlers/<type>.
endpoints/ — serverska strana vaše Mini Aplikacije. Svaka datoteka je funkcija koju Mini Aplikacija poziva po imenu, pa endpoints/getProfile.js odgovara na POST /api/getProfile. Platforma provjerava dolazi li poziv od korisnika Telegrama unutar vaše Mini Aplikacije prije nego što se vaš kod pokrene, i predaje vam tog korisnika. endpoints/ je ravna, poput handlers/. Vidi
Endpoints
.
.tgcloud/ — lokalno stanje stroja kojim upravlja isključivo CLI, uključujući vaše spremljene vjerodajnice. Ignorirano je u git-u i nikada ga ne biste trebali čitati ili pisati ručno — koristite CLI naredbe umjesto toga.
Sustav modula
U runtime-u, modul može vidjeti točno dvije stvari: platformski SDK i druge module u vašem projektu. Nema npm paketa, nema datotečnog sustava i nema mreže osim kroz fetch SDK-a.
Uvezite vlastite module na način na koji biste to učinili u bilo kojem JavaScript projektu — relativnom putanjom, s .js ekstenzijom — a SDK po njegovom imenu:
// tgcloud/handlers/message.js
import { users } from '../schema.js'; // modul sheme
import { addItem } from '../lib/cart.js'; // lib modul
import { format } from '../lib/internal/fmt.js'; // ugniježđeni lib modul
import { db, api, fetch } from 'sdk'; // platformski SDK
// tgcloud/lib/cart.js
import { format } from './internal/fmt.js'; // susjedni modul
import { users } from '../schema.js';
Relativni uvoz mora završavati s .js i ne može voditi izvan tgcloud/:
import { users } from '../schema'; // dodajte .js
import config from '../../config.js'; // izvan tgcloud/
import { z } from 'zod'; // nema npm paketa
Svaki modul također ima ime — svoju putanju unutar tgcloud/ bez ekstenzije, kao što je schema, lib/cart ili handlers/message. To ime koristi npx tgcloud run i izlaz CLI-ja, a modul se može uvesti i po njemu (from 'lib/cart', bez ekstenzije). Preferirajte relativne uvoze u svom kodu. Oni su ono što vaš uređivač razumije — skok na definiciju, automatsko dovršavanje, preimenovanje.
Ono što je dostupno u runtime-u su točno dvije stvari: sdk i njegovi podmoduli (sdk/db, sdk/api, sdk/fetch) — cijela površina platforme, vidi
SDK
— i vaši vlastiti moduli u tgcloud/. To je cijeli popis. Ako vaš kod uvozi (import) bilo što drugo, neće se razriješiti. Ovo ograničenje je ono što module čini bržima za učitavanje i sigurnima za pokretanje.
Rukovatelji
Rukovatelj je modul u handlers/ čiji zadani izvoz platforma poziva kada stigne odgovarajuće ažuriranje.
// handlers/message.js
import { api } from 'sdk';
export default async function (message) {
await api.sendMessage({
chat_id: message.chat.id,
text: `Rekao si: ${message.text ?? '(nema teksta)'}`,
});
}
Rukovatelj prima payload ažuriranja kao svoj argument — platforma raspakira Telegram
Update
umjesto vas. handlers/message.js dobiva
Message
(tj. update.message); handlers/callback_query.js dobiva
CallbackQuery
; i tako dalje. Drugi argument rukovatelja je objekt konteksta po pozivu, ctx. On nosi sirovi Update kao ctx.update — posegnite za njim kada trebate nešto izvan payloada, poput update_id.
Rukovatelj može biti async (obično jest) i može vratiti vrijednost. On doseže Bot API, bazu podataka i odlazni HTTP kroz SDK.
Ne treba vam pravo ažuriranje da biste testirali rukovatelj. npx tgcloud run izvršava ga na platformi s payloadom koji vi dostavite i vašim trenutnim lokalnim kodom:
npx tgcloud run handlers/message '{ chat: { id: 1 }, text: "hi" }'
Argument je payload — isti objekt koji vaš rukovatelj prima — u JSON5 formatu. Za dostavu ctx-a rukovatelja (njegovog drugog argumenta), dodajte --ctx, npr. --ctx '{ update: { update_id: 1 } }'. Ovo se pokreće protiv vaših lokalnih datoteka, tako da možete isprobati promjene prije nego što ih rasporedite. Vidi
run
.
Krajnje točke (Endpoints)
Krajnja točka je modul u endpoints/ koji poziva vaša Mini Aplikacija — mjesto za sve što front-end ne može učiniti sam, poput čitanja i pisanja u bazi podataka, pozivanja Bot API-ja ili razgovora s vanjskim servisima s vašim tajnama.
// 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 nije pronađen', { userId: user.id });
return profile;
}
Prvi argument, input, je JSON objekt koji je Mini Aplikacija poslala. Drugi, ctx, nosi ctx.initData —
init podatke
Mini Aplikacije (user, chat, start_param, auth_date, …), koje je platforma već provjerila. Ima isti oblik kao Telegram.WebApp.initDataUnsafe na klijentu, ali ovdje mu možete vjerovati. Poziv bez valjanih init podataka odbija se s 401 prije nego što se vaš kod pokrene, tako da je ctx.initData.user stvarno osoba koja je otvorila vašu Mini Aplikaciju. Krajnje točke se mogu pozivati samo na ovaj način — iz Mini Aplikacije vašeg bota, od strane Telegram korisnika — tako da nema javnog API-ja za zaštitu i nema prijave za graditi.
Što god krajnja točka vrati, šalje se natrag kao JSON. Da biste odbili poziv, throw new EndpointError(description, parameters?). Mini Aplikacija dobiva opis i objekt parameters kakvi jesu. Ako trebate granati o vrsti pogreške na front-endu, stavite kod u parameters — { code: 'OUT_OF_STOCK' }. Svaka druga iznimka prijavljuje se kao generička interna pogreška, a prava poruka ide samo u vaše logove, tako da interne informacije nikada ne cure prema klijentu.
Pozivanje krajnje točke iz Mini Aplikacije
Mini Aplikacija poziva krajnju točku po imenu s Telegram.WebApp.Serverless.call iz službenog
Mini Apps SDK-a
, telegram-web-app.js — skripte koju vaša Mini Aplikacija već uključuje. Ona šalje init podatke umjesto vas i poziva povratni poziv (callback) s pogreškom ili rezultatom krajnje točke:
Telegram.WebApp.Serverless.call('getProfile', { lang: 'en' }, function (err, profile) {
if (err) {
// err.message — opis; err.parameters — što je EndpointError nosio
if (err.type === 'ENDPOINT_ERROR') showMessage(err.message);
return;
}
render(profile);
});
Input je opcionalan i mora biti običan objekt. Bez njega poziv je Serverless.call('getProfile', callback).
err.type je 'ENDPOINT_ERROR' kada je vaša krajnja točka bacila EndpointError. Bilo koja druga vrijednost znači da je platforma odbila poziv ili da krajnja točka nije uspjela neočekivano, na primjer 'UNAUTHORIZED' kada init podaci nedostaju ili su nevažeći.
Imena krajnjih točaka su poput JavaScript identifikatora — slova, znamenke i _, ne smiju počinjati znamenkom (getProfile, place_order). Generirajte jednu s
npx tgcloud add endpoints/<name>
i isprobajte je bez Mini Aplikacije s
run
, prosljeđujući init podatke sami:
npx tgcloud run endpoints/getProfile '{}' --ctx '{ initData: { user: { id: 1, first_name: "Ann" } } }'
Što se raspoređuje (deploja)
Kada napravite npx tgcloud push, CLI prikuplja svaku .js datoteku ispod schema.js, lib/, handlers/ i endpoints/, i šalje taj točan skup kao prostor modula vašeg projekta — zajedno s build mapom Mini Aplikacije, ako je tgcloud.jsonc imenuje (vidi dolje). Sve što je prisutno u cloudu, a odsutno u vašem projektu, uklanja se, tako da raspoređeno stanje uvijek zrcali vašu mapu — uključujući brisanja. Ništa izvan tgcloud/ se ne raspoređuje kao kod — ostatak korijena projekta je vaš. Unutar tgcloud/, .js datoteka koja nije na jednoj od tih lokacija (tgcloud/util.js, tgcloud/helpers/x.js) se označava, tako da se ne rasporedi slučajno. Markdown, dot-datoteke i .tgcloud/ se nikada ne raspoređuju.
Front-end Mini Aplikacije
Korijen projekta je vaš, i to je prirodno mjesto za front-end vaše Mini Aplikacije — izgrađen s Reactom, Vueom, Svelteom ili običnim HTML-om, putem Vitea ili bilo kojeg drugog bundlera. Serverless hostira njezin izlaz izrade (build output) pokraj vašeg bota. Jedan push raspoređuje kod i statičke datoteke zajedno, a aplikacija se poslužuje na https://app<app_id>.tgcloud.ai/ (push ispisuje točnu adresu — koristite je kao URL vaše Mini Aplikacije u
@BotFather
).
Prijava zlouporabe. Mini Aplikacije na tgcloud.ai objavljuju neovisni programeri. Za prijavu nezakonitog sadržaja ili zlouporabe na
app<app_id>.tgcloud.aipoddomeni, pošaljite e-mail na [email protected] .
Za početak, prvo kreirajte front-end, bilo kojim alatom koji vam se sviđa, a zatim dodajte Serverless na vrh:
npm create vite@latest example_app # ili create-vue, …
cd example_app
npm create @tgcloud/bot .
Kreator projekta nikada ne prepisuje vaše datoteke. On dodaje tgcloud/ mapu pokraj front-enda, prepoznaje build mapu i dodaje npm run deploy skriptu koja gradi aplikaciju i raspoređuje je zajedno s botom.
Ono što se hostira postavlja se u tgcloud.jsonc, konfiguracijskoj datoteci projekta u korijenu:
{
"$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 }]
}
}
Samo je "source" obavezan. To je build mapa, relativna u odnosu na korijen projekta. Ostali ključevi "static" konfiguriraju kako se ona poslužuje (ispod). init kreira tgcloud.jsonc za vas. Kada prepozna front-end, također popunjava "source":
| Front-end | init postavlja "source" na |
|---|---|
| Vite — React, Vue, Svelte, Solid, … | dist |
| Next.js, Nuxt, SvelteKit, React Router, TanStack Start, Angular | ništa — prvo konfigurirajte statički izvoz, a zatim sami postavite "source" na mapu |
Uz bilo koju drugu postavku, sami postavite "source" na svoju build mapu.
Linija $schema daje vašem uređivaču automatsko dovršavanje i validaciju za svaki ključ. Datoteka je JSON s komentarima i dopuštenim zarezima na kraju. Ako drugi vaš alat mora čitati kao običan JSON, nazovite je tgcloud.json — tada mora biti strogi JSON.
Izgradite prije nego što gurnete (push). push učitava mapu točno onakvu kakva jest na disku — osim datoteka i mapa čije ime počinje točkom, koje se preskaču — i nikada ne pokreće vašu izradu (build), tako da bi običan npx tgcloud push nakon uređivanja front-enda rasporedio prethodnu izradu. Koristite npm run deploy (pokreće vašu build skriptu, zatim tgcloud push) ili prvo sami izgradite.
Opcije posluživanja:
spa— poslužujeindex.htmlza putanje koje ne postoje i nemaju ekstenziju datoteke, tako da klijentski usmjerivač radi pri ponovnom učitavanju. Putanje s ekstenzijom i dalje dobivaju404.immutable— uzorci za datoteke s hashiranim sadržajem (Viteov/assets/*) koje preglednici mogu cachirati godinu dana. Koristite samo za datoteke s hashiranim sadržajem. Bilo koja druga datoteka podudarena ovdje ostala bi zastarjela u preglednicima godinu dana, i nema načina da se opozove.headers— zaglavlja odgovora za putanje koje pravilo podudara (točna putanja ili prefiks koji završava s/*).nullvrijednost uklanja zaglavlje.redirects— pravila preusmjeravanja, gdje prvo podudaranje pobjeđuje. Postojeća datoteka uvijek pobjeđuje pravilo.statusje301osim ako ga ne postavite."static": "dist"— kratica za{ "source": "dist" }kada vam ništa drugo ne treba."static": false— uklonite raspoređenu stranicu pri sljedećem push-u. Brisanje ključa samo prestaje upravljati statičkim datotekama za ovaj projekt, a ono što je raspoređeno ostaje.
Stranica se poslužuje iz korijena vlastitog hosta, tako da je zadana base (/) vašeg bundlera točna. Telegram Web otvara Mini Aplikacije u iframeu, pa nemojte dodavati X-Frame-Options ili restriktivnu frame-ancestors politiku.
Isti host odgovara na vaše
krajnje točke
pod /api/ — tamo odlazi Telegram.WebApp.Serverless.call('getProfile', …). Nemojte stavljati vlastite datoteke pod /api/ u build mapu.
Baza podataka
Svaki bot dobiva svoju bazu podataka — SQLite spremište koje ostaje sačuvano između poziva i dostupno je svakom modulu kroz db. Svoje tablice opisujete u schema.js s malim, tipiziranim DSL-om; čitate i pišete ih s fluentnim graditeljem upita; i razvijate ih s pregledanim migracijama.
Znate li Drizzle ORM ? Tada već znate kako ovdje komunicirati s bazom podataka. Shema DSL i graditelj upita prate Drizzle — graditelji stupaca,
select().from().where(), operatori,onConflictDoUpdate,.returning()isqloznaka ponašaju se onako kako očekujete, pa je čitanje i pisanje podataka poznati API koji već koristite. Samo ga uvozite izsdk/db, a nekoliko platformskih specifičnosti (ponajviše bez stranih ključeva ) istaknuto je tamo gdje se pojavljuju.
Deklariranje tablica
Tablice su imenovani izvozi u schema.js. Pozivanje table() gradi opis u vrijeme učitavanja (ne dira bazu podataka); platforma otkriva izvezene tablice kada rasporedite schema.js i migrira bazu podataka da se podudara.
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(name, columns, extras?):
nameje SQL naziv tablice;columnsje mapa JS svojstvo → definicija stupca;extrasje opcionalni povratni poziv(t) => ({ … })gdjetizlaže stupce (t.createdje referenca na taj stupac) — ovdje deklarirajte indekse i ograničenja na razini tablice.
Vrste stupaca
| Tvornica | SQLite vrsta | Bilješke |
|---|---|---|
text() |
TEXT | |
integer() |
INTEGER | |
real() |
REAL | alias float() |
numeric() |
NUMERIC | |
blob() |
BLOB | čita/piše Uint8Array |
boolean() |
INTEGER | pohranjeno kao 0/1, čita se kao true/false |
json() |
TEXT | auto JSON.stringify / JSON.parse |
Argument naziva stupca je opcionalan — izostavite ga i koristi se JS ključ. Opcija
modekontrolira kako se vrijednosti pretvaraju između SQLite-a i JavaScripta.
mode |
Pohranjeno kao | JS vrijednost |
|---|---|---|
boolean |
INTEGER 0/1 | boolean |
json |
TEXT (JSON) | bilo koji objekt/niz |
timestamp |
INTEGER (unix sekunde) | Date |
timestamp_ms |
INTEGER (unix ms) | Date |
bytes |
BLOB | Uint8Array |
boolean() i json() su kratice za integer(name, { mode: 'boolean' }) i text(name, { mode: 'json' }).
blob() čita i piše Uint8Array — runtime nema Node Buffer (a Buffer je podklasa Uint8Array, pa je ovo prijenosni osnovni tip). Gore navedeni mode kontrolira samo kako se vrijednost kodira, neovisno o vrsti pohrane stupca: pa je blob('col', { mode: 'json' }) BLOB pandan json()-u — ista JSON kodiranja, zadržana u BLOB stupcu umjesto TEXT.
TLDR:
blob()koristiUint8Array.modekontrolira kodiranje, a ne pohranu, tako dablob(..., { mode: 'json' })pohranjuje JSON kao BLOB, dokjson()pohranjuje kao TEXT.
Modifikatori stupaca
Modifikatori stupaca nadovezuju se na stupac.
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' }) // ili 'virtual'
text('email').deprecated('replaced by login') // označava stupac za uklanjanje
.default()najson()stupcu kodira vrijednost umjesto vas;sql`…`default se prosljeđuje doslovno..deprecated()je terminalan — vidi Migracije .
Indeksi i ograničenja
Indeksi i ograničenja deklariraju se u extras povratnom pozivu, gdje su stupci u dosegu.
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})`) , // izrazni indeks
active: index('idx_active').on(t.userId).where(sql`done = 0`) , // djelomični indeks
}));
Modifikatori na razini tablice nadovezuju se nakon table(...): .strict(), .withoutRowid(), .deprecated('razlog').
Bez stranih ključeva (No foreign keys)
Runtime radi s PRAGMA foreign_keys isključenim. Deklarirani strani ključ bio bi tiho inertan — nema kaskada, nema zaštite od siročadi — što je gore nego nemati ih uopće, pa DSL čini nemogućim njihovo korištenje. Naime, .references() i foreignKey() na razini tablice bacaju pogrešku kada se deklariraju, a shema koja ih koristi se neće rasporediti.
Odnose biste trebali modelirati običnim stupcima (userId: integer('user_id')) i provoditi integritet u svom aplikacijskom kodu: umetnite roditelje prije djece, brišite djecu prije roditelja, pravilno rukujte pogreškama i očistite siročad s LEFT JOIN … WHERE parent.id IS NULL kada je potrebno.
Odsutnost stranih ključeva je namjerno ograničenje, a ne previd. Uračunajte to rano kada planirate bot.
Upiti
db je fluentni graditelj upita. Svaki upit je asinkron — terminalne metode (.all(), .get(), .values(), .run()) vraćaju Promise, pa uvijek koristite 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(); // svi redovi
await db.select().from(todos).where(eq(todos.id, 1)).get(); // prvi red ili null
await db.select().from(todos).values(); // redovi kao nizovi vrijednosti
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();
// prilagođena projekcija: { 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();
// broj redaka — pomoćnik, ne terminal graditelja:
await db.$count(todos); // svi redovi
await db.$count(todos, eq(todos.done, false)); // s filtrom
- Chainable:
.where(),.orderBy(),.limit(),.offset(),.groupBy(),.having(),.distinct(). - Terminali:
.all(),.get(),.values(). - Brojanje redaka s
db.$count(table, where?)(ilicount()u projekciji).
Umetanje, ažuriranje, brisanje:
await db.insert(todos).values({ userId: 1, text: 'Kupi mlijeko' }).run();
await db.insert(todos).values([{ text: 'A' }, { text: 'B' }]).run(); // serija
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();
Imajte na umu da je serijsko umetanje jedna naredba, pa je ograničeno SQLite varijabilnim limitom (
redovi × stupci); ako ga premašite, umetanje javlja pogrešku — podijelite ga sami u komade.
Operatori se uvoze iz 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 više argumenata isto je što i and(a, b). Drugi argument usporedbe je prema zadanim postavkama vrijednost, ali može biti drugi stupac ili sql`…` — npr. eq(a.x, b.y). Agregati (count/sum/avg/min/max) su sql fragmenti za .select({ … }) projekciju.
Sirovi SQL — kada graditelj nije dovoljan, spustite se na sirovi SQL. Način rada bira se metodom: db.run za pisanja, db.all za mnogo redaka, db.get za jedan red, db.values za redove kao pozicijske nizove.
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, 'kupi mlijeko']]
Pisanje se razrješava u rezultat pokretanja — rowsAffected, lastInsertRowid i rows, koji nose RETURNING klauzulu ako je SQL imao jednu. Posegnite za db.values kada upit projicira dva stupca istog imena (join poput SELECT u.id, o.id): db.all ključira redove prema imenu stupca, pa bi drugi id prepisao prvi.
sql`…` oznaka pretvara ${value} u vezani parametar, a ${table.column} u identifikator, i spaja ugniježđene sql fragmente. Koristite sql.raw('…') za doslovni tekst bez parametara.
Sirovi upiti nisu vezani za tablicu, pa njihovi redovi dolaze natrag bez pretvorbe moda — boolean je
0/1, JSON stupac je niz, timestamp je broj. Samo graditelj vezan za tablicu pretvara vrijednosti.
Migracije
Vaša baza podataka mijenja se kako vaš bot raste. Platforma to čuva sigurnim odvajanjem raspoređivanja koda od promjene podataka, i klasificiranjem svake promjene sheme prema tome koliko je rizična.
Deploy nikada ne dira bazu podataka. Kada napravite npx tgcloud push promijenjenog schema.js, platforma bilježi novu shemu i govori vam što bi se u bazi podataka promijenilo — ali ne primjenjuje ništa:
npx tgcloud push # rasporedi schema.js; javlja promjene DB-a na čekanju
npx tgcloud migrate # pregledaj i primijeni ih
npx tgcloud migrate računa razliku između vaše sheme i baze podataka uživo i vodi vas kroz to. Uvijek vas se pita prije nego što se išta primijeni. To znači da rutinski deploy koda nikada ne može slučajno pokrenuti migraciju podataka.
Svaka promjena na čekanju nosi status koji određuje kako migrate postupa s njom:
| Status | Značenje | U migrate |
|---|---|---|
| safe | Aditivno i neblokirajuće — nova tablica, stupac ili indeks | Primijenjeno zajedno u jednom koraku, uz potvrdu |
| warning | Potencijalno destruktivno ili sporo — brisanje nečega ili indeks na ogromnoj tablici | Prikazano jedno po jedno, svako se potvrđuje zasebno |
| manual | Ne može se učiniti automatski — npr. promjena vrste stupca | Prikazano s uputama; izvodite ručno |
| undocumented | Postoji u bazi podataka, ali ne u vašoj shemi | Prikazano za svijest; ne primjenjuje se |
Sigurne promjene su brze i reverzibilne u duhu, pa prolaze zajedno. Svako upozorenje je namjerna, pojedinačna potvrda — ne postoji “primijeni sve” za destruktivne promjene.
Ručne promjene dolaze s razlogom i predloženom radnjom. migrate završava sažetkom: koliko je promjena primijenjeno, preskočeno, čeka ručni popravak ili nisu u vašoj shemi.
Vidi
migrateza zastavice (--dry-run,--safe,--yes,--local).
Uklanjanje stvari
Brisanje tablice ili stupca iz schema.js ne briše ih — to bi učinilo slučajno brisanje katastrofalnim. Da biste nešto uklonili, označite kao deprecated:
// obriši stupac
text('email').deprecated('zamijenjeno s login')
// obriši cijelu tablicu
export const oldSessions = table('old_sessions', { /* … */ }).deprecated('nekorišteno');
Pri sljedećoj migraciji, deprecated objekt pojavljuje se kao brisanje s warning statusom koje potvrđujete pojedinačno. Jednom kada je nestalo, uklonite deklaraciju.
Promjena vrste stupca
Promjene vrste su manual — SQLite ih ne može uvijek učiniti na mjestu, a prisiljavanje postojećih vrijednosti je stvar procjene. migrate će prikazati promjenu i njezino obrazloženje; izvedite je sami sa sirovim SQL-om (db.run(...)), obično kreiranjem novog stupca ili tablice, kopiranjem podataka i zamjenom.
SDK
U runtime-u modul ima jednu biblioteku: sdk. Ona objedinjuje tri stvari koje backend bota treba — bazu podataka, Telegram Bot API i odlazni HTTP — bez instalacije i konfiguracije vjerodajnica. Baza podataka (db) pokrivena je u
Baza podataka
; ovaj odjeljak pokriva api, fetch i globalni console.
import { db, api, fetch, InputFile, BotApiError, EndpointError } from 'sdk'; // cijela površina
// ili iz podmodula:
import { table, integer, text, eq, sql } from 'sdk/db';
import { api } from 'sdk/api';
import { fetch } from 'sdk/fetch';
| Uvoz | Što je |
|---|---|
db |
Baza podataka — graditelj upita i shema DSL → Baza podataka |
api |
Telegram Bot API — api.sendMessage(...) →
ispod
|
fetch |
Odlazni HTTP → ispod |
InputFile |
Datoteka (bajtovi + ime) za učitavanje — radi s api i fetch →
Datoteke
|
EndpointError |
Odbijte poziv Mini Aplikacije s porukom koju može prikazati → Endpoints |
Uvezite vlastite module projekta relativnom putanjom s .js ekstenzijom (from '../schema.js', from '../lib/cart.js'). Vidi
Sustav modula
.
Bot API
api vam daje cijeli
Telegram Bot API
. Pozovite bilo koju metodu kao api.<method>(params). Svaka trenutna — i buduća — metoda Bot API-ja radi bez potrebe za ažuriranjem SDK-a.
import { api } from 'sdk';
const me = await api.getMe(); // → raspakirani rezultat
await api.sendMessage({ chat_id: id, text: 'Pozdrav!' });
await api.editMessageText({ chat_id, message_id, text: 'Ažurirano' });
await api.answerCallbackQuery({ callback_query_id, text: 'Gotovo' });
Omotnica odgovora je raspakirana. Bot API obično omata rezultate u { ok: true, result: … }. api vraća result izravno — getMe() se razrješava u objekt korisnika, a ne u omotnicu. Parametri koriste Bot API-jeva vlastita snake_case imena (chat_id, message_id, reply_markup, …).
Pogreške bacaju BotApiError. Kada Bot API vrati { ok: false }, poziv baca BotApiError umjesto vraćanja falsy vrijednosti, tako da je ne možete slučajno ignorirati. Pogreška nosi .code (Bot API error_code), .description (čovjeku čitljiva poruka), .method (koja metoda nije uspjela) i .parameters (dodatni podaci poput retry_after na 429, ili migrate_to_chat_id). Uhvatite je da biste obradili očekivanu pogrešku i ponovno bacili ostatak:
import { api, BotApiError } from 'sdk';
try {
await api.deleteMessage({ chat_id, message_id });
} catch (e) {
if (e instanceof BotApiError && e.code === 400) {
// 400 = poruka je već nestala; to je u redu ovdje.
} else {
throw e;
}
}
Datoteke
Radite s bajtovima datoteka, a ne samo file_id-jevima — učitajte nove datoteke i preuzmite postojeće.
Učitavanje — InputFile. Omotajte sirove bajtove u InputFile (običan spremnik: bajtovi, naziv datoteke i opcionalni MIME type) i proslijedite ga gdje god metoda Bot API-ja očekuje datoteku:
import { api, InputFile } from 'sdk';
const doc = new InputFile(bytes, 'izvjestaj.pdf', { type: 'application/pdf' });
await api.sendDocument({ chat_id, document: doc });
// datoteke ugniježđene unutar parametara također rade — npr. 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' }) },
]});
Bajtovi se streamaju na Telegram kao pravo multipart učitavanje — datoteka se ne bufferira cijela u jedan poziv platformi — ali ograničenja Telegram Bot API-ja i dalje vrijede: bot trenutno može učitati do 50 MB po datoteci (i 10 MB za fotografiju).
Preuzimanje — po file_id. Dva pomoćnika povlače bajtove datoteke natrag. Platforma razrješava file_id umjesto vas (pokreće getFile i dohvaća sadržaj u pozadini), tako da nikada ne dirate bot token ili URL za preuzimanje:
const bytes = await api.getFileContent(file_id); // → Uint8Array (cijela datoteka)
const { file, body } = await api.getFileStream(file_id); // streamajte umjesto toga
file.file_unique_id; // info o getFile
for await (const chunk of body) { /* Uint8Array */ } // ili bufferirajte: await stream.bytes()
Telegram Bot API ograničava
getFilepreuzimanja na 20 MB, pa je to gornja granica za oba pomoćnika.
HTTP
fetch je fetch-like klijent za pozivanje vanjskog svijeta — API-ja trećih strana, webhookova, bilo čega preko HTTP-a.
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();
Odgovor zrcali web platformu: res.status, res.statusText, res.ok (true za 200–299), res.url, res.headers (.get(), .has(), .keys(), .entries()) i čitači tijela await res.json() / await res.text().
Također možete čitati tijelo postupno kao stream —
for await (const chunk of res.body) { … }— što je način na koji konzumirate server-sent događaje ili token-po-token izlaz iz AI API-ja.
Pomoćnici tijela postavljaju odgovarajuće Content-Type umjesto vas:
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
Tijela zahtjeva također mogu nositi datoteke i sirove bajtove: proslijedite Uint8Array, InputFile ili FormData (za multipart/form-data) kao body, i bajtovi se streamaju van — tako da možete učitati u API treće strane na isti način kao što šaljete datoteke na Telegram:
import { fetch, FormData, InputFile } from 'sdk';
const form = new FormData();
form.append('file', new InputFile(bytes, 'photo.jpg', { type: 'image/jpeg' }));
form.append('caption', 'iz mog bota');
const res = await fetch('https://api.example.com/upload', { method: 'POST', body: form });
Inače se ponaša poput standardnog fetch-a kojeg već poznajete, s dva ograničenja:
- Odgovor se čita kao tekst —
await res.json()/await res.text()ili streamano komad-po-komad sres.body. - Ukupni odgovor je ograničen na 30 MB. Ta granica pokriva cijeli odgovor — streamanje s
res.bodyomogućuje vam postupnu obradu velikog tijela, ali ne podiže limit.
Zapisivanje (Logging)
Standardni globalni console je dostupan — ništa za uvoz, samo je tu kao i u bilo kojem JavaScriptu. Njegov izlaz se snima i prikazuje od strane npx tgcloud run, što ga čini vašim primarnim alatom za debagiranje tijekom razvoja.
console.log('obrada', { chatId: id }); // log / debug — obično
console.info('započeto'); // info — plavo
console.warn('stopa ograničena'); // warn — žuto
console.error(err); // error — crveno, s ispisom stoga (stack trace)
Svaka linija je označena s [datoteka:linija] odakle dolazi. console.error i console.trace dodaju puni stack, dok console.warn ne dodaje. Kada npx tgcloud run-ate modul, ove linije se ispisuju s obojenim prefiksom po razini, vremenom od početka pokretanja i porijeklom — vidi
run
.
Sučelje naredbenog retka (CLI)
tgcloud je most između mape vašeg projekta i clouda. On stvara strukturu projekata, pokazuje vam što se promijenilo, raspoređuje, pokreće module i primjenjuje migracije baze podataka. Potreban mu je
Node.js
18 ili noviji. Dva načina da ga dobijete:
# Preporučeno — kreirajte projekt s CLI-jem instaliranim u njega:
npm create @tgcloud/bot example_bot
# Ili instalirajte CLI globalno i inicijalizirajte praznu mapu:
npm install -g @tgcloud/cli
tgcloud init
NPM paket je @tgcloud/cli, a naredba koju instalira je tgcloud. Svaka naredba radi iz bilo koje podmape vašeg projekta.
| Naredba | Svrha |
|---|---|
init
|
Generiraj novi projekt u trenutnoj mapi |
add
|
Generiraj novi modul (rukovatelj, krajnja točka ili lib modul) |
login
|
Poveži projekt s botom (sprema token) |
status
|
Pokaži što se promijenilo lokalno u odnosu na cloud |
diff
|
Pokaži razlike liniju po liniju |
push
|
Rasporedi promijenjene module i build Mini Aplikacije u cloud |
migrate
|
Primijeni promjene sheme na bazu podataka |
run
|
Izvrši modul na platformi bez raspoređivanja |
fetch
|
Provjeri što je raspoređeno, bez mijenjanja lokalnih datoteka |
pull
|
Dovedi lokalne datoteke u liniju s cloudom |
reset
|
Odbaci lokalne promjene; obnovi iz stanja clouda |
webhook
|
Pregledaj i ponovno sinkroniziraj webhook kojim upravlja platforma |
completion
|
Ispiši shell completion skriptu (bash/zsh/fish) |
upgrade
|
Premjesti layout projekta prije 0.2.0 u tgcloud/ |
Autentifikacija
Projekt je vezan za jedan bot pomoću tokena koji je oblika app<id>:<secret>. Dio app<id> je javni i može se ispisati; secret se nikada ne pojavljuje u logovima ili pogreškama.
Token se razrješava ovim redoslijedom:
TGCLOUD_TOKENvarijabla okruženja — za CI; nikada se ne piše na disk..tgcloud/credentials— piše ganpx tgcloud login.- Ništa od navedenog → pogreška koja vas upućuje na
npx tgcloud login.
CLI nikada ne traži token usred naredbe. Prijava (login) je uvijek eksplicitan korak login. Ako spremljeni token postane nevažeći, CLI ga uklanja i traži da se ponovno prijavite.
init
npx tgcloud init
Generira novi projekt u trenutnom direktoriju: tgcloud/ mapu s schema.js, lib/, handlers/ i početnim rukovateljom, plus tgcloud.jsonc, AGENTS.md, docs/ i .tgcloud/ mapu stanja. Pokrenite ga i u korijenu postojećeg front-end projekta — dodaje tgcloud/ mapu i dokumentaciju, a nikada ne prepisuje vaše datoteke. Ako prepozna front-end, također usmjerava tgcloud.jsonc na build mapu i dodaje deploy skriptu (build, zatim push) u vaš package.json — samo ako već nemate deploy skriptu. Vidi
Front-end Mini Aplikacije
.
init odbija kreirati projekt unutar drugog projekta. Ponovno pokretanje u korijenu vlastitog projekta je u redu i samo dodaje ono što nedostaje.
add
npx tgcloud add <target>
Generira jedan novi modul, povezan i spreman za uređivanje — rukovatelj, krajnju točku ili lib/ modul.
npx tgcloud add handlers/callback_query # novi rukovatelj ažuriranjima
npx tgcloud add endpoints/getProfile # nova krajnja točka za Mini Aplikaciju
npx tgcloud add lib/cart # novi zajednički modul
Imajte na umu da
addnikada ne prepisuje postojeću datoteku.
<target> je putanja modula (prateći .js je opcionalan). Za handlers/, naziv mora biti vrsta ažuriranja Telegrama. Platforma oglašava valjani skup, pa se nevažeće ime odbija unaprijed. handlers/ i endpoints/ su ravni, dok lib/ može biti ugniježđen (lib/payments/stripe).
Naziv modula je obavezan. Davanje samo direktorija je pogreška — ali korisna: za handlers/ ispisuje vrste ažuriranja koje već nemate, pa možete kopirati jedno.
$ npx tgcloud add handlers
Greška: Navedite naziv, npr. "handlers/callback_query". Dostupne vrste handlers/: callback_query, inline_query, chat_member, …
<Tab>completion nudi isti skup — vidicompletion.
Generirana datoteka ima live export default, pa je rukovatelj aktivan čim ga rasporedite — nema ničega za odkomentirati. Rasporedite novi modul s
push
.
login
npx tgcloud login
Traži vaš CLI pristupni token — od
@BotFather
→ vaš bot → Serverless → CLI Access → Access token, odvojen token od vašeg
API tokena bota
— validira ga protiv platforme i sprema u .tgcloud/credentials.
loginje jedina naredba koja traži token i zahtijeva interaktivni terminal. U CI-ju umjesto toga postaviteTGCLOUD_TOKEN.
status
npx tgcloud status
Prikazuje, po datoteci, što se promijenilo između vaše radne mape i raspoređene kopije: modificirano, novo, izbrisano, nepromijenjeno. Radi offline, uspoređujući sa stanjem clouda pri vašem zadnjem push-u, pull-u ili fetch-u. Također upozorava na zalutale .js datoteke unutar tgcloud/ koje se neće rasporediti.
Za projekt s Mini Aplikacijom, status također pokazuje što se promijenilo u njezinoj build mapi od zadnjeg deploya.
diff
npx tgcloud diff
Poput status-a, ali prikazuje stvarne razlike liniju po liniju za promijenjene module. Također offline.
push
npx tgcloud push [files...]
Raspoređuje vaš projekt u cloud u jednoj atomskoj seriji.
Bez argumenata, raspoređuje cijeli projekt, a raspoređeno stanje se čini da točno zrcali vašu mapu — moduli koje ste lokalno izbrisali uklanjaju se u cloudu.
S argumentima datoteka ili mapa (npx tgcloud push tgcloud/handlers/message.js, npx tgcloud push handlers/), raspoređuje samo promjene u tim datotekama. Ciljani push nikada ne briše druge module. Meta se može napisati kao putanja datoteke (tgcloud/handlers/message.js), putanja unutar tgcloud/ (handlers/message.js) ili ime modula (handlers/message) — svi znače isti modul.
Jedna opcija je --force — za preskakanje provjere istodobnosti i prepisivanje onoga što je u cloudu. Koristite je samo kada ste sigurni (vidi
Održavanje sinkronizacije
).
Ako tgcloud.jsonc imenuje statičku mapu, puni push raspoređuje je u istoj seriji — učitavaju se samo datoteke koje su se promijenile — i ispisuje adresu stranice. Izgradite prvo, jer push nikada ne pokreće vašu izradu (npm run deploy radi oboje). npx tgcloud push dist/ raspoređuje samo statičke datoteke, a push koji imenuje samo module ostavlja ih netaknutima.
Nakon deploya, ako se schema.js promijenio i baza podataka nije sinkronizirana, push ispisuje sažetak promjena na čekanju i predlaže npx tgcloud migrate. Nikada ih ne primjenjuje sam.
migrate
npx tgcloud migrate
Primjenjuje vaše promjene sheme na bazu podataka. Računa razliku između schema.js i baze podataka uživo, a zatim vas vodi kroz nju korak po korak s tekućim [N/M] brojačem:
- Kratki sažetak svih promjena na čekanju.
- Sigurne promjene, primijenjene zajedno u jednom koraku uz vašu potvrdu.
- Upozorenja (brisanja, spore operacije), jedna po jedna, svaka potvrđena zasebno.
- Ručne promjene, prikazane s razlogom i predloženom radnjom, nisu automatski primijenjene.
- Nedokumentirani objekti (u bazi podataka, ali ne u vašoj shemi), prikazani za svijest.
Završava sažetkom: primijenjeno, preskočeno, čeka ručni popravak, nije u shemi.
Vidi Migracije za model.
Opcije: --dry-run (ispiši sve, ne primjenjuj ništa), --safe (auto-primijeni sigurne promjene, preskoči upozorenja), --yes (auto-primijeni sigurne promjene i svako upozorenje, preskoči ručne — koristite s oprezom), --local (diff protiv vaše lokalne schema.js umjesto one raspoređene). Bez zastavice, migrate zahtijeva terminal i javlja pogrešku u neinteraktivnom okruženju umjesto nagađanja.
run
npx tgcloud run <module> [args] [--ctx <json5>]
Izvršava rukovatelj ili krajnju točku na platformi bez raspoređivanja, koristeći vaše trenutne lokalne datoteke. Ovo je brza unutarnja petlja za testiranje logike.
<module>— obično ime (traži se podhandlers/iendpoints/) ili putanja poputhandlers/message. Obično ime koje postoji u oba direktorija mora biti napisano s direktorijem.[args]— payload koji se prosljeđuje rukovatelju, napisan u JSON5 tako da možete preskočiti navodnike oko ključeva. To je objekt vrste ažuriranja koji vaš rukovatelj prima (npr. poruka zahandlers/message) iliinputkrajnje točke, i mora biti objekt — ne niz ili običan string.--ctx <json5>— objekt konteksta modula (njegov drugi argument), također JSON5. Koristite ga za dostavu onoga što vaš kod čita sctx-a — sirovo ažuriranje za rukovatelj,--ctx '{ update: { update_id: 1 } }', ili init podatke za krajnju točku,--ctx '{ initData: { user: { id: 1 } } }'.runga ne verificira, budući da testirate svoju logiku, a ne provjere platforme.
npx tgcloud run handlers/message '{ chat: { id: 1 }, text: "hi" }'
Platforma pokreće modul protiv prostora modula sastavljenog iz vašeg lokalnog projekta (tako da se koristi i lokalno promijenjen lib/ kod) i vraća povratnu vrijednost, sve zapisano s console.* i proteklo vrijeme. Čitajte velike argumente iz datoteke s npx tgcloud run handlers/message "$(cat message.json5)".
fetch
npx tgcloud fetch
Provjerava što je trenutno raspoređeno, bez diranja vaših radnih datoteka. Nakon toga, status i diff uspoređuju s najnovijim stanjem clouda. Korisno za pogledati sukob prije odlučivanja kako ga razriješiti.
pull
npx tgcloud pull
Dovodi vaš lokalni projekt u liniju s cloudom ažuriranjem vaših radnih datoteka na raspoređeno stanje.
pull nikada ne piše build mapu vaše Mini Aplikacije.
reset
npx tgcloud reset
Odbacuje vaše lokalne promjene i vraća module iz zadnjeg poznatog stanja clouda. Koristite za bacanje eksperimenta. Build mapa Mini Aplikacije nije dirana.
webhook
npx tgcloud webhook
npx tgcloud webhook sync [--drop-pending]
Telegram isporučuje ažuriranja vašem botu putem webhooka, kojim platforma upravlja umjesto vas — nikada ga ne usmjeravate ručno. npx tgcloud webhook prikazuje njegovo trenutno stanje: URL, listu allowed_updates, koliko je ažuriranja na čekanju, zadnju pogrešku isporuke (ako postoji) i je li sinkroniziran s vašim raspoređenim rukovateljima.
“Sinkroniziran” znači da webhook pokazuje na platformu i da se njegova lista allowed_updates podudara s rukovateljima koje ste rasporedili — tako da Telegram isporučuje točno one vrste ažuriranja kojima rukujete, i ništa drugo. Raspoređivanje novog rukovatelja (ili uklanjanje jednog) može ostaviti webhook nesinkroniziranim dok se ne osvježi; npx tgcloud status i to označava.
npx tgcloud webhook sync to popravlja — ponovno usmjerava webhook na platformu i ponovno gradi allowed_updates iz vaših raspoređenih rukovatelja. Dodajte --drop-pending za odbacivanje ažuriranja koja je Telegram već stavio u red prije sinkronizacije (inače se isporučuju čim webhook ponovno postane zdrav).
completion
Bilješka: tab-completion radi samo kada je običan
tgcloudna vašemPATH-u — pa ga instalirajte globalno (npm install -g @tgcloud/cli) ili na drugi način stavite binarnu datoteku naPATH. Ne može se zakačiti nanpx.
tgcloud completion <bash|zsh|fish>
Ispisuje shell completion skriptu na stdout. Omogućite je jednom, a zatim <Tab> dovršava naredbe, zastavice, direktorije modula, vrste ažuriranja rukovatelja koje još nemate i vaše lokalne izvršne module.
# bash — treba paket bash-completion:
echo 'eval "$(tgcloud completion bash)"' >> ~/.bashrc
# zsh — osigurajte da se `autoload -U compinit && compinit` pokreće u vašem ~/.zshrc:
echo 'eval "$(tgcloud completion zsh)"' >> ~/.zshrc
# fish:
tgcloud completion fish > ~/.config/fish/completions/tgcloud.fish
Nakon toga ponovno pokrenite shell (ili ponovno učitajte datoteku). Pokretanje tgcloud completion bez shella ponovno ispisuje ove upute.
upgrade
npx tgcloud upgrade [--dry-run]
Jednokratna migracija za projekte kreirane prije CLI 0.2.0, koji su držali schema.js, lib/ i handlers/ u korijenu projekta. Premješta ih u tgcloud/. Vaša veza s botom se zadržava i ništa ne treba ponovno raspoređivati. --dry-run ispisuje planirane poteze bez diranja ičega. Ostale naredbe ne rade na starom layoutu dok ga ne nadogradite.
Održavanje sinkronizacije
Svaki projekt ima monotonu povećavajuću reviziju u cloudu, koja se povećava pri svakom deployu. CLI pamti reviziju s kojom se zadnji put sinkronizirao i šalje je pri svakom push-u. Ako je cloud napredovao — jer je drugi stroj ili član tima napravio deploy — push se odbija umjesto da tiho prepiše njihov rad, a CLI nudi tri načina naprijed:
npx tgcloud fetch # vidi što se promijenilo u cloudu, zatim ponovno provjeri
npx tgcloud pull # dovedi najnovije u svoje radne datoteke
npx tgcloud push --force # prepiši stanje clouda (opasno)
Ova provjera optimistične istodobnosti je razlog zašto bot možete dijeliti unutar tima bez procesa deploya u korak. Naredbe završavaju s ne-nula kodom pri neuspjehu — odbijeni deploy, neuspjela migracija, pogreška autentifikacije, modul koji je bacio pogrešku tijekom run-a — tako da se čisto slažu u skriptama i CI kanalima.