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 push staje kako biste prvo mogli pull-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.js istom 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/ i handlers/ u korijenu projekta. Pokrenite npx tgcloud upgrade jednom kako biste ih premjestili u tgcloud/.

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.ai poddomeni, 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žuje index.html za putanje koje ne postoje i nemaju ekstenziju datoteke, tako da klijentski usmjerivač radi pri ponovnom učitavanju. Putanje s ekstenzijom i dalje dobivaju 404.
  • 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 /*). null vrijednost uklanja zaglavlje.
  • redirects — pravila preusmjeravanja, gdje prvo podudaranje pobjeđuje. Postojeća datoteka uvijek pobjeđuje pravilo. status je 301 osim 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() i sql oznaka ponašaju se onako kako očekujete, pa je čitanje i pisanje podataka poznati API koji već koristite. Samo ga uvozite iz sdk/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?):

  • name je SQL naziv tablice;
  • columns je mapa JS svojstvo → definicija stupca;
  • extras je opcionalni povratni poziv (t) => ({ … }) gdje t izlaže stupce (t.created je 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 mode kontrolira 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() koristi Uint8Array. mode kontrolira kodiranje, a ne pohranu, tako da blob(..., { mode: 'json' }) pohranjuje JSON kao BLOB, dok json() 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() na json() 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?) (ili count() 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 migrate za 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 getFile preuzimanja 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 s res.body.
  • Ukupni odgovor je ograničen na 30 MB. Ta granica pokriva cijeli odgovor — streamanje s res.body omoguć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:

  1. TGCLOUD_TOKEN varijabla okruženja — za CI; nikada se ne piše na disk.
  2. .tgcloud/credentials — piše ga npx tgcloud login .
  3. 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 add nikada 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 — vidi completion .

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.

login je jedina naredba koja traži token i zahtijeva interaktivni terminal. U CI-ju umjesto toga postavite TGCLOUD_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 pod handlers/ i endpoints/) ili putanja poput handlers/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 za handlers/message) ili input krajnje 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 s ctx-a — sirovo ažuriranje za rukovatelj, --ctx '{ update: { update_id: 1 } }', ili init podatke za krajnju točku, --ctx '{ initData: { user: { id: 1 } } }'. run ga 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 tgcloud na vašem PATH-u — pa ga instalirajte globalno (npm install -g @tgcloud/cli) ili na drugi način stavite binarnu datoteku na PATH. Ne može se zakačiti na npx.

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.