Telegram Serverless hukuruhusu kuendesha msimbo wa backend kwa ajili ya boti yako na Mini App moja kwa moja kwenye miundombinu ya Telegram — hakuna seva za kuweka, hakuna kontena za kuhakikisha zinaendelea kufanya kazi, na hakuna haja ya kufikiria kuhusu kuongeza uwezo (scaling). Unaandika moduli rahisi za JavaScript, unazipeleka kwa amri moja, na Telegram inaziendesha katika V8 sandbox ya haraka na iliyotengwa ambayo inakaa karibu na Bot API na hifadhidata iliyojengewa ndani.

Ikiwa umewahi kuunganisha boti kwenye VPS, cloud function, au hosting panel ili tu kujibu /start, hii ndiyo hatua ambayo huhitaji tena kuifanya.

Kwa nini serverless

Boti ya Telegram ni, katika msingi wake, programu inayokabiliana na masasisho (updates). Kijadi, ilibidi uweke programu hiyo mahali fulani ambapo inafanya kazi kila wakati, inayoweza kufikika, na salama — na kisha uweke hivyo kila wakati. Telegram Serverless huondoa safu hiyo kabisa:

  • Hakuna miundombinu. Hakuna mashine ya kukodisha, kufanyia viraka, au kufuatilia. Msimbo wako unafanya kazi kulingana na mahitaji na hujiendesha yenyewe kwa kuongeza uwezo kulingana na boti yako.
  • Vitambulisho vimejumuishwa. Telegram Bot API, hifadhidata inayotumia SQLite, na HTTP ya kutoka nje zinapatikana kwa kila moduli bila haja ya ziada — hakuna cha kusakinisha, hakuna vitambulisho vya kuunganisha.
  • Utekelezaji wa haraka na uliotengwa. Kila ombi linafanya kazi katika V8 isolate nyepesi, karibu na mifumo ya Telegram yenyewe, kwa hivyo wito kwa Bot API na hifadhidata yako ni wa haraka na wa kuaminika.
  • Utiririshaji halisi wa kazi wa msanidi programu. Mradi unaishi kwenye folda katika mashine yako chini ya udhibiti wa matoleo (version control). Unahariri faili, unaona hasa nini kimebadilika, unapeleka msimbo kikamilifu, na unasasisha muundo wa hifadhidata yako kwa mabadiliko yaliyokaguliwa — jinsi unavyofanya kazi na kila kitu kingine.

Mfano wa kimawazo: Jinsi mradi unavyofanya kazi

Unafanya kazi katika maeneo matatu, na yanalingana kikamilifu:

Mahali Nini kinaishi hapo
Folda ya mradi wako Moduli za JavaScript — schema, msimbo wa pamoja, vishikilia masasisho, endpoints za Mini App — na, ikiwa unayo, front‑end ya Mini App
Cloud Nakala iliyopelekwa ya moduli hizo, hifadhidata ya boti yako, na faili za Mini App
tgcloud CLI Daraja — inakuonyesha tofauti na kuzisawazisha

Huwezi ku-SSH kwenye kitu chochote. Unahariri faili ndani ya kompyuta yako, unaendesha npx tgcloud push, na jukwaa linashughulikia yaliyosalia. Trafiki ya boti yako inashughulikiwa na moduli zilizopelekwa; hifadhidata yako inadumu kati ya maombi.

Mradi una aina nne tu za msimbo, yote ndani ya folda moja ya tgcloud/:

tgcloud/
├─ endpoints/  # vitendaji vinavyoitwa na Mini App yako
├─ handlers/   # vitendaji vinavyoitikia aina za masasisho ya Telegram
├─ lib/        # msimbo wa pamoja unaoingiza kutoka popote
└─ schema.js   # jedwali za hifadhidata yako

Wakati sasisho linapowasili — ujumbe, kubonyeza kitufe, inline query — Telegram inalipeleka kwa kishikilia kinacholingana (handlers/message.js, handlers/callback_query.js, …) na kuita export yake ya kawaida. Kitendaji hicho kinawasiliana na Bot API na hifadhidata kupitia SDK, na kisha kinamaliza. Huo ndio mzunguko mzima. Sasisho lisilo na kishikilia kinacholingana linapuuzwa tu, kwa hivyo unaongeza tu vishikilia unavyohitaji.

Mini App ni ya hiari. Ikiwa boti yako inayo moja, front‑end yake inaishi kwenye mradi huo huo, karibu na tgcloud/. Serverless inahifadhi matokeo ya build yake na kuendesha endpoints/ inayoziita. Tazama Mini App front‑end .

Onyesho la haraka

Hii hapa ni boti kamili ya onyesho inayofanya kazi. Inajibu kila ujumbe na kukumbuka ni ujumbe mingapi imeona kutoka kwa kila chat.

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;

  // Ingiza kaunta, au iongeze ikiwa chat hii tayari inayo — na urudishe 
  // mstari unaotokana katika taarifa hiyo hiyo kupitia .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: `Hujambo! Nimeona ujumbe ${row.seen} kutoka kwako.`,
  });
}

Ipeleke (Deploy):

npx tgcloud push       # pakia moduli
npx tgcloud migrate    # tengeneza jedwali la `counters`

Hiyo ni boti inayofanya kazi na hali endelevu na bila seva. Kila kitu ndani yake — api, db, table() DSL — kimeelezwa katika sehemu hapa chini.

Serverless ni backend ya jumla kwa boti za Telegram na Mini Apps, si kiolezo cha aina moja ya programu. Inafaa kwa:

  • Boti za AI za Mazungumzo zinazohitaji kuhifadhi hali ya kila mtumiaji katika hifadhidata.
  • Backend za Mini App zinazohifadhi data za watumiaji na kuhudumia maudhui yanayobadilika.
  • Michezo na Zana — ikijumuisha ubao wa wanaoongoza, maswali na zaidi.
  • Uendeshaji wa Kiotomatiki na Integrations zinazoita API za HTTP za watu wengine na kusukuma matokeo kwenye chats.

Kuanza

Mwongozo huu unakupeleka kutoka kwenye folda tupu hadi kwenye boti inayofanya kazi inayojibu ujumbe na kuhifadhi data. Inadhania kuwa umesakinisha Node.js 18 au mpya zaidi na una boti iliyosajiliwa na @BotFather . Kufikia mwisho utakuwa umetumia kila amri unayohitaji kila siku: push, migrate, run, na status.

Kabla ya kitu kingine chochote, washa Serverless. Katika @BotFather , fungua boti yako → Serverless na uwashe. Hiyo inawasha kipengele hicho kwa boti hii na kufungua token yake ya ufikiaji ya CLI, vishikilia, maktaba, na hifadhidata.

1. Unda mradi

Njia ya haraka zaidi ya kuanza ni mtayarishaji wa mradi, ambayo inaunda kiolezo cha mradi na kusakinisha CLI ndani yake:

npm create @tgcloud/bot example_bot
cd example_bot

Hoja hiyo ni folda lengwa: pitisha . ili kuunda kwenye folda ya sasa, au njia yoyote. Inafanya kazi kwenye folda iliyopo pia na kamwe haifuti faili ulizokuwa nazo.

Hii inakupa mradi ulio tayari kuhaririwa:

example_bot/
├─ tgcloud/             # kila kitu kinachoendeshwa na jukwaa
│  ├─ handlers/
│  │  └─ message.js     # kishikilia ujumbe cha kuanzia (hurudisha maandishi)
│  ├─ lib/              # moduli zako za pamoja zinaenda hapa (tupu kuanza)
│  └─ schema.js         # jedwali za hifadhidata yako
├─ docs/
│  └─ tgcloud-sdk.md    # marejeleo ya SDK (kwa ajili yako na zana zako za AI)
├─ AGENTS.md            # mwongozo kwa wasaidizi wa uandishi wa msimbo wa AI
├─ package.json
└─ tgcloud.jsonc        # usanidi wa mradi

Faili zilizoundwa zinaelezea wenyewe — kila moja ina mifano ya maoni ya kile unachoweza kufanya baadaye.

CLI inasakinisha kwenye mradi kama dev-dependency ya ndani, kwa hivyo unaiendesha na npx tgcloud <command> (npx inatafuta nakala katika node_modules ya mradi wako), au kupitia njia za mkato za npm run ambazo kiolezo kinaongeza kwenye package.json (npm run deploy, npm run status). Kwa chaguo-msingi hakuna tgcloud ya kimataifa kwenye PATH yako.

Unaweza pia kuisakinisha kimataifa — npm install -g @tgcloud/cli — ikiwa ungependelea kuandika tgcloud tu kutoka popote. Hiyo ni muhimu kwa kuendesha tgcloud init katika folda yoyote tupu, na ndivyo shell tab-completion inavyohitaji. Kwa namna yoyote unapata mradi uleule.

Unajenga Mini App? Anza kutoka front‑end yake badala yake. Iunde na zana yako ya kawaida, kisha uendeshe mtayarishaji wa mradi katika folda hiyo. Tazama Mini App front‑end .

2. Unganisha boti yako

Kila mradi umeunganishwa na boti moja. Ziunganishe na login, ambayo inauliza token yako ya ufikiaji ya CLI ( @BotFather → boti yako → Serverless → CLI Access → Token ya ufikiaji — token tofauti na token ya API ya boti yako ) na kuihifadhi ndani:

npx tgcloud login

Token ina umbo la app<id>:<secret>. CLI inaiweka katika .tgcloud/, ambayo imepuuzwa na git, na kamwe haichapishi sehemu ya siri. Login ni mara pekee unayoulizwa hiyo — tazama Authentication kwa jinsi token zinavyotatuliwa katika CI.

3. Tazama kote

Amri mbili zinakuambia mahali mambo yalipo wakati wowote, zote zikiwa nje ya mtandao kikamilifu:

npx tgcloud status     # nini kimebadilika ndani ya kompyuta dhidi ya nakala iliyopelekwa
npx tgcloud diff       # mabadiliko ya mstari kwa mstari

Mara baada ya init kila kitu ni kipya na hakuna kitu kilichopelekwa bado. status inaonyesha faili za kuanzia zikisubiri kupandishwa.

4. Ipeleke (Deploy)

Tuma moduli zako kwenye cloud:

npx tgcloud push

push hupakia kila moduli iliyobadilika katika kundi moja na kusasisha rekodi yako ya ndani ya kile ambacho cloud inacho sasa. Boti yako iko hai: ifungue kwenye Telegram na uitumie ujumbe — kishikilia cha kuanzia kinakurudishia ujumbe huo.

Kupeleka msimbo kamwe hakugusi hifadhidata yako. Kusukuma msimbo na kubadilisha muundo wa hifadhidata yako ni hatua tofauti kwa makusudi, kwa hivyo kupeleka msimbo hakutakushangaza na mabadiliko ya data. Hiyo ndiyo hatua inayofuata.

5. Ongeza jedwali la hifadhidata

Hebu tuifanye boti ikumbuke kitu fulani. Fungua tgcloud/schema.js na utangaze jedwali:

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

Ipeleke schema, kisha uitumie kwenye hifadhidata:

npx tgcloud push       # hupakia schema mpya
npx tgcloud migrate    # hutengeneza jedwali la `messages`

push inaripoti kuwa schema haijasawazishwa na inakuonyesha mabadiliko yanayosubiri, lakini haitumii chochote. migrate inakuongoza kupitia mabadiliko na, baada ya uthibitisho wako, inatengeneza jedwali. Mfano huu wa hatua mbili — na nini kinatokea kwa mabadiliko ya hatari zaidi kama kufuta — umeelezwa katika Migrations .

6. Hifadhi na soma data

Sasa tumia jedwali kutoka kwa kishikilia chako. Hariri tgcloud/handlers/message.js:

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

export default async function (message) {
  // Hifadhi ujumbe huu.
  await db.insert(messages)
    .values({ chatId: message.chat.id, text: message.text })
    .run();

  // Hesabu ni mingapi tumehifadhi kwa chat hii.
  const count = await db.$count(messages, eq(messages.chatId, message.chat.id));

  await api.sendMessage({
    chat_id: message.chat.id,
    text: `Imehifadhiwa. Huo ni ujumbe ${count} kutoka kwenye chat hii hadi sasa.`,
  });
}

Ipeleke kishikilia kilichosasishwa na npx tgcloud push, kisha itumie boti yako jumbe chache na utazame hesabu ikipanda. Hifadhidata inabaki kati ya maombi — hiyo ndiyo kumbukumbu ya boti yako.

7. Jaribu bila kupeleka

Huna haja ya kupeleka ili kujaribu mabadiliko. npx tgcloud run inatekeleza kishikilia kwenye jukwaa ikitumia faili zako za ndani, bila kuzichapisha:

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

Hoja hiyo ni payload ambayo kishikilia chako hupokea — kwa handlers/message, Ujumbe — iliyoandikwa katika JSON5 (hivyo unaweza kuruka kuweka alama za kunukuu kwenye keys). Amri hiyo inachapisha chochote ambacho kishikilia kiliandika na console.*, thamani ya kurudishwa, na muda uliotumika. Hii ndiyo njia ya haraka zaidi ya kuboresha mantiki — hakuna kupeleka, hakuna kusubiri ujumbe halisi.

8. Kaa katika usawaziko

Unapofanya kazi, amri chache huweka mradi wako wa ndani na cloud katika usawaziko: npx tgcloud status inaonyesha nini kimebadilika, npx tgcloud push inapeleka, npx tgcloud pull inaleta mradi wako wa ndani sambamba na cloud, npx tgcloud fetch inakagua nini kimepelekwa bila kugusa faili zako, na npx tgcloud reset inafuta mabadiliko ya ndani.

Ikiwa watu wawili (au mashine mbili) watapeleka kwa boti moja, jukwaa linagundua mgogoro na push inasimama ili kukuacha u-pull kwanza — huwezi kamwe kubadilisha kazi ya mtu mwingine kimya kimya. Tazama Staying in sync .

Kujenga na AI

Unapendelea kujenga na msaidizi wa AI — au ni AI pekee ndiye anayeshughulika na msimbo kwenye timu yako? Bado unaweza kusafirisha boti. Tumechukua hatua ya kwanza kumfanya wakala wa AI ajisikie yuko nyumbani kwenye mradi: kila mradi mpya unaundwa na AGENTS.md na marejeleo ya docs/tgcloud-sdk.md ambayo zana za uandishi wa msimbo wa wakala husoma kiotomatiki.

Pamoja na muda mdogo wa utekelezaji (runtime) wa kujitegemea — SDK moja, hakuna vifurushi vya npm vya kusumbua — hiyo inampa msaidizi mwanzo mzuri kwenye mikataba ambayo uundaji wa msimbo wa kawaida huwa unakosa hapa: imports za jamaa na .js, hakuna funguo za kigeni (foreign keys), kila wito wa db ni async, kishikilia kimoja kwa kila aina ya sasisho, na mtiririko wa push/migrate wa hatua mbili.

Ijaribu:

npm create @tgcloud/bot my-bot
cd my-bot
opencode            # au Claude Code, Cursor, … — wakala yeyote anayesoma AGENTS.md

Kisha uulize tu, kwa lugha rahisi:

Andika boti inayokumbuka orodha ya mambo ya kufanya ya kila mtu — ongeza kipengee wanapotuma maandishi, na uonyeshe orodha nzima wanapotuma /list.

Msaidizi anahariri schema.js na vishikilia vyako kwa ajili yako; unakagua, unajaribu mabadiliko papo hapo na npx tgcloud run, kisha unaenda mubashara na npx tgcloud push na npx tgcloud migrate. AGENTS.md ni sehemu ya mradi wako — ihariri boti inapokua ili mwongozo ubaki sahihi.

On the go na BotFather

Umebaki na simu yako tu? Mradi mzima unaishi katika @BotFather pia — fungua boti yako → Serverless na unapata kila kitu ambacho CLI inasimamia, kwenye skrini ya kugusa:

  • Handlers — tengeneza, hariri, na jaribu vishikilia vya masasisho; BotFather huweka webhook katika usawaziko na vishikilia ulivyo navyo (sawa na In sync / Out of sync ambayo CLI inaripoti).
  • Endpoints — vitendaji vinavyoitwa na Mini App yako.
  • Library — moduli zako za pamoja za lib/.
  • Database — hariri schema.js katika syntax ileile ya Drizzle, kagua mabadiliko yanayosubiri, na uyaapply; Save inapeleka (deploy).
  • Mini App Files — front‑end ambayo watumiaji wako wanaifungua kwenye Telegram.
  • CLI Access — chukua token ya ufikiaji ya CLI hapa utakapokuwa umerejea kwenye kibodi.

Ni mradi mmoja uleule wa cloud, kwa hivyo unaweza kuanza kishikilia kwenye simu yako na kuki-pull na npx tgcloud pull kwenye laptop yako baadaye — hakuna kitu kinachofungwa na mteja mmoja. Kuendesha kishikilia kunaonyesha hata matokeo yake ya Console moja kwa moja kwenye chat, kama vile npx tgcloud run .

Miradi na moduli

Mradi wa serverless ni folda ya kawaida chini ya udhibiti wa matoleo. Kila kitu kinachoendeshwa na jukwaa kinakaa kwenye folda moja, tgcloud/; mradi wote uliobaki ni wako — front‑end ya mini-app, usanidi, docs. Hakuna hatua ya build kwa boti, hakuna node_modules wakati wa utekelezaji, na hakuna hatua ya kuingia ya seva.

Anatomia ya mradi

example_bot/
├─ tgcloud/             # sehemu iliyopelekwa — hakuna nyingine
│  ├─ handlers/         # vishikilia masasisho — bapa, ngazi moja tu
│  │  ├─ message.js
│  │  └─ callback_query.js
│  ├─ endpoints/        # vitendaji vinavyoitwa na Mini App yako — bapa, ngazi moja tu
│  │  └─ getProfile.js
│  ├─ lib/              # moduli za pamoja; ndogo-folda zinaruhusiwa
│  │  ├─ reply.js
│  │  └─ internal/util.js
│  └─ schema.js         # schema ya hifadhidata — faili moja
└─ .tgcloud/            # hali ya CLI, imepuuzwa na git

Ni faili za .js ndani ya tgcloud/ — schema.js, lib/, handlers/, endpoints/ — ndizo zinazopelekwa. Kila kitu kingine — Markdown, faili za usanidi, folda ya .tgcloud/, chochote nje ya tgcloud/ — kinabaki kwenye mashine yako. Majina ya moduli ni ya jamaa na tgcloud/. Faili tgcloud/handlers/message.js ni moduli handlers/message, na hilo ndilo jina ambalo npx tgcloud run inatumia.

Unaboresha kutoka mradi wa zamani? Miradi iliyoundwa kabla ya CLI 0.2.0 iliweka schema.js, lib/ na handlers/ kwenye mzizi wa mradi. Endesha npx tgcloud upgrade mara moja ili kuzihamishia kwenye tgcloud/.

schema.js — hifadhidata yako. Inatangaza jedwali kama exports zilizopewa jina ikitumia schema DSL na kuishi katika tgcloud/ kama faili moja. Inapelekwa kama moduli nyingine yoyote, lakini kuipeleka hakubadilishi hifadhidata — mabadiliko ya schema yanatumika kando na npx tgcloud migrate. Tazama The database .

lib/ — msimbo wa pamoja, chochote unachotaka kutumia tena katika vishikilia: wasaidizi wa kawaida, safu za ufikiaji wa hifadhidata, uumbizaji, integrations na huduma za nje. lib/ ndiyo folda pekee inayoweza kuwa na ndogo-folda (lib/internal/util.js, lib/payments/stripe.js), ili uweze kupanga codebase kubwa jinsi unavyopenda. Moduli katika lib/ hazijawahi kuitwa moja kwa moja na jukwaa; zipo ili ziingizwe na vishikilia na kwa kila mmoja.

handlers/ — sehemu za kuingilia za boti yako. Kila faili inalingana na aina moja ya sasisho la Telegram, na jukwaa huelekeza kila sasisho linaloingia kwa kishikilia kinacholingana:

Faili Hushughulikia
handlers/message.js Ujumbe mpya unaoingia
handlers/inline_query.js Maswali mapya ya inline
handlers/callback_query.js Maswali mapya ya callback
… aina nyingine yoyote ya sasisho la Bot API

handlers/ ni bapa — hakuna ndogo-folda. export default ya kishikilia ndicho kitendaji ambacho jukwaa huita (tazama Handlers ).

Aina ya sasisho inashughulikiwa tu ikiwa faili ya kishikilia chake ipo na si tupu. Ikiwa hakuna handlers/<type>.js — au faili ni tupu — masasisho ya aina hiyo yanapuuzwa, na jukwaa halindeshi chochote kwa ajili yao. Kwa hivyo weka tu vishikilia unavyohitaji kweli: kila kimoja unachoongeza ni aina nyingine ya sasisho ambayo boti yako huamka kuishughulikia, na kuacha vingine inamaanisha Telegram haianzishi msimbo wako kwa masasisho ambayo ungeyapuuza tu.

Ili kuunda kishikilia kipya, endesha npx tgcloud add handlers/<type> .

endpoints/ — upande wa seva wa Mini App yako. Kila faili ni kitendaji ambacho Mini App huita kwa jina, kwa hivyo endpoints/getProfile.js inajibu POST /api/getProfile. Jukwaa hukagua kuwa wito huo unatoka kwa mtumiaji wa Telegram ndani ya Mini App yako kabla msimbo wako haujaanza, na kukupa mtumiaji huyo. endpoints/ ni bapa, kama handlers/. Tazama Endpoints .

.tgcloud/ — hali ya mashine ya ndani inayodhibitiwa kikamilifu na CLI, ikijumuisha vitambulisho vyako vilivyohifadhiwa. Imepuuzwa na git, na hupaswi kamwe kusoma kutoka kwake au kuandika kwake kwa mkono — tumia amri za CLI badala yake.

Mfumo wa moduli

Wakati wa utekelezaji, moduli inaweza kuona vitu viwili tu: SDK ya jukwaa na moduli nyingine katika mradi wako. Hakuna vifurushi vya npm, hakuna mfumo wa faili, na hakuna mtandao isipokuwa kupitia fetch ya SDK.

Ingiza moduli zako mwenyewe jinsi ungefanya katika mradi wowote wa JavaScript — kwa njia ya jamaa, na kiendelezi cha .js — na SDK kwa jina lake:

// tgcloud/handlers/message.js
import { users } from '../schema.js';              // moduli ya schema
import { addItem } from '../lib/cart.js';          // moduli ya lib
import { format } from '../lib/internal/fmt.js';  // moduli ya lib iliyowekwa ndani
import { db, api, fetch } from 'sdk';              // SDK ya jukwaa
// tgcloud/lib/cart.js
import { format } from './internal/fmt.js';        // jirani
import { users } from '../schema.js';

Import ya jamaa lazima iishie katika .js na haiwezi kutoka nje ya tgcloud/:

import { users } from '../schema';       // ongeza .js
import config from '../../config.js';    // nje ya tgcloud/
import { z } from 'zod';                 // hakuna vifurushi vya npm

Kila moduli pia ina jina — njia yake ndani ya tgcloud/ bila kiendelezi, kama schema, lib/cart au handlers/message. Jina hilo ndilo ambalo npx tgcloud run na matokeo ya CLI hutumia, na moduli inaweza kuingizwa kwa jina hilo pia (from 'lib/cart', hakuna kiendelezi). Pendelea imports za jamaa katika msimbo wako. Ndivyo mhariri wako anavyoelewa — jump‑to‑definition, autocomplete, rename.

Kinachopatikana wakati wa utekelezaji ni vitu viwili tu: sdk na moduli zake ndogo (sdk/db, sdk/api, sdk/fetch) — uso mzima wa jukwaa, tazama The SDK — na moduli zako mwenyewe katika tgcloud/. Hiyo ndiyo orodha kamili. Ikiwa msimbo wako una-import kitu kingine, haitafanya kazi. Kizuizi hiki ndicho kinachofanya moduli ziwe za haraka kupakia na salama kuendesha.

Handlers

Kishikilia ni moduli katika handlers/ ambayo default export yake inaitwa na jukwaa sasisho linapowasili.

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

export default async function (message) {
  await api.sendMessage({
    chat_id: message.chat.id,
    text: `Ulisema: ${message.text ?? '(hakuna maandishi)'}`,
  });
}

Kishikilia hupokea payload ya sasisho kama hoja yake — jukwaa hufungua Sasisho la Telegram kwa ajili yako. handlers/message.js hupata Ujumbe (yaani update.message); handlers/callback_query.js hupata CallbackQuery ; na kadhalika. Hoja ya pili ya kishikilia ni kitu cha muktadha wa kila ombi, ctx. Inachukua Update mbichi kama ctx.update — itumie unapohitaji kitu nje ya payload, kama update_id.

Kishikilia kinaweza kuwa async (kwa kawaida ndivyo kilivyo) na kinaweza kurudisha thamani. Hufikia Bot API, hifadhidata, na HTTP ya nje kupitia SDK.

Huna haja ya sasisho halisi ili kujaribu kishikilia. npx tgcloud run inakiendesha kwenye jukwaa na payload unayotoa na msimbo wako wa sasa wa ndani:

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

Hoja hiyo ni payload — kitu kilekile ambacho kishikilia chako hupokea — katika JSON5. Ili kutoa ctx ya kishikilia (hoja yake ya pili), ongeza --ctx, mfano --ctx '{ update: { update_id: 1 } }'. Hii inafanya kazi dhidi ya faili zako za ndani, kwa hivyo unaweza kujaribu mabadiliko kabla ya kuyapeleka. Tazama run .

Endpoints

Endpoint ni moduli katika endpoints/ ambayo Mini App yako inaita — mahali pa chochote ambacho front‑end haiwezi kufanya yenyewe, kama kusoma na kuandika hifadhidata, kuita Bot API, au kuzungumza na huduma za nje na siri zako.

// 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('Profile not found', { userId: user.id });
  return profile;
}

Hoja ya kwanza, input, ni kitu cha JSON ambacho Mini App ilituma. Ya pili, ctx, inachukua ctx.initData — init data ya Mini App (user, chat, start_param, auth_date, …), ambayo imethibitishwa na jukwaa. Ina umbo sawa na Telegram.WebApp.initDataUnsafe kwa mteja, lakini hapa unaweza kuiamini. Wito usio na init data sahihi unakataliwa na 401 kabla msimbo wako haujaanza, kwa hivyo ctx.initData.user ndiye mtu aliyefungua Mini App yako. Endpoints zinaweza kuitwa kwa njia hii tu — kutoka kwa Mini App ya boti yako, na mtumiaji wa Telegram — kwa hivyo hakuna API ya umma ya kulinda na hakuna login ya kujenga.

Chochote ambacho endpoint inarudisha kinatumwa tena kama JSON. Ili kukataa wito, throw new EndpointError(description, parameters?). Mini App inapata maelezo na kitu cha parameters kama vilivyo. Ikiwa unahitaji kuchanganua aina ya kosa katika front‑end, weka kodi kwenye parameters — { code: 'OUT_OF_STOCK' }. Ubaguzi wowote mwingine unaripotiwa kama kosa la jumla la ndani, na ujumbe halisi unaenda kwenye kumbukumbu zako tu, kwa hivyo mambo ya ndani hayavuji kwa mteja.

Kuita endpoint kutoka kwa Mini App

Mini App inaita endpoint kwa jina na Telegram.WebApp.Serverless.call kutoka kwa Mini Apps SDK rasmi, telegram-web-app.js — hati ambayo Mini App yako tayari inajumuisha. Inatuma init data kwa ajili yako na kurudisha kosa au matokeo ya endpoint:

Telegram.WebApp.Serverless.call('getProfile', { lang: 'en' }, function (err, profile) {
  if (err) {
    // err.message — maelezo; err.parameters — kile EndpointError ilichobeba
    if (err.type === 'ENDPOINT_ERROR') showMessage(err.message);
    return;
  }
  render(profile);
});

Input ni ya hiari na lazima iwe kitu rahisi. Bila hiyo wito ni Serverless.call('getProfile', callback).

err.type ni 'ENDPOINT_ERROR' wakati endpoint yako ilipotupa EndpointError. Thamani nyingine yoyote inamaanisha jukwaa lilikataliwa wito au endpoint ilishindwa bila kutarajiwa, kwa mfano 'UNAUTHORIZED' wakati init data haipo au si sahihi.

Majina ya Endpoint ni kama vitambulishi vya JavaScript — herufi, tarakimu na _, zisizoanza na tarakimu (getProfile, place_order). Iunde na npx tgcloud add endpoints/<name> , na ujaribu bila Mini App na run , ukitoa init data mwenyewe:

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

Nini kinachopelekwa

Wakati unapo-npx tgcloud push, CLI inakusanya kila faili ya .js chini ya schema.js, lib/, handlers/, na endpoints/, na kutuma seti hiyo kamili kama nafasi ya moduli ya mradi wako — pamoja na folda ya build ya Mini App, ikiwa tgcloud.jsonc inataja moja (tazama chini). Chochote kilichopo kwenye cloud lakini hakipo kwenye mradi wako kinafutwa, kwa hivyo hali iliyopelekwa daima huakisi folda yako — ikijumuisha ufutaji. Hakuna kitu nje ya tgcloud/ kinachopelekwa kama msimbo — mradi wote uliobaki ni wako. Ndani ya tgcloud/, faili ya .js ambayo haipo katika moja ya maeneo hayo (tgcloud/util.js, tgcloud/helpers/x.js) inawekwa alama, ili isisafirishwe bila kujulikana. Markdown, dotfiles, na .tgcloud/ hazijawahi kupelekwa.

Mini App front-end

Mzizi wa mradi ni wako, na ndio mahali pa asili kwa front‑end ya Mini App yako — iliyojengwa na React, Vue, Svelte au HTML rahisi, na Vite au bundler nyingine yoyote. Serverless inahifadhi matokeo ya build yake karibu na boti yako. push moja inapeleka msimbo na faili tuli pamoja, na programu inahudumiwa katika https://app<app_id>.tgcloud.ai/ (push inachapisha anwani kamili — itumie kama URL ya Mini App yako katika @BotFather ).

Kuripoti unyanyasaji. Mini Apps kwenye tgcloud.ai zinachapishwa na wasanidi huru. Ili kuripoti maudhui haramu au unyanyasaji kwenye subdomain ya app<app_id>.tgcloud.ai, tuma barua pepe kwa [email protected] .

Ili kuanza, tengeneza front‑end kwanza, na zana yoyote unayopenda, kisha ongeza Serverless juu yake:

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

Mtayarishaji wa mradi kamwe haifuti faili zako. Inaongeza folda ya tgcloud/ karibu na front‑end, inatambua folda ya build, na inaongeza hati ya npm run deploy ambayo inajenga programu na kuipeleka pamoja na boti.

Kinachohudumiwa kinawekwa katika tgcloud.jsonc, faili ya usanidi ya mradi kwenye mzizi wa mradi:

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

"source" pekee ndiyo inayohitajika. Ni folda ya build, ya jamaa na mzizi wa mradi. Funguo zingine za "static" zinasanidi jinsi inavyohudumiwa (chini). init inatengeneza tgcloud.jsonc kwa ajili yako. Inapotambua front‑end, pia inajaza "source":

Front‑end init inaweka "source" kuwa
Vite — React, Vue, Svelte, Solid, … dist
Next.js, Nuxt, SvelteKit, React Router, TanStack Start, Angular hakuna — sanidi static export kwanza, kisha uweke "source" kwenye folda yake mwenyewe

Kwa usanidi mwingine wowote, weka "source" kwenye folda yako ya build mwenyewe.

Mstari wa $schema unampa mhariri wako autocomplete na uthibitishaji kwa kila ufunguo. Faili ni JSON na maoni na koma za mwisho zinaruhusiwa. Ikiwa zana nyingine yako inahitaji kuisoma kama JSON rahisi, iite tgcloud.json badala yake — basi lazima iwe JSON kali.

Build kabla ya kusukuma. push hupakia folda kama ilivyo kwenye diski — isipokuwa faili na folda ambazo jina lake linaanza na nukta, ambazo hurukwa — na kamwe haifanyi build yako, kwa hivyo npx tgcloud push rahisi baada ya kuhariri front‑end ingepeleka build ya awali. Tumia npm run deploy (inaendesha hati yako ya build, kisha tgcloud push), au jenga mwenyewe kwanza.

Chaguzi za kuhudumia:

  • spa — hudumia index.html kwa njia ambazo hazipo na hazina kiendelezi cha faili, kwa hivyo kipanga njia cha upande wa mteja hufanya kazi kwenye reload. Njia zilizo na kiendelezi bado hupata 404.
  • immutable — ruwaza za faili zenye maelezo ya maudhui (Vite’s /assets/*) ambazo vivinjari vinaweza kuhifadhi kwa mwaka. Itumie tu kwa faili zenye maelezo ya maudhui. Faili nyingine yoyote inayolingana hapa ingekaa bila kusasishwa kwenye vivinjari kwa mwaka, na hakuna njia ya kuikumbuka.
  • headers — vichwa vya majibu kwa njia ambazo sheria inalingana (njia kamili, au kiambishi kinachoishia katika /*). Thamani ya null huondoa kichwa.
  • redirects — sheria za kuelekeza, ambapo mechi ya kwanza inashinda. Faili iliyopo daima hushinda sheria. status ni 301 isipokuwa ukiweka nyingine.
  • "static": "dist" — njia ya mkato ya { "source": "dist" } unapohitaji kitu kingine.
  • "static": false — ondoa tovuti iliyopelekwa kwenye push inayofuata. Kufuta ufunguo badala yake husitisha mradi huu kusimamia faili tuli, na chochote kilichopelekwa kinabaki.

Tovuti inahudumiwa kutoka mzizi wa mwenyeji wake, kwa hivyo base (/) ya msingi ya bundler yako ni sahihi. Telegram Web hufungua Mini Apps katika iframe, kwa hivyo usiongeze X-Frame-Options au sera kali ya frame-ancestors.

Mwenyeji uleule anajibu endpoints zako chini ya /api/ — hapo ndipo Telegram.WebApp.Serverless.call('getProfile', …) inapoenda. Usiweke faili zako mwenyewe chini ya /api/ kwenye folda ya build.

Hifadhidata

Kila boti inapata hifadhidata yake mwenyewe — hifadhi inayotumia SQLite inayodumu kati ya maombi na inapatikana kwa kila moduli kupitia db. Unaelezea jedwali zako katika schema.js na DSL ndogo, iliyochapwa; unazisoma na kuziandika na mjenzi wa wito wa maji; na unaziboresha na migrations zilizokaguliwa.

Unajua Drizzle ORM ? Basi tayari unajua jinsi ya kuzungumza na hifadhidata hapa. Schema DSL na mjenzi wa wito hufuata Drizzle — wajenzi wa safu, select().from().where(), waendeshaji, onConflictDoUpdate, .returning(), na sql tag zote hufanya kazi jinsi unavyotarajia, kwa hivyo kusoma na kuandika data ni API inayojulikana unayotumia tayari. Unaiingiza tu kutoka sdk/db, na mambo machache ya jukwaa (hasa bila funguo za kigeni ) yanaonyeshwa yanapotokea.

Kutangaza jedwali

Jedwali ni exports zilizopewa jina katika schema.js. Kuita table() hujenga maelezo wakati wa mzigo (haigusi hifadhidata); jukwaa hugundua jedwali zilizosafirishwa unapopeleka schema.js na kuhamisha hifadhidata ili ilingane.

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 ni jina la jedwali la SQL;
  • columns ni ramani ya JS property → ufafanuzi wa safu;
  • extras ni callback ya hiari (t) => ({ … }) ambapo t inafichua safu (t.created ni marejeleo ya safu hiyo) — tangaza faharisi na vikwazo vya kiwango cha jedwali hapa.

Aina za safu

Kiwanda Aina ya SQLite Maelezo
text() TEXT
integer() INTEGER
real() REAL alias float()
numeric() NUMERIC
blob() BLOB inasoma/inaandika Uint8Array
boolean() INTEGER imehifadhiwa kama 0/1, inasomwa kama true/false
json() TEXT auto JSON.stringify / JSON.parse

Hoja ya jina la safu ni ya hiari — iruke na key ya JS itatumika. Chaguo la mode linadhibiti jinsi thamani hubadilika kati ya SQLite na JavaScript.

mode Imehifadhiwa kama Thamani ya JS
boolean INTEGER 0/1 boolean
json TEXT (JSON) kitu chochote/safu
timestamp INTEGER (unix seconds) Date
timestamp_ms INTEGER (unix ms) Date
bytes BLOB Uint8Array

boolean() na json() ni njia za mkato za integer(name, { mode: 'boolean' }) na text(name, { mode: 'json' }).

blob() inasoma na kuandika Uint8Array — utekelezaji hauna Node Buffer (na Buffer ni ndogo-darasa ya Uint8Array, kwa hivyo huu ndio aina ya msingi inayobebeka). mode hapo juu inadhibiti tu jinsi thamani inavyo-kodishwa, huru kutoka kwa aina ya uhifadhi wa safu: kwa hivyo blob('col', { mode: 'json' }) ni mwenzake wa BLOB wa json() — usimbaji huo huo wa JSON, uliowekwa kwenye safu ya BLOB badala ya TEXT.

TLDR: blob() inatumia Uint8Array. mode inadhibiti usimbaji, si uhifadhi, kwa hivyo blob(..., { mode: 'json' }) inahifadhi JSON kama BLOB, wakati json() inahifadhi kama TEXT.

Vidhibiti vya safu

Vidhibiti vya safu hujiunga kwenye safu.

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' })  // au 'virtual'
text('email').deprecated('replaced by login')   // inaweka alama ya safu kwa ajili ya kuondolewa

.default() kwenye safu ya json() inakukodisha thamani; sql`…` default inapitishwa kama ilivyo. .deprecated() ni ya mwisho — tazama Migrations .

Faharisi na vikwazo

Faharisi na vikwazo vinatangazwa katika extras callback, ambapo safu ziko kwenye upeo.

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})`) ,          // faharisi ya usemi
  active: index('idx_active').on(t.userId).where(sql`done = 0`) ,  // faharisi ya sehemu
}));

Vidhibiti vya kiwango cha jedwali hujiunga baada ya table(...): .strict(), .withoutRowid(), .deprecated('reason').

Hakuna funguo za kigeni (Foreign keys)

Runtime inafanya kazi na PRAGMA foreign_keys ikiwa imezimwa. Ufunguo wa kigeni uliotangazwa ungekuwa bila nguvu kimya kimya — hakuna cascades, hakuna ulinzi wa yatima — ambayo ni mbaya kuliko kutokuwa na chochote, kwa hivyo DSL inafanya kuwa vigumu. Yaani, .references() na foreignKey() ya kiwango cha jedwali hutupa kosa inapotangazwa, na schema inayozitumia haitaweza kupelekwa.

Unapaswa kuweka mifano ya mahusiano na safu rahisi (userId: integer('user_id')) na kutekeleza uadilifu katika msimbo wa maombi yako: ingiza wazazi kabla ya watoto, futa watoto kabla ya wazazi, shughulikia makosa vizuri na futa yatima na LEFT JOIN … WHERE parent.id IS NULL unapohitaji.

Kutokuwepo kwa funguo za kigeni ni kizuizi cha makusudi, si kosa. Izingatie mapema unapopanga boti yako.

Kuhoji (Querying)

db ni mjenzi wa wito wa maji. Kila wito ni wa asynchronous — mbinu za mwisho (.all(), .get(), .values(), .run()) hurudisha Promises, kwa hivyo daima 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();                          // safu zote
await db.select().from(todos).where(eq(todos.id, 1)).get();   // safu ya kwanza au null
await db.select().from(todos).values();                       // safu kama safu za thamani

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

// makadirio ya kawaida: { 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();

// hesabu ya safu — msaidizi, si mjenzi wa mwisho:
await db.$count(todos);                        // safu zote
await db.$count(todos, eq(todos.done, false)); // na kichujio
  • Zinazoweza kuunganishwa: .where(), .orderBy(), .limit(), .offset(), .groupBy(), .having(), .distinct().
  • Mwisho: .all(), .get(), .values().
  • Hesabu safu na db.$count(table, where?) (au count() katika makadirio).

Ingiza, sasisha, futa:

await db.insert(todos).values({ userId: 1, text: 'Buy milk' }).run();
await db.insert(todos).values([{ text: 'A' }, { text: 'B' }]).run();   // kundi
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();

Kumbuka kuwa uingizaji wa kundi ni taarifa moja, kwa hivyo umepunguzwa na kikomo cha tofauti cha SQLite (safu × safu wima); ukivuka, uingizaji unatoa kosa — igawanye mwenyewe.

Waendeshaji wanaingizwa kutoka 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) na hoja nyingi ni sawa na and(a, b). Hoja ya pili ya ulinganisho ni thamani kwa chaguo-msingi, lakini inaweza kuwa safu nyingine au sql`…` — mfano eq(a.x, b.y). Aggregates (count/sum/avg/min/max) ni vipande vya sql kwa makadirio ya .select({ … }).

SQL mbichi — wakati mjenzi haitoshi, shuka kwenye SQL mbichi. Hali inachaguliwa na mbinu: db.run kwa uandishi, db.all kwa safu nyingi, db.get kwa safu moja, db.values kwa safu kama safu za msimamo.

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, 'buy milk']]

Uandishi unatatua kuwa matokeo ya uendeshaji — rowsAffected, lastInsertRowid, na rows, ambayo hubeba kifungu cha RETURNING ikiwa SQL ilikuwa na moja. Tumia db.values wakati wito unaonyesha safu mbili za jina moja (join kama SELECT u.id, o.id): db.all inaweka safu kwa jina la safu, kwa hivyo id ya pili ingeandika juu ya ya kwanza.

sql`…` tag inabadilisha ${value} kuwa kigezo kilichofungwa na ${table.column} kuwa kitambulishi, na kuunganisha vipande vya sql vilivyowekwa ndani. Tumia sql.raw('…') kwa herufi bila vigezo.

Wito mbichi hazijafungwa na jedwali, kwa hivyo safu zao hurudi bila ubadilishaji wa hali — boolean ni 0/1, safu ya JSON ni kamba, timestamp ni namba. Mjenzi aliyefungwa na jedwali pekee ndiye anayebadilisha thamani.

Migrations

Hifadhidata yako hubadilika boti yako inavyokua. Jukwaa huiweka salama kwa kutenganisha kupeleka msimbo kutoka kwa kubadilisha data, na kwa kuainisha kila mabadiliko ya schema kwa jinsi ya hatari ilivyo.

Kupeleka msimbo hakugusi hifadhidata. Unapopeleka npx tgcloud push schema.js iliyobadilika, jukwaa hurekodi schema mpya na kukuambia nini hifadhidata ingebadilisha — lakini haitumii chochote:

npx tgcloud push       # peleka schema.js; inaripoti mabadiliko ya DB yanayosubiri
npx tgcloud migrate    # kagua na uyaapply

npx tgcloud migrate huhesabu tofauti kati ya schema yako na hifadhidata hai na kukuongoza kupitia hiyo. Unaulizwa daima kabla ya chochote kutumika. Hii inamaanisha kupeleka msimbo wa kawaida hakuwezi kuanzisha mabadiliko ya data kwa bahati mbaya.

Kila mabadiliko yanayosubiri hubeba hali inayodhibiti jinsi migrate inavyoyatendea:

Hali Maana Katika migrate
safe Nyongeza na isiyozuia — jedwali jipya, safu, au faharisi Zinatumiwa pamoja katika hatua moja, baada ya uthibitisho
warning Inaweza kuharibu au polepole — kufuta kitu, au faharisi kwenye jedwali kubwa Inawasilishwa moja kwa moja, kila moja ikithibitishwa kando
manual Haiwezi kufanywa kiotomatiki — mfano kubadilisha aina ya safu Inaonyeshwa na mwongozo; unaifanya kwa mkono
undocumented Ipo kwenye hifadhidata lakini si kwenye schema yako Inaonyeshwa kwa ufahamu; haitumiki

Mabadiliko salama ni ya haraka na yanaweza kutenduliwa katika roho, kwa hivyo yanapita pamoja. Kila onyo ni uthibitisho wa makusudi, wa mtu binafsi — hakuna “apply all” kwa mabadiliko ya uharibifu.

Mabadiliko ya mwongozo huja na sababu na hatua iliyopendekezwa. migrate inaisha na muhtasari: mabadiliko mangapi yalitumiwa, kurukwa, kusubiri marekebisho ya mwongozo, au hayapo kwenye schema yako.

Tazama migrate kwa bendera (--dry-run, --safe, --yes, --local). Bila bendera, migrate inahitaji terminal na inatoa makosa katika mazingira yasiyo ya mwingiliano badala ya kubahatisha.

Kuondoa vitu

Kufuta jedwali au safu kutoka schema.js hakufuti — hiyo ingefanya ufutaji wa ajali kuwa mbaya sana. Ili kuondoa kitu, kiweke alama kuwa kimepitwa na wakati (deprecated):

// futa safu
text('email').deprecated('replaced by login')

// futa jedwali zima
export const oldSessions = table('old_sessions', { /* … */ }).deprecated('unused');

Katika migrate inayofuata, kitu kilichopitwa na wakati kinaonekana kama kushuka kwa hali ya onyo ambayo unathibitisha kando. Mara inapokuwa imekwenda, ondoa tangazo.

Kubadilisha aina ya safu

Mabadiliko ya aina ni ya mwongozo — SQLite haiwezi kuyafanya kila wakati mahali, na kulazimisha thamani zilizopo ni wito wa uamuzi. migrate itaonyesha mabadiliko na sababu yake; yafanye mwenyewe na SQL mbichi (db.run(...)), kwa kawaida kwa kutengeneza safu au jedwali mpya, kunakili data, na kubadilishana.

SDK

Wakati wa utekelezaji moduli ina maktaba moja: sdk. Inakusanya vitu vitatu ambavyo backend ya boti inahitaji — hifadhidata, Telegram Bot API, na HTTP ya nje — bila kitu cha kusakinisha na bila vitambulisho vya kusanidi. Hifadhidata (db) imeelezwa katika The database ; sehemu hii inashughulikia api, fetch, na console ya kimataifa.

import { db, api, fetch, InputFile, BotApiError, EndpointError } from 'sdk';   // uso mzima
// au kutoka moduli ndogo:
import { table, integer, text, eq, sql } from 'sdk/db';
import { api } from 'sdk/api';
import { fetch } from 'sdk/fetch';
Ingizo Ni nini
db Hifadhidata — mjenzi wa wito na schema DSL → The database
api Telegram Bot API — api.sendMessage(...) → chini
fetch HTTP ya nje → chini
InputFile Faili (bytes + jina) ya kupakia — inafanya kazi na api na fetch → Files
EndpointError Kataa wito wa Mini App na ujumbe inaoweza kuonyesha → Endpoints

Ingiza moduli za mradi wako mwenyewe kwa njia ya jamaa na kiendelezi cha .js (from '../schema.js', from '../lib/cart.js'). Tazama The module system .

The Bot API

api inakupa Telegram Bot API nzima. Ita mbinu yoyote kama api.<method>(params). Kila mbinu ya sasa — na ya baadaye — ya Bot API inafanya kazi bila sasisho la SDK linalohitajika.

import { api } from 'sdk';

const me = await api.getMe();                            // → matokeo yaliyofunguliwa
await api.sendMessage({ chat_id: id, text: 'Hujambo!' });
await api.editMessageText({ chat_id, message_id, text: 'Imesasishwa' });
await api.answerCallbackQuery({ callback_query_id, text: 'Imekamilika' });

Bahasha ya majibu imefunguliwa. Bot API kawaida hufunga matokeo katika { ok: true, result: … }. api inarudisha result moja kwa moja — getMe() inatatua kuwa kitu cha mtumiaji, si bahasha. Vigezo hutumia majina ya snake_case ya Bot API mwenyewe (chat_id, message_id, reply_markup, …).

Kushindwa hutupa BotApiError. Wakati Bot API inarudisha { ok: false }, wito hutupa BotApiError badala ya kurudisha thamani ya uwongo, kwa hivyo huwezi kuipuuza kwa bahati mbaya. Kosa linabeba .code (Bot API error_code), .description (ujumbe unaosomeka na binadamu), .method (ni mbinu gani ilishindwa), na .parameters (data ya ziada kama retry_after kwenye 429, au migrate_to_chat_id). Ikamate ili kushughulikia kushindwa kunakotarajiwa na kutupa tena vinginevyo:

import { api, BotApiError } from 'sdk';

try {
  await api.deleteMessage({ chat_id, message_id });
} catch (e) {
  if (e instanceof BotApiError && e.code === 400) {
    // 400 = ujumbe umekwisha; ni sawa hapa.
  } else {
    throw e;
  }
}

Faili

Fanya kazi na bytes za faili, si file_ids tu — pakia faili mpya na upakue zilizopo.

Pakia — InputFile. Funga bytes mbichi katika InputFile (kishikilia rahisi: bytes, jina la faili, na type ya hiari ya MIME) na upitishe popote mbinu ya Bot API inapotarajia faili:

import { api, InputFile } from 'sdk';

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

// faili zilizowekwa ndani ya params hufanya kazi pia — mfano albamu:
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' }) },
]});

Bytes zinatiririshwa kwenda Telegram kama upakiaji halisi wa multipart — faili haijahifadhiwa yote katika wito mmoja kwa jukwaa — lakini mipaka ya Bot API ya Telegram bado inatumika: boti kwa sasa inaweza kupakia hadi 50 MB kwa kila faili (na 10 MB kwa picha).

Pakua — na file_id. Wasaidizi wawili huvuta bytes za faili. Jukwaa hutatua file_id kwa ajili yako (inaendesha getFile na kuchota maudhui nyuma ya pazia), kwa hivyo huwezi kugusa token ya boti au URL ya kupakua:

const bytes = await api.getFileContent(file_id);          // → Uint8Array (faili nzima)

const { file, body } = await api.getFileStream(file_id);  // tiririsha badala yake
file.file_unique_id;                                      // maelezo ya getFile
for await (const chunk of body) { /* Uint8Array */ }      // au ihifadhi: await stream.bytes()

Bot API ya Telegram hufunga vipakuliwa vya getFile katika 20 MB, kwa hivyo huo ndio kikomo kwa wasaidizi wote wawili.

HTTP

fetch ni mteja anayefanana na fetch kwa kuita ulimwengu wa nje — API za watu wengine, webhooks, chochote kupitia HTTP.

import { fetch } from 'sdk';

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

Majibu huakisi jukwaa la wavuti: res.status, res.statusText, res.ok (kweli kwa 200–299), res.url, res.headers (.get(), .has(), .keys(), .entries()), na wasomaji wa mwili await res.json() / await res.text().

Unaweza pia kusoma mwili kwa kuongezeka kama mkondo (stream) — for await (const chunk of res.body) { … } — ambayo ndiyo jinsi unavyotumia matukio yaliyotumwa na seva au matokeo ya token-kwa-token kutoka AI APIs.

Wasaidizi wa mwili huweka Content-Type inayolingana kwa ajili yako:

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

Miili ya maombi inaweza pia kubeba faili na bytes mbichi: pitisha Uint8Array, InputFile, au FormData (kwa multipart/form-data) kama body, na bytes zinatiririshwa nje — kwa hivyo unaweza kupakia kwa API ya mtu wa tatu jinsi unavyotuma faili kwa 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', 'kutoka kwa boti yangu');
const res = await fetch('https://api.example.com/upload', { method: 'POST', body: form });

Vinginevyo inafanya kazi kama fetch ya kawaida unayoijua tayari, na vikwazo viwili:

  • Majibu yanasomwa kama maandishi — await res.json() / await res.text(), au kutiririshwa chunk-kwa-chunk na res.body.
  • Jumla ya majibu imefungwa katika 30 MB. Kikomo hicho kinashughulikia majibu yote — kutiririsha na res.body hukuruhusu kuchakata mwili mkubwa kwa kuongezeka, lakini haiondoi kikomo.

Logging

console ya kimataifa ya kawaida inapatikana — hakuna cha kuingiza, ipo tu kama katika JavaScript yoyote. Matokeo yake yanakamatwa na kuonyeshwa na npx tgcloud run, ambayo inafanya kuwa zana yako kuu ya utatuzi wakati wa maendeleo.

console.log('inachakata', { chatId: id });   // log / debug — rahisi
console.info('imeanza');                     // info  — bluu
console.warn('kikomo cha kasi');             // warn  — njano
console.error(err);                          // error — nyekundu, na stack trace

Kila mstari umewekwa alama na [file:line] ulikotoka. console.error na console.trace huongeza stack kamili, wakati console.warn haifanyi hivyo. Unapoi-npx tgcloud run moduli, mistari hii inachapishwa na kiambishi cha rangi kwa kila ngazi, muda tangu kuanza kwa uendeshaji, na asili — tazama run .

Command-line interface

tgcloud ni daraja kati ya folda ya mradi wako na cloud. Inaunda miradi, inakuonyesha nini kimebadilika, inapeleka, inaendesha moduli, na inatumia migrations za hifadhidata. Inahitaji Node.js 18 au mpya zaidi. Njia mbili za kuipata:

# Inayopendekezwa — tengeneza mradi na CLI iliyosakinishwa ndani yake:
npm create @tgcloud/bot example_bot

# Au sakinisha CLI kimataifa na init folda tupu:
npm install -g @tgcloud/cli
tgcloud init

Kifurushi cha npm ni @tgcloud/cli, na amri inayosakinisha ni tgcloud. Kila amri inafanya kazi kutoka ndogo-folda yoyote ya mradi wako.

Amri Kusudi
init Unda mradi mpya katika folda ya sasa
add Unda moduli mpya (kishikilia, endpoint au moduli ya lib)
login Unganisha mradi na boti (huhifadhi token)
status Onyesha nini kimebadilika ndani ya kompyuta dhidi ya cloud
diff Onyesha mabadiliko ya mstari kwa mstari
push Peleka moduli zilizobadilika na build ya Mini App kwenye cloud
migrate Tumia mabadiliko ya schema kwenye hifadhidata
run Tekeleza moduli kwenye jukwaa bila kupeleka
fetch Kagua nini kimepelekwa, bila kubadilisha faili za ndani
pull Leta faili za ndani sambamba na cloud
reset Futa mabadiliko ya ndani; rejesha kutoka hali ya cloud
webhook Kagua na usawazishe webhook inayodhibitiwa na jukwaa
completion Chapisha hati ya kukamilisha shell (bash/zsh/fish)
upgrade Hamisha mpangilio wa mradi wa kabla ya 0.2.0 kwenye tgcloud/

Authentication

Mradi umeunganishwa na boti moja kwa token yake, ambayo ina umbo la app<id>:<secret>. Sehemu ya app<id> ni ya umma na inaweza kuchapishwa; siri haionekani kamwe kwenye kumbukumbu au makosa.

Token inatatuliwa kwa utaratibu huu:

  1. TGCLOUD_TOKEN tofauti ya mazingira — kwa CI; kamwe haiandikwi kwenye diski.
  2. .tgcloud/credentials — iliandikwa na npx tgcloud login .
  3. Hakuna → kosa linalokuelekeza kwenye npx tgcloud login.

CLI kamwe haiulizi token katikati ya amri. Kuingia daima ni hatua ya wazi ya login. Ikiwa token iliyohifadhiwa inakuwa batili, CLI huiondoa na kukuomba u-login tena.

init

npx tgcloud init

Inaunda mradi mpya katika saraka ya sasa: folda ya tgcloud/ na schema.js, lib/, handlers/ na kishikilia cha kuanzia, pamoja na tgcloud.jsonc, AGENTS.md, docs/, na folda ya hali ya .tgcloud/. Iendeshe kwenye mzizi wa mradi wa front‑end uliopo pia — inaongeza folda ya tgcloud/ na docs, na kamwe haifuti faili zako. Ikiwa inatambua front‑end, pia inaelekeza tgcloud.jsonc kwenye folda ya build na inaongeza hati ya deploy (jenga, kisha sukuma) kwenye package.json yako — tu ikiwa huna hati ya deploy tayari. Tazama Mini App front‑end .

init inakataa kuunda mradi ndani ya mradi mwingine. Kuiendesha tena kwenye mzizi wa mradi ni sawa na inaongeza tu chochote kinachokosekana.

add

npx tgcloud add <target>

Inaunda moduli moja mpya, iliyounganishwa na tayari kuhaririwa — kishikilia, endpoint au moduli ya lib/.

npx tgcloud add handlers/callback_query   # kishikilia sasisho kipya
npx tgcloud add endpoints/getProfile      # endpoint mpya kwa Mini App
npx tgcloud add lib/cart                  # moduli mpya ya pamoja

Kumbuka kuwa add kamwe haifuti faili iliyopo.

<target> ni njia ya moduli (kiendelezi cha .js cha mwisho ni cha hiari). Kwa handlers/, jina lazima liwe aina ya sasisho la Telegram. Jukwaa hutangaza seti sahihi, kwa hivyo jina batili linakataliwa kwanza. handlers/ na endpoints/ ni bapa, wakati lib/ inaweza kuwekwa ndani (lib/payments/stripe).

Jina la moduli linahitajika. Kutoa folda tu ni kosa — lakini la kusaidia: kwa handlers/ inatoa orodha ya aina za masasisho ambazo huna bado, kwa hivyo unaweza kunakili moja.

$ npx tgcloud add handlers
Kosa: Taja jina, mfano "handlers/callback_query". Aina za handlers/ zinazopatikana: callback_query, inline_query, chat_member, …

<Tab> completion inatoa seti ileile — tazama completion .

Faili iliyoundwa ina export default hai, kwa hivyo kishikilia kinafanya kazi mara tu unapo-deploy — hakuna cha kuondoa maoni. Peleka moduli mpya na push .

login

npx tgcloud login

Inakuuliza token yako ya ufikiaji ya CLI — kutoka @BotFather → boti yako → Serverless → CLI Access → Access token, token tofauti na token ya API ya boti yako — inathibitisha dhidi ya jukwaa, na kuihifadhi kwenye .tgcloud/credentials.

login ni amri pekee inayouliza token, na inahitaji terminal ya mwingiliano. Katika CI, weka TGCLOUD_TOKEN badala yake.

status

npx tgcloud status

Inaonyesha, kwa kila faili, nini kimebadilika kati ya folda yako ya kazi na nakala iliyopelekwa: iliyorekebishwa, mpya, iliyofutwa, haijabadilika. Inafanya kazi nje ya mtandao, ikilinganisha dhidi ya hali ya cloud kama ya push, pull au fetch yako ya mwisho. Pia inaonya kuhusu faili za .js zilizopotea ndani ya tgcloud/ ambazo hazitapelekwa.

Kwa mradi wenye Mini App, status pia inaonyesha nini kimebadilika katika folda yake ya build tangu deploy ya mwisho.

diff

npx tgcloud diff

Kama status, lakini inaonyesha tofauti halisi ya mstari kwa mstari kwa moduli zilizobadilika. Pia nje ya mtandao.

push

npx tgcloud push [files...]

Inapeleka mradi wako kwenye cloud katika kundi moja.

Bila hoja yoyote, inapeleka mradi mzima, na hali iliyopelekwa inafanywa kuakisi folda yako kikamilifu — moduli ulizofuta ndani ya kompyuta zinaondolewa kwenye cloud.

Kwa hoja za faili au folda (npx tgcloud push tgcloud/handlers/message.js, npx tgcloud push handlers/), inapeleka tu mabadiliko katika faili hizo. Push inayolengwa kamwe haifuti moduli nyingine. Lengo linaweza kuandikwa kama njia ya faili (tgcloud/handlers/message.js), njia ndani ya tgcloud/ (handlers/message.js) au jina la moduli (handlers/message) — zote zinamaanisha moduli ileile.

Chaguo lake moja ni --force — kuruka ukaguzi wa usawaziko na kuandika juu ya chochote kilichopo kwenye cloud. Itumie tu unapokuwa na uhakika (tazama Staying in sync ).

Ikiwa tgcloud.jsonc inataja folda tuli, push kamili inaipeleka katika kundi lileile — faili zilizobadilika tu ndizo zinazopakiwa — na inachapisha anwani ya tovuti. Jenga kwanza, kwa sababu push kamwe haifanyi build yako (npm run deploy hufanya yote). npx tgcloud push dist/ inapeleka faili tuli pekee, na push inayotaja moduli pekee inaziacha bila kuguswa.

Baada ya deploy, ikiwa schema.js ilibadilika na hifadhidata haijasawazishwa, push inachapisha muhtasari wa mabadiliko yanayosubiri na kupendekeza npx tgcloud migrate. Kamwe haitumii yenyewe.

migrate

npx tgcloud migrate

Inatumia mabadiliko yako ya schema kwenye hifadhidata. Inaunda tofauti kati ya schema.js na hifadhidata hai, kisha inakuongoza kupitia hiyo hatua kwa hatua na kaunta inayokimbia ya [N/M]:

  • Muhtasari mfupi wa mabadiliko yote yanayosubiri.
  • Mabadiliko salama, yaliyotumiwa pamoja katika hatua moja baada ya uthibitisho wako.
  • Maonyo (kufuta, operesheni polepole), moja kwa moja, kila moja ikithibitishwa kando.
  • Mabadiliko ya mwongozo, yaliyoonyeshwa na sababu na hatua iliyopendekezwa, hayatumiwi kiotomatiki.
  • Vitu visivyothibitishwa (kwenye hifadhidata lakini si kwenye schema yako), yaliyoonyeshwa kwa ufahamu.

Inaisha na muhtasari: yaliyotumiwa, kurukwa, kusubiri marekebisho ya mwongozo, hayapo kwenye schema.

Tazama Migrations kwa mfano.

Chaguzi: --dry-run (chapisha kila kitu, usitumie chochote), --safe (auto‑apply mabadiliko salama, ruka maonyo), --yes (auto‑apply mabadiliko salama na kila onyo, ruka mwongozo — tumia kwa uangalifu), --local (linganisha dhidi ya schema.js ya ndani badala ya ile iliyopelekwa).

run

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

Inatekeleza kishikilia au endpoint kwenye jukwaa bila kupeleka, ikitumia faili zako za sasa za ndani. Hii ndiyo mzunguko wa haraka wa ndani wa kujaribu mantiki.

  • <module> — jina wazi (lililotafutwa chini ya handlers/ na endpoints/) au njia kama handlers/message. Jina wazi lililopo katika folda zote mbili lazima liandikwe na folda yake.
  • [args] — payload iliyopitishwa kwa kishikilia, iliyoandikwa katika JSON5 kwa hivyo unaweza kuruka kunukuu keys. Ni kitu cha aina ya sasisho ambacho kishikilia chako hupokea (mfano Ujumbe kwa handlers/message), au input ya endpoint, na lazima iwe kitu — si safu au kamba wazi.
  • --ctx <json5> — kitu cha muktadha wa moduli (hoja yake ya pili), pia JSON5. Itumie kutoa kile msimbo wako unasoma kutoka ctx — sasisho mbichi kwa kishikilia, --ctx '{ update: { update_id: 1 } }', au init data kwa endpoint, --ctx '{ initData: { user: { id: 1 } } }'. run haithibitishi, kwa kuwa unajaribu mantiki yako, si ukaguzi wa jukwaa.
npx tgcloud run handlers/message '{ chat: { id: 1 }, text: "hi" }'

Jukwaa linaendesha moduli dhidi ya nafasi ya moduli iliyokusanywa kutoka kwa mradi wako wa ndani (kwa hivyo msimbo wa lib/ uliobadilishwa ndani unatumika pia) na inarudisha thamani ya kurudishwa, chochote kilichoingizwa na console.*, na muda uliotumika. Soma hoja kubwa kutoka faili na npx tgcloud run handlers/message "$(cat message.json5)".

fetch

npx tgcloud fetch

Inakagua nini kimepelekwa sasa, bila kugusa faili zako za kazi. Baada ya hapo, status na diff hulinganisha dhidi ya hali ya hivi karibuni ya cloud. Ni muhimu kutazama mgogoro kabla ya kuamua jinsi ya kuutatua.

pull

npx tgcloud pull

Inaleta mradi wako wa ndani sambamba na cloud kwa kusasisha faili zako za kazi kwa hali iliyopelekwa.

pull kamwe haiandiki folda ya build ya Mini App yako.

reset

npx tgcloud reset

Inafuta mabadiliko yako ya ndani na kurejesha moduli kutoka hali ya mwisho inayojulikana ya cloud. Itumie kutupa majaribio. Folda ya build ya Mini App haiguswi.

webhook

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

Telegram huwasilisha masasisho kwa boti yako kupitia webhook, ambayo jukwaa inakusimamia — huwezi kuielekeza popote kwa mkono. npx tgcloud webhook inaonyesha hali yake ya sasa: URL, orodha ya allowed_updates, masasisho mangapi yanasubiri, kosa la mwisho la uwasilishaji (kama lipo), na kama iko katika usawaziko na vishikilia ulivyopeleka.

“Iko katika usawaziko” inamaanisha webhook inaelekeza kwenye jukwaa na allowed_updates zake zinalingana na vishikilia ulivyopeleka — kwa hivyo Telegram inawasilisha aina za masasisho unayoshughulikia, na si kitu kingine. Kupeleka kishikilia kipya (au kuondoa kimoja) kunaweza kuacha webhook nje ya usawaziko hadi ifufuliwe; npx tgcloud status inaweka alama hii pia.

npx tgcloud webhook sync inarekebisha — inaelekeza webhook kwenye jukwaa na kujenga upya allowed_updates kutoka kwa vishikilia ulivyopeleka. Ongeza --drop-pending ili kufuta masasisho ambayo Telegram ilikuwa imeyapanga kabla ya usawaziko (vinginevyo yatawasilishwa mara tu webhook itakapokuwa na afya tena).

completion

Kumbuka: tab‑completion inafanya kazi tu wakati tgcloud wazi iko kwenye PATH yako — kwa hivyo isakinishe kimataifa (npm install -g @tgcloud/cli) au vinginevyo weka binary kwenye PATH yako. Haiwezi kuunganishwa kwenye npx.

tgcloud completion <bash|zsh|fish>

Inachapisha hati ya kukamilisha shell kwenye stdout. Iwezeshe mara moja, kisha <Tab> inakamilisha amri, bendera, folda za moduli, aina za masasisho ya vishikilia ambayo huna bado, na moduli zako za ndani zinazoweza kuendeshwa.

# bash — inahitaji kifurushi cha bash-completion:
echo 'eval "$(tgcloud completion bash)"' >> ~/.bashrc

# zsh — hakikisha `autoload -U compinit && compinit` inaendeshwa katika ~/.zshrc yako:
echo 'eval "$(tgcloud completion zsh)"' >> ~/.zshrc

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

Anzisha tena shell yako (au re-source faili) baadaye. Kuendesha tgcloud completion bila shell kunachapisha maagizo haya tena.

upgrade

npx tgcloud upgrade [--dry-run]

Migration ya mara moja kwa miradi iliyoundwa kabla ya CLI 0.2.0, ambayo iliweka schema.js, lib/ na handlers/ kwenye mzizi wa mradi. Inazihamishia kwenye tgcloud/. Kiungo chako na boti kinahifadhiwa, na hakuna kinachohitaji kupelekwa tena. --dry-run inachapisha hatua zilizopangwa bila kugusa chochote. Amri nyingine hazifanyi kazi kwenye mpangilio wa zamani hadi utakapoboresha.

Staying in sync

Kila mradi una marekebisho (revision) yanayoongezeka kwa umoja katika cloud, yaliyoongezwa kwenye kila deploy. CLI inakumbuka marekebisho iliyosawazishwa nayo mara ya mwisho na inatuma kwenye kila push. Ikiwa cloud imesonga mbele — kwa sababu mashine nyingine au mfanyakazi mwenzako alipeleka — push inakataliwa badala ya kuandika juu ya kazi yao kimya kimya, na CLI inatoa njia tatu mbele:

npx tgcloud fetch           # tazama nini kimebadilika kwenye cloud, kisha kagua tena
npx tgcloud pull            # leta ya hivi karibuni kwenye faili zako za kazi
npx tgcloud push --force    # andika juu ya hali ya cloud (hatari)

Ukaguzi huu wa usawaziko wa matumaini ndiyo sababu unaweza kushiriki boti kwenye timu bila mchakato wa deploy wa lockstep. Amri zinatoka zisizo-zero kwenye kushindwa — deploy iliyokataliwa, migration iliyoshindwa, kosa la uthibitishaji, moduli iliyotupa kosa wakati wa run — kwa hivyo zinajumuishwa vizuri katika hati na mabomba ya CI.