Telegram Serverless ermöglicht es Ihnen, Backend-Code für Ihren Bot und Ihre Mini-App direkt auf der Infrastruktur von Telegram auszuführen – keine Server müssen bereitgestellt, keine Container am Laufen gehalten und keine Skalierung bedacht werden. Sie schreiben einfache JavaScript-Module, stellen sie mit einem einzigen Befehl bereit, und Telegram führt sie in einer schnellen, isolierten V8-Sandbox aus, die sich direkt neben der Bot-API und einer integrierten Datenbank befindet.
Wenn Sie jemals einen Bot mit einem VPS, einer Cloud-Funktion oder einem Hosting-Panel verbunden haben, nur um auf einen /start zu antworten, ist dies der Teil, den Sie nicht mehr tun müssen.
Warum serverlos
Ein Telegram-Bot ist im Grunde ein Programm, das auf Updates reagiert. Traditionell mussten Sie dieses Programm irgendwo hosten, das immer erreichbar, erreichbar und sicher war – und es dann auch so halten. Telegram Serverless entfernt diese Schicht vollständig:
- Keine Infrastruktur. Es gibt keine Maschine, die gemietet, gepatcht oder überwacht werden muss. Ihr Code wird nach Bedarf ausgeführt und skaliert automatisch mit Ihrem Bot.
- Anmeldedaten inklusive. Die Telegram Bot API, eine SQLite-gestützte Datenbank und ausgehende HTTP-Anfragen sind für jedes Modul sofort verfügbar – nichts muss installiert, keine Anmeldedaten müssen verdrahtet werden.
- Schnelle, isolierte Ausführung. Jeder Aufruf läuft in einem leichtgewichtigen V8-Isolat, nahe den eigenen Systemen von Telegram, sodass Aufrufe an die Bot-API und Ihre Datenbank schnell und zuverlässig sind.
- Ein echtes Entwickler-Workflow. Ein Projekt lebt in einem Ordner auf Ihrem Rechner unter Versionskontrolle. Sie bearbeiten Dateien, sehen genau, was sich geändert hat, stellen atomar bereit und rollen Ihr Datenbankschema mit geprüften Migrationen nach vorne – so, wie Sie es bereits mit allem anderen tun.
Das mentale Modell: So funktioniert das Projekt
Sie arbeiten an drei Orten, und diese bilden saubere Entsprechungen zueinander:
| Wo | Was befindet sich dort |
|---|---|
| Ihr Projektordner | JavaScript-Module – Schema, gemeinsam genutzter Code, Update-Handler, Mini-App-Endpunkte – und, falls vorhanden, das Frontend der Mini-App |
| Die Cloud | Die bereitgestellte Kopie dieser Module, die Datenbank Ihres Bots und die Dateien der Mini-App |
Die tgcloud-CLI |
Die Brücke – sie zeigt Ihnen Unterschiede und synchronisiert sie |
Sie SSHen sich nie irgendwo ein. Sie bearbeiten Dateien lokal, führen npx tgcloud push aus, und die Plattform kümmert sich um den Rest. Der Traffic Ihres Bots wird von den bereitgestellten Modulen verarbeitet; Ihre Datenbank bleibt zwischen den Aufrufen bestehen.
Ein Projekt besteht aus nur vier Arten von Code, die alle innerhalb eines tgcloud/-Ordners liegen:
tgcloud/
├─ endpoints/ # Funktionen, die Ihre Mini-App aufruft
├─ handlers/ # Funktionen, die auf Telegram-Update-Typen reagieren
├─ lib/ # gemeinsam genutzter Code, den Sie von überall importieren
└─ schema.js # Ihre Datenbanktabellen
Wenn ein Update eintrifft – eine Nachricht, ein Tastendruck, eine Inline-Abfrage –, leitet Telegram es an den passenden Handler (handlers/message.js, handlers/callback_query.js, …) weiter und ruft dessen Standardexport auf. Diese Funktion spricht über das SDK mit der Bot-API und der Datenbank und gibt zurück. Das ist der gesamte Loop. Ein Update ohne passenden Handler wird einfach ignoriert, sodass Sie nur die Handler hinzufügen, die Sie benötigen.
Eine Mini-App ist optional. Wenn Ihr Bot eine hat, befindet sich ihr Frontend im selben Projekt, neben tgcloud/. Serverless hostet die Build-Ausgabe und führt die Endpunkte aus, die sie aufruft. Siehe
Mini-App Frontend
.
Kurze Demo
Hier ist eine vollständige, funktionierende Demo-Bot. Er antwortet auf jede Nachricht und merkt sich, wie viele er von jedem Chat gesehen hat.
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;
// Fügt den Zähler ein oder inkrementiert ihn, wenn dieser Chat bereits einen hat – und holt die
// resultierende Zeile im selben Statement über .returning() zurück.
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: `Hallo! Ich habe ${row.seen} Nachricht(en) von dir gesehen.`,
});
}
Stellen Sie es bereit:
npx tgcloud push # lädt die Module hoch
npx tgcloud migrate # erstellt die `counters`-Tabelle
Das ist ein Live-Bot mit persistentem Zustand und ohne Server. Alles darin – api, db, die table()-DSL – wird in den folgenden Abschnitten beschrieben.
Serverless ist ein allgemeines Backend für Telegram-Bots und Mini-Apps, kein Vorlagen-System für eine bestimmte Art von App. Es ist ideal für:
- Konversationelle KI-Bots, die benutzerspezifischen Zustand in einer Datenbank speichern müssen.
- Mini-App-Backends, die Benutzerdaten speichern und dynamische Inhalte bereitstellen.
- Spiele und Tools – einschließlich Bestenlisten, Quizfragen und mehr.
- Automatisierungen und Integrationen, die externe HTTP-APIs aufrufen und Ergebnisse in Chats pushen.
Erste Schritte
Diese Anleitung führt Sie von einem leeren Ordner zu einem Live-Bot, der auf Nachrichten antwortet und Daten speichert. Sie setzt voraus, dass Sie
Node.js
18 oder neuer installiert haben und einen mit
@BotFather
registrierten Bot besitzen. Am Ende werden Sie jeden Befehl verwendet haben, den Sie täglich benötigen: push, migrate, run und status.
Schalten Sie Serverless zuerst ein. Öffnen Sie in @BotFather Ihren Bot → Serverless und schalten Sie ihn ein. Das aktiviert die Funktion für diesen Bot und schaltet seinen CLI-Zugriffstoken, Handler, die Bibliothek und die Datenbank frei.
1. Ein Projekt erstellen
Der schnellste Weg zum Start ist der Projekt-Ersteller, der ein Projektgerüst erstellt und die CLI darin installiert:
npm create @tgcloud/bot example_bot
cd example_bot
Das Argument ist der Zielordner: übergeben Sie . zum Erstellen im aktuellen Ordner oder einen beliebigen Pfad. Es funktioniert auch in einem bestehenden Ordner und überschreibt niemals Dateien, die Sie bereits haben.
Dies gibt Ihnen ein fertig zu bearbeitendes Projekt:
example_bot/
├─ tgcloud/ # alles, was die Plattform ausführt
│ ├─ handlers/
│ │ └─ message.js # ein Starter-Nachrichten-Handler (gibt Text zurück)
│ ├─ lib/ # Ihre gemeinsam genutzten Module kommen hierher (leer zu Beginn)
│ └─ schema.js # Ihre Datenbanktabellen
├─ docs/
│ └─ tgcloud-sdk.md # SDK-Referenz (für Sie und Ihre KI-Tools)
├─ AGENTS.md # Orientierung für KI-Coding-Assistenten
├─ package.json
└─ tgcloud.jsonc # Projektkonfiguration
Die generierten Dateien sind selbsterklärend – jede enthält kommentierte Beispiele für das, was Sie als Nächstes tun können.
Die CLI wird als lokale Entwicklungsabhängigkeit im Projekt installiert, sodass Sie sie mit npx tgcloud <command> ausführen (npx findet die Kopie in den node_modules Ihres Projekts) oder über die npm run-Verknüpfungen, die der Generator zu package.json hinzufügt (npm run deploy, npm run status). Standardmäßig gibt es kein globales tgcloud in Ihrem PATH.
Sie können es auch global installieren – npm install -g @tgcloud/cli –, wenn Sie lieber ein nacktes tgcloud von überall aus tippen möchten. Das ist praktisch, um tgcloud init in jedem leeren Ordner auszuführen, und es ist das, was die Shell-Tab-Vervollständigung benötigt. In beiden Fällen erhalten Sie dasselbe Projekt.
Erstellen Sie eine Mini-App? Starten Sie stattdessen mit ihrem Frontend. Erstellen Sie sie mit Ihrem üblichen Werkzeug und führen Sie dann den Projekt-Ersteller in diesem Ordner aus. Siehe Mini-App Frontend .
2. Ihren Bot verknüpfen
Jedes Projekt ist an einen einzigen Bot gebunden. Verknüpfen Sie sie mit login, was Sie nach Ihrem CLI-Zugriffstoken fragt (
@BotFather
→ Ihr Bot → Serverless → CLI Access → Access token – ein separater Token von Ihrem
Bot API Token
) und ihn lokal speichert:
npx tgcloud login
Der Token hat das Format app<id>:<secret>. Die CLI speichert ihn in .tgcloud/, das ignoriert wird, und gibt niemals den geheimen Teil aus. Login ist das einzige Mal, dass Sie danach gefragt werden – siehe
Authentifizierung
für die Auflösung von Tokens in CI.
3. Umsehen
Zwei Befehle zeigen Ihnen jederzeit, wo die Dinge stehen, beide vollständig offline:
npx tgcloud status # Was hat sich lokal im Vergleich zur bereitgestellten Kopie geändert
npx tgcloud diff # Die zeilenweisen Änderungen
Direkt nach init ist alles neu und noch nichts bereitgestellt. status zeigt die Starter-Dateien, die hochgeladen werden müssen.
4. Bereitstellen
Senden Sie Ihre Module in die Cloud:
npx tgcloud push
push lädt jedes geänderte Modul in einem atomaren Stapel hoch und aktualisiert Ihre lokale Aufzeichnung dessen, was die Cloud nun enthält. Ihr Bot ist live: Öffnen Sie ihn in Telegram und senden Sie ihm eine Nachricht – der Starter-Handler gibt sie zurück.
Bereitstellungen berühren niemals Ihre Datenbank. Das Pushen von Code und das Ändern Ihres Datenbankschemas sind bewusst getrennte Schritte, sodass eine Code-Bereitstellung niemals mit einer Datenmigration überraschen kann. Das ist es, was der nächste Schritt ist.
5. Eine Datenbanktabelle hinzufügen
Lassen Sie den Bot etwas speichern. Öffnen Sie tgcloud/schema.js und deklarieren Sie eine Tabelle:
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())`) ,
});
Stellen Sie das Schema bereit und wenden Sie es dann auf die Datenbank an:
npx tgcloud push # lädt das neue Schema hoch
npx tgcloud migrate # erstellt die `messages`-Tabelle
push meldet, dass das Schema nicht synchron ist und zeigt die ausstehende Änderung an, wendet aber nichts an. migrate führt Sie durch die Änderung und erstellt auf Ihre Bestätigung hin die Tabelle. Dieses Zwei-Schritt-Modell – und was mit riskanteren Änderungen wie dem Löschen geschieht – wird in
Migrationen
behandelt.
6. Daten speichern und lesen
Verwenden Sie die Tabelle nun aus Ihrem Handler. Bearbeiten Sie tgcloud/handlers/message.js:
import { api, db } from 'sdk';
import { eq } from 'sdk/db';
import { messages } from '../schema.js';
export default async function (message) {
// Speichert diese Nachricht.
await db.insert(messages)
.values({ chatId: message.chat.id, text: message.text })
.run();
// Zählt, wie viele wir für diesen Chat gespeichert haben.
const count = await db.$count(messages, eq(messages.chatId, message.chat.id));
await api.sendMessage({
chat_id: message.chat.id,
text: `Gespeichert. Das sind ${count} Nachricht(en) aus diesem Chat bisher.`,
});
}
Stellen Sie den aktualisierten Handler mit npx tgcloud push bereit und senden Sie dann einige Nachrichten an Ihren Bot und beobachten Sie, wie die Zählung steigt. Die Datenbank bleibt zwischen den Aufrufen erhalten – das ist das Gedächtnis Ihres Bots.
7. Ohne Bereitstellung testen
Sie müssen nichts bereitstellen, um eine Änderung auszuprobieren. npx tgcloud run führt einen Handler auf der Plattform mit Ihren lokalen Dateien aus, ohne diese zu veröffentlichen:
npx tgcloud run handlers/message '{ chat: { id: 1 }, text: "hallo" }'
Das Argument ist die Nutzlast, die Ihr Handler erhält – für handlers/message eine
Nachricht
– geschrieben in
JSON5
(damit Sie Schlüssel weglassen können) . Der Befehl gibt alles aus, was der Handler mit console.* protokolliert hat, den Rückgabewert und wie lange es gedauert hat. Das ist die engste Schleife, um Logik zu iterieren – keine Bereitstellung, kein Warten auf eine echte Nachricht.
8. Synchron bleiben
Während Sie arbeiten, halten eine Handvoll Befehle Ihr lokales Projekt und die Cloud synchron: npx tgcloud status zeigt, was sich geändert hat, npx tgcloud push stellt bereit, npx tgcloud pull bringt Ihr lokales Projekt mit der Cloud in Einklang, npx tgcloud fetch prüft, was bereitgestellt ist, ohne Ihre Dateien zu berühren, und npx tgcloud reset verwirft lokale Änderungen.
Wenn zwei Personen (oder zwei Maschinen) dasselbe Bot bereitstellen, erkennt die Plattform den Konflikt und
pushstoppt, damit Sie zuerstpullkönnen – Sie können niemals stillschweigend die Arbeit eines anderen überschreiben. Siehe Synchron bleiben .
Mit KI aufbauen
Bevorzugen Sie den Aufbau mit einem KI-Assistenten – oder ist der einzige Programmierer in Ihrem Team eine KI? Sie können trotzdem einen Bot ausliefern. Wir haben einen ersten Schritt unternommen, um einem KI-Agenten das Gefühl zu geben, sich in einem Projekt zu Hause zu fühlen: Jeder neue wird mit einer AGENTS.md und einer Referenz docs/tgcloud-sdk.md erstellt, die Agenten-Codierungswerkzeuge automatisch lesen.
Zusammen mit einer kleinen, in sich geschlossenen Laufzeit – ein SDK, keine npm-Pakete zum Jonglieren – gibt das dem Assistenten einen schnellen Start bei den Konventionen, die generische Code-Generatoren hier oft übersehen: relative Importe mit .js, keine Fremdschlüssel, jeder db-Aufruf ist asynchron, ein Handler pro Update-Typ und der Zwei-Schritt-push/migrate-Flow.
Versuchen Sie es:
npm create @tgcloud/bot my-bot
cd my-bot
opencode # oder Claude Code, Cursor, … – jeder Agent, der AGENTS.md liest
Fragen Sie dann einfach in Klartext:
Schreibe einen Bot, der sich die To-Do-Listen jeder Person merkt – füge ein Element hinzu, wenn sie Text sendet, und zeige die gesamte Liste an, wenn sie /list sendet.
Der Assistent bearbeitet schema.js und Ihre Handler für Sie; Sie überprüfen, testen eine Änderung sofort mit npx tgcloud run, und gehen dann mit npx tgcloud push und npx tgcloud migrate live. AGENTS.md ist Teil Ihres Projekts – bearbeiten Sie es, wenn der Bot wächst, damit die Anleitungen korrekt bleiben.
Unterwegs mit BotFather
Nur noch auf dem Handy? Das gesamte Projekt lebt auch in @BotFather – öffnen Sie Ihren Bot → Serverless und Sie erhalten alles, was die CLI verwaltet, auf einem Touchscreen:
- Handler – Erstellen, bearbeiten und testen Sie Update-Handler; BotFather hält den Webhook mit den von Ihnen vorhandenen Handlern synchron (das gleiche Synchron / Nicht synchron, das die CLI meldet).
- Endpunkte – Die Funktionen, die Ihre Mini-App aufruft.
- Bibliothek – Ihre gemeinsam genutzten
lib/-Module. - Datenbank – Bearbeiten Sie
schema.jsin der gleichen Drizzle-ähnlichen Syntax, überprüfen Sie ausstehende Änderungen und wenden Sie sie an; Speichern stellt bereit. - Mini-App-Dateien – Das Frontend, das Ihre Benutzer in Telegram öffnen.
- CLI-Zugriff – Holen Sie sich den CLI-Zugriffstoken hier, wenn Sie wieder am Keyboard sind.
Es ist dasselbe Cloud-Projekt, sodass Sie einen Handler auf Ihrem Handy starten und ihn später mit npx tgcloud pull auf Ihren Laptop holen können – nichts ist an einen einzigen Client gebunden. Das Ausführen eines Handlers zeigt sogar seine Konsolenausgabe direkt im Chat an, genau wie
npx tgcloud run
.
Projekte und Module
Ein serverloses Projekt ist ein gewöhnlicher Ordner unter Versionskontrolle. Alles, was die Plattform ausführt, befindet sich in einem Ordner, tgcloud/; der Rest des Projekts gehört Ihnen – ein Mini-App-Frontend, Konfiguration, Dokumentation. Es gibt keinen Build-Schritt für den Bot, keine node_modules zur Laufzeit und keinen Server-Einstiegspunkt.
Aufbau eines Projekts
example_bot/
├─ tgcloud/ # der bereitgestellte Teil – nichts anderes
│ ├─ handlers/ # Update-Handler – flach, nur eine Ebene
│ │ ├─ message.js
│ │ └─ callback_query.js
│ ├─ endpoints/ # Funktionen, die Ihre Mini-App aufruft – flach, nur eine Ebene
│ │ └─ getProfile.js
│ ├─ lib/ # gemeinsam genutzte Module; Unterverzeichnisse erlaubt
│ │ ├─ reply.js
│ │ └─ internal/util.js
│ └─ schema.js # Datenbank-Schema – eine Datei
└─ .tgcloud/ # CLI-Status, git-ignoriert
Nur .js-Dateien innerhalb von tgcloud/ – schema.js, lib/, handlers/, endpoints/ – werden bereitgestellt. Alles andere – Markdown, Konfigurationsdateien, der .tgcloud/-Ordner, alles außerhalb von tgcloud/ – bleibt auf Ihrem Rechner. Modulnamen sind relativ zu tgcloud/. Die Datei tgcloud/handlers/message.js ist das Modul handlers/message, und das ist der Name, den npx tgcloud run verwendet.
Upgrade von einem älteren Projekt? Projekte, die vor CLI 0.2.0 erstellt wurden, behielten
schema.js,lib/undhandlers/im Projektstammverzeichnis. Führen Sienpx tgcloud upgradeeinmal aus, um sie intgcloud/zu verschieben.
schema.js – Ihre Datenbank. Sie deklariert Tabellen als benannte Exporte unter Verwendung der Schema-DSL und liegt als einzelne Datei in tgcloud/. Sie wird wie jedes andere Modul bereitgestellt, aber die Bereitstellung ändert niemals die Datenbank – Schemaänderungen werden separat mit npx tgcloud migrate angewendet. Siehe
Die Datenbank
.
lib/ – gemeinsam genutzter Code, alles, was Sie über Handler hinweg wiederverwenden möchten: reine Hilfsfunktionen, Datenbankzugriffsschichten, Formatierung, Integrationen mit externen Diensten. lib/ ist das einzige Verzeichnis, das Unterverzeichnisse enthalten darf (lib/internal/util.js, lib/payments/stripe.js), sodass Sie eine größere Codebasis nach Belieben organisieren können. Module in lib/ werden von der Plattform nie direkt aufgerufen; sie existieren, um von Handlern und untereinander importiert zu werden.
handlers/ – die Einstiegspunkte Ihres Bots. Jede Datei entspricht einem Telegram-Update-Typ, und die Plattform leitet jedes eingehende Update an den passenden Handler weiter:
| Datei | Behandelt |
|---|---|
handlers/message.js |
Neu eingehende Nachrichten |
handlers/inline_query.js |
Neu eingehende Inline -Abfragen |
handlers/callback_query.js |
Neu eingehende Callback-Abfragen |
| … | Jeder andere Bot API Update-Typ |
handlers/ ist flach – keine Unterverzeichnisse. Der export default eines Handlers ist die Funktion, die die Plattform aufruft (siehe
Handler
).
Ein Update-Typ wird nur dann behandelt, wenn seine Handlerdatei existiert und nicht leer ist. Wenn es kein handlers/<type>.js gibt – oder die Datei leer ist –, werden Updates dieses Typs ignoriert, und die Plattform führt nichts für sie aus. Bewahren Sie also nur die Handler auf, die Sie tatsächlich benötigen: Jeder, den Sie hinzufügen, ist ein weiterer Update-Typ, zu dem Ihr Bot aufwacht, um ihn zu verarbeiten, und wenn Sie die restlichen weglassen, startet Telegram Ihren Code nicht für Updates, die Sie sowieso verwerfen würden.
Um einen neuen Handler zu generieren, führen Sie
npx tgcloud add handlers/<type>aus.
endpoints/ – die Serverseite Ihrer Mini-App. Jede Datei ist eine Funktion, die die Mini-App nach Namen aufruft, sodass endpoints/getProfile.js auf POST /api/getProfile antwortet. Die Plattform prüft, ob der Aufruf von einem Telegram-Benutzer innerhalb Ihrer Mini-App stammt, bevor Ihr Code ausgeführt wird, und übergibt Ihnen diesen Benutzer. endpoints/ ist flach, wie handlers/. Siehe
Endpunkte
.
.tgcloud/ – maschinenlokaler Status, der vollständig von der CLI verwaltet wird, einschließlich Ihrer gespeicherten Anmeldedaten. Er wird von Git ignoriert, und Sie sollten ihn nie von Hand lesen oder schreiben – verwenden Sie stattdessen die CLI-Befehle.
Das Modulsystem
Zur Laufzeit kann ein Modul genau zwei Dinge sehen: das Plattform-SDK und die anderen Module in Ihrem Projekt. Es gibt keine npm-Pakete, kein Dateisystem und kein Netzwerk außer über den fetch des SDK.
Importieren Sie Ihre eigenen Module so, wie Sie es in jedem JavaScript-Projekt tun würden – per relativem Pfad, mit der .js-Erweiterung – und das SDK nach seinem Namen:
// tgcloud/handlers/message.js
import { users } from '../schema.js'; // das Schema-Modul
import { addItem } from '../lib/cart.js'; // ein Lib-Modul
import { format } from '../lib/internal/fmt.js'; // verschachteltes Lib-Modul
import { db, api, fetch } from 'sdk'; // das Plattform-SDK
// tgcloud/lib/cart.js
import { format } from './internal/fmt.js'; // ein Nachbar
import { users } from '../schema.js';
Ein relativer Import muss mit .js enden und darf nicht außerhalb von tgcloud/ führen:
import { users } from '../schema'; // .js hinzufügen
import config from '../../config.js'; // außerhalb von tgcloud/
import { z } from 'zod'; // keine npm-Pakete
Jedes Modul hat auch einen Namen – seinen Pfad innerhalb von tgcloud/ ohne die Erweiterung, z. B. schema, lib/cart oder handlers/message. Dieser Name ist das, was npx tgcloud run und die Ausgabe der CLI verwenden, und ein Modul kann auch unter diesem Namen importiert werden (from 'lib/cart', keine Erweiterung). Bevorzugen Sie relative Importe in Ihrem Code. Sie sind das, was Ihr Editor versteht – Sprung zur Definition, Autovervollständigung, Umbenennung.
Was zur Laufzeit verfügbar ist, sind genau zwei Dinge: sdk und seine Untermodule (sdk/db, sdk/api, sdk/fetch) – die gesamte Plattformoberfläche, siehe
Das SDK
– und Ihre eigenen Module in tgcloud/. Das ist die vollständige Liste. Wenn Ihr Code etwas anderes importiert, wird es nicht aufgelöst. Diese Einschränkung hält die Module schnell zu laden und sicher auszuführen.
Handler
Ein Handler ist ein Modul in handlers/, dessen Standardexport die Plattform aufruft, wenn ein passendes Update eintrifft.
// handlers/message.js
import { api } from 'sdk';
export default async function (message) {
await api.sendMessage({
chat_id: message.chat.id,
text: `Du hast gesagt: ${message.text ?? '(kein Text)'}`,
});
}
Ein Handler erhält die Nutzlast des Updates als Argument – die Plattform entpackt das Telegram
Update
für Sie. handlers/message.js erhält die
Nachricht
(d.h. update.message); handlers/callback_query.js erhält die
CallbackQuery
; und so weiter. Das zweite Argument des Handlers ist ein pro-Aufruf-Kontextobjekt, ctx. Es trägt das rohe Update als ctx.update – greifen Sie darauf zu, wenn Sie etwas außerhalb der Nutzlast benötigen, wie z.B. update_id.
Ein Handler kann async sein (ist es normalerweise) und einen Wert zurückgeben. Er erreicht die Bot-API, die Datenbank und ausgehende HTTP-Anfragen über das SDK.
Sie benötigen kein echtes Update, um einen Handler zu testen. npx tgcloud run führt ihn auf der Plattform mit der von Ihnen bereitgestellten Nutzlast und Ihrem aktuellen lokalen Code aus:
npx tgcloud run handlers/message '{ chat: { id: 1 }, text: "hi" }'
Das Argument ist die Nutzlast – dasselbe Objekt, das Ihr Handler erhält – in JSON5. Um den ctx (sein zweites Argument) des Handlers bereitzustellen, fügen Sie --ctx hinzu, z.B. --ctx '{ update: { update_id: 1 } }'. Dies läuft gegen Ihre lokalen Dateien, sodass Sie Änderungen ausprobieren können, bevor Sie sie bereitstellen. Siehe
run
.
Endpunkte
Ein Endpunkt ist ein Modul in endpoints/, das Ihre Mini-App aufruft – der Ort für alles, was das Frontend nicht allein tun kann, wie z. B. das Lesen und Schreiben der Datenbank, das Aufrufen der Bot-API oder die Kommunikation mit externen Diensten mit Ihren Geheimnissen.
// 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 nicht gefunden', { userId: user.id });
return profile;
}
Das erste Argument, input, ist das JSON-Objekt, das die Mini-App gesendet hat. Das zweite, ctx, enthält ctx.initData – die
Init-Daten
der Mini-App (user, chat, start_param, auth_date, …) , die bereits von der Plattform verifiziert wurden. Sie haben die gleiche Form wie Telegram.WebApp.initDataUnsafe auf dem Client, aber hier können Sie ihr vertrauen. Ein Aufruf ohne gültige Init-Daten wird mit 401 abgelehnt, bevor Ihr Code ausgeführt wird, sodass ctx.initData.user wirklich die Person ist, die Ihre Mini-App geöffnet hat. Endpunkte können nur auf diese Weise aufgerufen werden – von der Mini-App Ihres Bots, durch einen Telegram-Benutzer –, sodass es keine öffentliche API zu schützen und keine Anmeldung zu erstellen gibt.
Was auch immer der Endpunkt zurückgibt, wird als JSON zurückgegeben. Um einen Aufruf abzulehnen, throw new EndpointError(description, parameters?). Die Mini-App erhält die Beschreibung und das parameters-Objekt, wie sie sind. Wenn Sie auf die Art des Fehlers im Frontend verzweigen müssen, legen Sie einen Code in parameters ab – { code: 'OUT_OF_STOCK' }. Jede andere Ausnahme wird als generischer interner Fehler gemeldet, und die eigentliche Nachricht geht nur an Ihre Protokolle, sodass interne Fehler nicht nach außen an den Client dringen.
Einen Endpunkt von der Mini-App aufrufen
Die Mini-App ruft einen Endpunkt mit seinem Namen über Telegram.WebApp.Serverless.call aus dem offiziellen
Mini Apps SDK
, telegram-web-app.js – dem Skript, das Ihre Mini-App bereits enthält – auf. Es sendet die Init-Daten für Sie und ruft mit einem Fehler oder dem Ergebnis des Endpunkts zurück:
Telegram.WebApp.Serverless.call('getProfile', { lang: 'en' }, function (err, profile) {
if (err) {
// err.message – die Beschreibung; err.parameters – was Ihr EndpointError trug
if (err.type === 'ENDPOINT_ERROR') showMessage(err.message);
return;
}
render(profile);
});
Die Eingabe ist optional und muss ein normales Objekt sein. Ohne sie ist der Aufruf Serverless.call('getProfile', callback).
err.type ist 'ENDPOINT_ERROR', wenn Ihr Endpunkt einen EndpointError ausgelöst hat. Jeder andere Wert bedeutet, dass die Plattform den Aufruf abgelehnt hat oder der Endpunkt unerwartet fehlgeschlagen ist, z.B. 'UNAUTHORIZED', wenn die Init-Daten fehlen oder ungültig sind.
Endpunktnamen sind wie JavaScript-Bezeichner – Buchstaben, Ziffern und _, beginnend nicht mit einer Ziffer (getProfile, place_order). Generieren Sie einen mit
npx tgcloud add endpoints/<name>
und versuchen Sie ihn ohne die Mini-App mit
run
aus, indem Sie die Init-Daten selbst übergeben:
npx tgcloud run endpoints/getProfile '{}' --ctx '{ initData: { user: { id: 1, first_name: "Ann" } } }'
Was bereitgestellt wird
Wenn Sie npx tgcloud push ausführen, sammelt die CLI jede .js-Datei unter schema.js, lib/, handlers/ und endpoints/ und sendet diesen exakten Satz als Modulraum Ihres Projekts – zusammen mit dem Build-Ordner der Mini-App, wenn tgcloud.jsonc einen benennt (siehe unten). Alles, was in der Cloud vorhanden, aber nicht in Ihrem Projekt ist, wird entfernt, sodass der bereitgestellte Zustand immer Ihrem Ordner entspricht – auch Löschungen. Nichts außerhalb von tgcloud/ wird als Code bereitgestellt – der Rest des Projektstamms gehört Ihnen. Innerhalb von tgcloud/ wird eine .js-Datei, die sich nicht an diesen Orten befindet (tgcloud/util.js, tgcloud/helpers/x.js), markiert, sodass sie nicht stillschweigend nicht bereitgestellt wird. Markdown, Punktdateien und .tgcloud/ werden niemals bereitgestellt.
Mini-App Frontend
Der Projektstamm gehört Ihnen und ist der natürliche Ort für das Frontend Ihrer Mini-App – erstellt mit React, Vue, Svelte oder einfachem HTML, von Vite oder jedem anderen Bundler. Serverless hostet seine Build-Ausgabe neben Ihrem Bot. Ein push stellt den Code und die statischen Dateien zusammen bereit, und die App wird unter https://app<app_id>.tgcloud.ai/ bereitgestellt ( push gibt die genaue Adresse aus – verwenden Sie sie als URL Ihrer Mini-App in
@BotFather
).
Missbrauch melden. Mini-Apps auf tgcloud.ai werden von unabhängigen Entwicklern veröffentlicht. Um illegale Inhalte oder Missbrauch auf einer
app<app_id>.tgcloud.ai-Subdomain zu melden, senden Sie eine E-Mail an [email protected] .
Um zu beginnen, erstellen Sie zuerst das Frontend mit dem Werkzeug Ihrer Wahl und fügen Sie dann Serverless darauf auf:
npm create vite@latest example_app # oder create-vue, …
cd example_app
npm create @tgcloud/bot .
Der Projekt-Ersteller überschreibt Ihre Dateien niemals. Er fügt den tgcloud/-Ordner neben dem Frontend hinzu, erkennt den Build-Ordner und fügt ein npm run deploy-Skript hinzu, das die App erstellt und zusammen mit dem Bot bereitstellt.
Was gehostet wird, ist in tgcloud.jsonc festgelegt, der Konfigurationsdatei des Projekts im Projektstamm:
{
"$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 }]
}
}
Nur "source" ist erforderlich. Es ist der Build-Ordner, relativ zum Projektstamm. Die anderen Schlüssel von "static" konfigurieren, wie er bereitgestellt wird (unten). init erstellt tgcloud.jsonc für Sie. Wenn es das Frontend erkennt, füllt es auch "source" aus:
| Frontend | init setzt "source" auf |
|---|---|
| Vite – React, Vue, Svelte, Solid, … | dist |
| Next.js, Nuxt, SvelteKit, React Router, TanStack Start, Angular | nichts – konfigurieren Sie zuerst einen statischen Export und setzen Sie dann "source" auf dessen Ordner selbst |
Mit jeder anderen Einrichtung setzen Sie "source" auf Ihren Build-Ordner selbst.
Die Zeile $schema gibt Ihrem Editor Autovervollständigung und Validierung für jeden Schlüssel. Die Datei ist JSON mit Kommentaren und erlaubten abschließenden Kommas. Wenn ein anderes Werkzeug sie als reines JSON lesen muss, nennen Sie sie stattdessen tgcloud.json – dann muss sie striktes JSON sein.
Vor dem Pushen bauen. push lädt den Ordner exakt so hoch, wie er auf der Festplatte liegt – außer Dateien und Ordnern, deren Namen mit einem Punkt beginnen und die übersprungen werden –, und führt niemals Ihren Build aus, sodass ein einfaches npx tgcloud push nach dem Bearbeiten des Frontends den vorherigen Build bereitstellen würde. Verwenden Sie npm run deploy (es führt Ihr build-Skript aus, dann tgcloud push), oder bauen Sie es zuerst selbst.
Die Bereitstellungsoptionen:
spa– liefertindex.htmlfür Pfade, die nicht existieren und keine Dateierweiterung haben, sodass ein Client-seitiger Router beim Neuladen funktioniert. Pfade mit einer Erweiterung erhalten immer noch404.immutable– Muster für inhaltsgehashte Dateien (Vites/assets/*), die Browser ein Jahr lang cachen dürfen. Verwenden Sie es nur für inhaltsgehashte Dateien. Jede andere hier übereinstimmende Datei würde ein Jahr lang in Browsern veraltet bleiben, und es gibt keine Möglichkeit, sie zurückzurufen.headers– Antwort-Header für die Pfade, auf die eine Regel passt (ein exakter Pfad oder ein Präfix, das auf/*endet). Einnull-Wert entfernt einen Header.redirects– Weiterleitungsregeln, bei denen die erste Übereinstimmung gewinnt. Eine vorhandene Datei schlägt immer eine Regel.statusist301, es sei denn, Sie legen ihn fest."static": "dist"– eine Abkürzung für{ "source": "dist" }, wenn Sie nichts weiter benötigen."static": false– entfernt die bereitgestellte Website beim nächsten Push. Das Löschen des Schlüssels verhindert stattdessen, dass dieses Projekt statische Dateien verwaltet, und was auch immer bereitgestellt ist, bleibt.
Die Website wird vom Stamm ihres eigenen Hosts bereitgestellt, daher ist die Standardbasis (/) Ihres Bundlers korrekt. Telegram Web öffnet Mini-Apps in einem iFrame, also fügen Sie keine X-Frame-Options oder eine restriktive frame-ancestors-Richtlinie hinzu.
Derselbe Host beantwortet Ihre
Endpunkte
unter /api/ – dort geht Telegram.WebApp.Serverless.call('getProfile', …) hin. Legen Sie keine eigenen Dateien unter /api/ im Build-Ordner ab.
Die Datenbank
Jeder Bot erhält seine eigene Datenbank – ein SQLite-gestütztes Speichersystem, das zwischen den Aufrufen erhalten bleibt und über db für jedes Modul verfügbar ist. Sie beschreiben Ihre Tabellen in schema.js mit einer kleinen, typisierten DSL; Sie lesen und schreiben sie mit einem flüssigen Query-Builder; und Sie entwickeln sie mit geprüften Migrationen weiter.
Kennen Sie Drizzle ORM ? Dann wissen Sie bereits, wie Sie mit der Datenbank hier sprechen. Die Schema-DSL und der Query-Builder folgen Drizzle – die Spalten-Builder,
select().from().where(), die Operatoren,onConflictDoUpdate,.returning()und dersql-Tag verhalten sich alle so, wie Sie es erwarten, sodass das Lesen und Schreiben von Daten die vertraute API ist, die Sie bereits verwenden. Sie importieren es einfach aussdk/db, und ein paar Plattform-spezifische Elemente (am bemerkenswertesten keine Fremdschlüssel ) werden dort darauf hingewiesen, wo sie auftreten.
Tabellen deklarieren
Tabellen sind benannte Exporte in schema.js. Das Aufrufen von table() erstellt zur Ladezeit eine Beschreibung (es berührt die Datenbank nicht); die Plattform entdeckt die exportierten Tabellen, wenn Sie schema.js bereitstellen und migriert die Datenbank entsprechend.
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?):
nameist der SQL-Tabellenname;columnsist eine Map von JS-Eigenschaft → Spaltendefinition;extrasist ein optionaler Callback(t) => ({ … }), bei demtdie Spalten exponiert (t.createdist ein Verweis auf diese Spalte) – deklarieren Sie hier Indizes und tabellenweite Einschränkungen.
Spaltentypen
| Factory | SQLite-Typ | Hinweise |
|---|---|---|
text() |
TEXT | |
integer() |
INTEGER | |
real() |
REAL | Alias float() |
numeric() |
NUMERIC | |
blob() |
BLOB | liest/schreibt Uint8Array |
boolean() |
INTEGER | als 0/1 gespeichert, als true/false gelesen |
json() |
TEXT | automatisch JSON.stringify / JSON.parse |
Der Spaltennamen-Argument ist optional – lassen Sie es weg, und der JS-Schlüssel wird verwendet. Die Option
modesteuert, wie Werte zwischen SQLite und JavaScript konvertiert werden.
mode |
Gespeichert als | JS-Wert |
|---|---|---|
boolean |
INTEGER 0/1 | boolean |
json |
TEXT (JSON) | beliebiges Objekt/Array |
timestamp |
INTEGER (Unix-Sekunden) | Date |
timestamp_ms |
INTEGER (Unix-ms) | Date |
bytes |
BLOB | Uint8Array |
boolean() und json() sind Kurzformen für integer(name, { mode: 'boolean' }) und text(name, { mode: 'json' }).
Ein blob() liest und schreibt ein Uint8Array – die Laufzeit hat keinen Node Buffer (und ein Buffer ist eine Uint8Array-Unterklasse, daher ist dies der portable Basistyp). Der obige mode steuert nur, wie ein Wert kodiert wird, unabhängig vom Speichertyp der Spalte: also ist blob('col', { mode: 'json' }) das BLOB-Gegenstück zu json() – dieselbe JSON-Kodierung, gespeichert in einer BLOB-Spalte anstelle von TEXT.
TL;DR:
blob()verwendetUint8Array.modesteuert die Kodierung, nicht die Speicherung, sodassblob(..., { mode: 'json' })JSON als BLOB speichert, währendjson()es als TEXT speichert.
Spaltenmodifikatoren
Spaltenmodifikatoren werden an eine Spalte angehängt.
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' }) // oder 'virtual'
text('email').deprecated('ersetzt durch login') // markiert die Spalte zur Entfernung
.default()bei einerjson()-Spalte kodiert den Wert für Sie; einsql`…`default wird unverändert durchgereicht..deprecated()ist terminal – siehe Migrationen .
Indizes und Einschränkungen
Indizes und Einschränkungen werden im extras-Callback deklariert, wo die Spalten im Geltungsbereich sind.
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})`) , // Ausdrucksindex
active: index('idx_active').on(t.userId).where(sql`done = 0`) , // partieller Index
}));
Tabellenweite Modifikatoren werden nach table(...) angehängt: .strict(), .withoutRowid(), .deprecated('reason').
Keine Fremdschlüssel
Die Laufzeit wird mit PRAGMA foreign_keys ausgeschaltet. Ein deklarierter Fremdschlüssel wäre stillschweigend inert – keine Kaskaden, kein Waisenkindschutz – was schlechter ist als gar keiner, daher macht die DSL es unmöglich. Insbesondere .references() und tabellenweite foreignKey() werfen eine Ausnahme bei der Deklaration, und ein Schema, das sie verwendet, wird nicht bereitgestellt.
Sie sollten Beziehungen mit einfachen Spalten (userId: integer('user_id')) modellieren und die Integrität in Ihrem Anwendungscode erzwingen: Eltern vor Kindern einfügen, Kinder vor Eltern löschen, Fehler ordnungsgemäß behandeln und Waisen mit einem LEFT JOIN … WHERE parent.id IS NULL aufkehren, wenn Sie müssen.
Das Fehlen von Fremdschlüsseln ist eine bewusste Einschränkung, kein Versehen. Berücksichtigen Sie dies frühzeitig bei der Planung Ihres Bots.
Abfragen
db ist ein flüssiger Query-Builder. Jede Abfrage ist asynchron – die Terminalmethoden (.all(), .get(), .values(), .run()) geben Promises zurück, also immer 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(); // alle Zeilen
await db.select().from(todos).where(eq(todos.id, 1)).get(); // erste Zeile oder null
await db.select().from(todos).values(); // Zeilen als Wert-Arrays
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();
// benutzerdefinierte Projektion: { 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();
// Zeilenanzahl – ein Helfer, kein Builder-Terminal:
await db.$count(todos); // alle Zeilen
await db.$count(todos, eq(todos.done, false)); // mit Filter
- Verkettbar:
.where(),.orderBy(),.limit(),.offset(),.groupBy(),.having(),.distinct(). - Terminals:
.all(),.get(),.values(). - Zeilen mit
db.$count(table, where?)zählen (odercount()in einer Projektion).
Einfügen, Aktualisieren, Löschen:
await db.insert(todos).values({ userId: 1, text: 'Milch kaufen' }).run();
await db.insert(todos).values([{ text: 'A' }, { text: 'B' }]).run(); // Stapel
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();
Beachten Sie, dass ein Stapel-Insert ein einziges Statement ist, daher ist er durch das Variablenlimit von SQLite begrenzt (Zeilen × Spalten); wenn Sie es überschreiten, schlägt der Insert fehl – teilen Sie ihn selbst auf.
Operatoren werden aus sdk/db importiert:
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) mit mehreren Argumenten ist dasselbe wie and(a, b). Das zweite Argument eines Vergleichs ist standardmäßig ein Wert, kann aber auch eine andere Spalte oder sql`…` sein – z.B. eq(a.x, b.y). Aggregierte Funktionen (count/sum/avg/min/max) sind SQL-Fragmente für eine .select({ … })-Projektion.
Rohes SQL – wenn der Builder nicht ausreicht, wechseln Sie zu rohem SQL. Der Modus wird per Methode gewählt: db.run für Schreibvorgänge, db.all für viele Zeilen, db.get für eine Zeile, db.values für Zeilen als positionelle Arrays.
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, 'milch kaufen']]
Ein Schreibvorgang wird zu einem run-Ergebnis aufgelöst – rowsAffected, lastInsertRowid und rows, die eine RETURNING-Klausel enthalten, wenn das SQL eine hatte. Greifen Sie auf db.values zu, wenn eine Abfrage zwei Spalten mit demselben Namen projiziert (ein Join wie SELECT u.id, o.id): db.all schlüsselt Zeilen nach Spaltennamen auf, sodass die zweite id die erste überschreiben würde.
Der sql`…` -Tag verwandelt ${value} in einen gebundenen Parameter und ${table.column} in einen Bezeichner und fügt verschachtelte sql-Fragmente ein. Verwenden Sie sql.raw('…') für einen Literal ohne Parameter.
Rohe Abfragen sind nicht an eine Tabelle gebunden, daher werden ihre Zeilen ohne Moduskonvertierung zurückgegeben – ein boolescher Wert ist
0/1, eine JSON-Spalte ist eine Zeichenkette, ein Zeitstempel ist eine Zahl. Nur der tabellengebundene Builder konvertiert Werte.
Migrationen
Ihre Datenbank ändert sich, wenn Ihr Bot wächst. Die Plattform hält dies sicher, indem sie das Bereitstellen von Code von der Änderung von Daten trennt und jede Schemaänderung nach ihrer Risikoeinstufung klassifiziert.
Das Bereitstellen ändert die Datenbank niemals. Wenn Sie eine geänderte schema.js mit npx tgcloud push bereitstellen, zeichnet die Plattform das neue Schema auf und informiert Sie darüber, wie sich die Datenbank ändern würde – wendet aber nichts an:
npx tgcloud push # schema.js bereitstellen; meldet ausstehende DB-Änderungen
npx tgcloud migrate # sie überprüfen und anwenden
npx tgcloud migrate berechnet den Unterschied zwischen Ihrem Schema und der Live-Datenbank und führt Sie Schritt für Schritt durch, mit einem laufenden [N/M]-Zähler:
- Eine kurze Zusammenfassung aller ausstehenden Änderungen.
- Sichere Änderungen, die auf Ihre Bestätigung hin zusammen in einem Schritt angewendet werden.
- Warnungen (Löschungen, langsame Operationen), einzeln, jede separat bestätigt.
- Manuelle Änderungen, angezeigt mit einem Grund und einem vorgeschlagenen Vorgehen, nicht automatisch angewendet.
- Undokumentierte Objekte (in der Datenbank, aber nicht in Ihrem Schema), zur Kenntnisnahme angezeigt.
Es endet mit einer Zusammenfassung: angewendet, übersprungen, auf manuelle Korrektur wartend, nicht im Schema.
Siehe
migratefür die Flags (--dry-run,--safe,--yes,--local).
Dinge entfernen
Das Löschen einer Tabelle oder Spalte aus schema.js löscht sie nicht – das würde eine versehentliche Löschung katastrophal machen. Um etwas zu entfernen, markieren Sie es als veraltet:
// eine Spalte löschen
text('email').deprecated('ersetzt durch login')
// eine ganze Tabelle löschen
export const oldSessions = table('old_sessions', { /* … */ }).deprecated('unused');
Beim nächsten migrate erscheint das veraltete Objekt als Warnung-Status-Löschung, die Sie einzeln bestätigen. Sobald es weg ist, entfernen Sie die Deklaration.
Spaltentyp ändern
Typänderungen sind manuell – SQLite kann sie nicht immer vor Ort durchführen, und das Erzwingen bestehender Werte ist eine Ermessensentscheidung. migrate zeigt die Änderung und ihre Begründung an; führen Sie sie selbst mit rohem SQL (db.run(...)) durch, typischerweise durch Erstellen einer neuen Spalte oder Tabelle, Kopieren von Daten und Austauschen.
Das SDK
Zur Laufzeit hat ein Modul eine Bibliothek: sdk. Sie bündelt die drei Dinge, die ein Bot-Backend benötigt – eine Datenbank, die Telegram Bot API und ausgehende HTTP-Anfragen – ohne Installation und ohne Konfiguration von Anmeldedaten. Die Datenbank (db) wird in
Die Datenbank
behandelt; dieser Abschnitt behandelt api, fetch und das globale console.
import { db, api, fetch, InputFile, BotApiError, EndpointError } from 'sdk'; // die gesamte Oberfläche
// oder aus Untermodulen:
import { table, integer, text, eq, sql } from 'sdk/db';
import { api } from 'sdk/api';
import { fetch } from 'sdk/fetch';
| Import | Was es ist |
|---|---|
db |
Datenbank – Query-Builder und Schema-DSL → Die Datenbank |
api |
Telegram Bot API – api.sendMessage(...) →
unten
|
fetch |
Ausgehende HTTP-Anfragen → unten |
InputFile |
Eine Datei (Bytes + Name) zum Hochladen – funktioniert sowohl mit api als auch mit fetch →
Dateien
|
EndpointError |
Lehnt den Aufruf einer Mini-App mit einer Nachricht ab, die sie anzeigen kann → Endpunkte |
Importieren Sie Ihre eigenen Projektmodule per relativen Pfad mit der .js-Erweiterung (from '../schema.js', from '../lib/cart.js'). Siehe
Das Modulsystem
.
Die Bot API
api bietet Ihnen die gesamte
Telegram Bot API
. Rufen Sie jede Methode als api.<method>(params) auf. Jede aktuelle – und zukünftige – Bot-API-Methode funktioniert ohne SDK-Update.
import { api } from 'sdk';
const me = await api.getMe(); // → das entpackte Ergebnis
await api.sendMessage({ chat_id: id, text: 'Hallo!' });
await api.editMessageText({ chat_id, message_id, text: 'Aktualisiert' });
await api.answerCallbackQuery({ callback_query_id, text: 'Fertig' });
Die Antworthülle ist entpackt. Die Bot-API wickelt Ergebnisse normalerweise in { ok: true, result: … } ein. api gibt das result direkt zurück – getMe() wird zum Benutzerobjekt aufgelöst, nicht zu einer Hülle. Parameter verwenden die eigenen Namen der Bot-API im Snake-Case (chat_id, message_id, reply_markup, …).
Fehler lösen BotApiError aus. Wenn die Bot-API { ok: false } zurückgibt, löst der Aufruf stattdessen eine BotApiError aus, anstatt einen falsch positiven Wert zurückzugeben, sodass Sie ihn nicht versehentlich ignorieren können. Der Fehler trägt .code (der Bot API error_code) , .description (die menschenlesbare Nachricht) , .method (welche Methode fehlgeschlagen ist) und .parameters (zusätzliche Daten wie retry_after bei einer 429 oder migrate_to_chat_id). Fangen Sie sie ab, um einen erwarteten Fehler zu behandeln, und werfen Sie den Rest erneut:
import { api, BotApiError } from 'sdk';
try {
await api.deleteMessage({ chat_id, message_id });
} catch (e) {
if (e instanceof BotApiError && e.code === 400) {
// 400 = die Nachricht ist bereits weg; das ist hier in Ordnung.
} else {
throw e;
}
}
Dateien
Arbeiten Sie mit Bytes von Dateien, nicht nur mit file_ids – laden Sie neue Dateien hoch und laden Sie vorhandene herunter.
Hochladen – InputFile. Wickeln Sie rohe Bytes in ein InputFile (einen einfachen Container: Bytes, ein Dateiname und ein optionaler MIME-type) und übergeben Sie es überall dort, wo eine Bot-API-Methode eine Datei erwartet:
import { api, InputFile } from 'sdk';
const doc = new InputFile(bytes, 'bericht.pdf', { type: 'application/pdf' });
await api.sendDocument({ chat_id, document: doc });
// Dateien, die in Parametern verschachtelt sind, funktionieren auch – z.B. ein 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' }) },
]});
Die Bytes werden als echter Multipart-Upload an Telegram gestreamt – die Datei wird nicht als Ganzes in einem einzigen Aufruf an die Plattform gepuffert –, aber die Limits der Telegram Bot API gelten immer noch: Ein Bot kann derzeit bis zu 50 MB pro Datei hochladen (und 10 MB für ein Foto).
Herunterladen – nach file_id. Zwei Helfer ziehen die Bytes einer Datei zurück. Die Plattform löst die file_id für Sie auf (sie führt getFile aus und ruft den Inhalt im Hintergrund ab), sodass Sie nie den Bot-Token oder eine Download-URL berühren:
const bytes = await api.getFileContent(file_id); // → Uint8Array (die ganze Datei)
const { file, body } = await api.getFileStream(file_id); // stattdessen streamen
file.file_unique_id; // die getFile-Infos
for await (const chunk of body) { /* Uint8Array */ } // oder puffern: await stream.bytes()
Telegrams Bot API limitiert
getFile-Downloads auf 20 MB, das ist also die Obergrenze für beide Helfer.
HTTP
fetch ist ein fetch-ähnlicher Client zum Aufrufen der Außenwelt – Drittanbieter-APIs, Webhooks, alles über 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();
Die Antwort spiegelt die Webplattform wider: res.status, res.statusText, res.ok (true für 200-299), res.url, res.headers (.get(), .has(), .keys(), .entries()) und Body-Leser await res.json() / await res.text().
Sie können den Body auch inkrementell als Stream lesen –
for await (const chunk of res.body) { … }– so konsumieren Sie serverseitig gesendete Ereignisse oder Token-für-Token-Ausgaben von KI-APIs.
Body-Helfer setzen den passenden Content-Type für Sie:
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
Anforderungs-Bodies können auch Dateien und rohe Bytes tragen: übergeben Sie ein Uint8Array, ein InputFile oder ein FormData (für multipart/form-data) als body, und die Bytes werden gestreamt – so laden Sie zu einer Drittanbieter-API hoch, genauso wie Sie Dateien an Telegram senden:
import { fetch, FormData, InputFile } from 'sdk';
const form = new FormData();
form.append('file', new InputFile(bytes, 'foto.jpg', { type: 'image/jpeg' }));
form.append('caption', 'von meinem Bot');
const res = await fetch('https://api.example.com/upload', { method: 'POST', body: form });
Es verhält sich ansonsten wie der Standard fetch, den Sie bereits kennen, mit zwei Einschränkungen:
- Die Antwort wird als Text gelesen –
await res.json()/await res.text()oder chunk-weise gestreamt mitres.body. - Die Gesamtantwort ist auf 30 MB begrenzt. Diese Grenze deckt die gesamte Antwort ab – das Streaming mit
res.bodyermöglicht es Ihnen, einen großen Body inkrementell zu verarbeiten, aber es hebt das Limit nicht auf.
Protokollierung
Die Standard-Global-Konsole console ist verfügbar – nichts muss importiert werden, sie ist einfach wie in jedem JavaScript da. Ihre Ausgabe wird erfasst und von npx tgcloud run angezeigt, was sie zu Ihrem primären Debugging-Werkzeug während der Entwicklung macht.
console.log('verarbeite', { chatId: id }); // log / debug – normal
console.info('gestartet'); // info – blau
console.warn('rate limited'); // warn – gelb
console.error(err); // error – rot, mit Stack-Trace
Jede Zeile ist mit dem [file:line] markiert, von dem sie stammt. console.error und console.trace fügen einen vollständigen Stack hinzu, während console.warn dies nicht tut. Wenn Sie ein Modul mit npx tgcloud run ausführen, werden diese Zeilen mit einem farbigen Präfix pro Ebene, der Zeit seit Beginn der Ausführung und dem Ursprung angezeigt – siehe
run
.
Befehlszeilenschnittstelle
tgcloud ist die Brücke zwischen Ihrem Projektordner und der Cloud. Sie erstellt Projektgerüste, zeigt Ihnen, was sich geändert hat, stellt bereit, führt Module aus und wendet Datenbankmigrationen an. Sie benötigt
Node.js
18 oder neuer. Zwei Möglichkeiten, sie zu erhalten:
# Empfohlen – erstellen Sie ein Projekt mit der darin installierten CLI:
npm create @tgcloud/bot example_bot
# Oder installieren Sie die CLI global und initialisieren Sie einen leeren Ordner:
npm install -g @tgcloud/cli
tgcloud init
Das npm-Paket heißt @tgcloud/cli, und der daraus installierte Befehl ist tgcloud. Jeder Befehl funktioniert von jedem Unterordner Ihres Projekts aus.
| Befehl | Zweck |
|---|---|
init
|
Erstellt ein neues Projektgerüst im aktuellen Ordner |
add
|
Erstellt ein neues Modulgerüst (einen Handler, einen Endpunkt oder ein Lib-Modul) |
login
|
Verknüpft das Projekt mit einem Bot (speichert den Token) |
status
|
Zeigt an, was sich lokal im Vergleich zur Cloud geändert hat |
diff
|
Zeigt die zeilenweisen Änderungen an |
push
|
Stellt geänderte Module und das Build der Mini-App in der Cloud bereit |
migrate
|
Wendet Schemaänderungen auf die Datenbank an |
run
|
Führt ein Modul auf der Plattform aus, ohne es bereitzustellen |
fetch
|
Prüft, was bereitgestellt ist, ohne lokale Dateien zu ändern |
pull
|
Bringt lokale Dateien mit der Cloud in Einklang |
reset
|
Verwirft lokale Änderungen; stellt vom Cloud-Status wieder her |
webhook
|
Überprüft und synchronisiert den von der Plattform verwalteten Webhook |
completion
|
Gibt ein Shell-Vervollständigungsskript aus (bash/zsh/fish) |
upgrade
|
Verschiebt ein Projektlayout vor 0.2.0 in den Ordner tgcloud/ |
Authentifizierung
Ein Projekt wird durch seinen Token an einen Bot gebunden, der das Format app<id>:<secret> hat. Der Teil app<id> ist öffentlich und darf ausgegeben werden; das Geheimnis erscheint nie in Protokollen oder Fehlern.
Der Token wird in dieser Reihenfolge aufgelöst:
TGCLOUD_TOKENUmgebungsvariable – für CI; niemals auf die Festplatte geschrieben..tgcloud/credentials– geschrieben vonnpx tgcloud login.- Keines davon -> Ein Fehler, der Sie auf
npx tgcloud loginhinweist.
Die CLI fragt nie mitten im Befehl nach einem Token. Der Login ist immer der explizite login-Schritt. Wenn ein gespeicherter Token ungültig wird, entfernt die CLI ihn und fordert Sie auf, sich erneut anzumelden.
init
npx tgcloud init
Erstellt ein neues Projektgerüst im aktuellen Verzeichnis: den tgcloud/-Ordner mit schema.js, lib/, handlers/ und einem Starter-Handler, plus tgcloud.jsonc, AGENTS.md, docs/ und den .tgcloud/-Statusordner. Führen Sie es im Stammverzeichnis eines bestehenden Frontend-Projekts aus – es fügt den tgcloud/-Ordner und die Dokumentation hinzu und überschreibt niemals Ihre Dateien. Wenn es das Frontend erkennt, verweist es auch tgcloud.jsonc auf den Build-Ordner und fügt ein deploy-Skript (build, dann push) zu Ihrer package.json hinzu – nur, wenn Sie noch kein deploy-Skript haben. Siehe
Mini-App Frontend
.
init weigert sich, ein Projekt innerhalb eines anderen Projekts zu erstellen. Das erneute Ausführen im Stammverzeichnis eines Projekts ist in Ordnung und fügt einfach alles hinzu, was fehlt.
add
npx tgcloud add <target>
Erstellt ein einzelnes neues Modulgerüst, das verkabelt und bereit zur Bearbeitung ist – ein Handler, ein Endpunkt oder ein lib/-Modul.
npx tgcloud add handlers/callback_query # ein neuer Update-Handler
npx tgcloud add endpoints/getProfile # ein neuer Endpunkt für die Mini-App
npx tgcloud add lib/cart # ein neues gemeinsam genutztes Modul
Beachten Sie, dass
addniemals eine vorhandene Datei überschreibt.
Das <target> ist der Pfad des Moduls (eine nachgestellte .js ist optional). Für handlers/ muss der Name ein Telegram-Update-Typ sein. Die Plattform bewirbt die gültige Menge, sodass ein ungültiger Name sofort abgelehnt wird. handlers/ und endpoints/ sind flach, während lib/ verschachtelt sein kann (lib/payments/stripe).
Der Modulname ist erforderlich. Das Übergeben nur des Verzeichnisses ist ein Fehler – aber ein hilfreicher: für handlers/ listet es die Update-Typen auf, die Sie noch nicht haben, sodass Sie einen kopieren können.
$ npx tgcloud add handlers
Error: Specify a name, e.g. "handlers/callback_query". Available handlers/ types: callback_query, inline_query, chat_member, …
<Tab>-Vervollständigung bietet die gleiche Menge – siehecompletion.
Die generierte Datei hat einen Live-export default, sodass der Handler sofort aktiv ist, sobald Sie ihn bereitstellen – es muss nichts entkommentiert werden. Stellen Sie das neue Modul mit
push
bereit.
login
npx tgcloud login
Fordert Ihren CLI-Zugriffstoken an – von
@BotFather
→ Ihr Bot → Serverless → CLI Access → Access token, ein separater Token von Ihrem
Bot API Token
– validiert ihn gegen die Plattform und speichert ihn in .tgcloud/credentials.
loginist der einzige Befehl, der nach einem Token fragt, und er erfordert ein interaktives Terminal. In CI setzen Sie stattdessenTGCLOUD_TOKEN.
status
npx tgcloud status
Zeigt pro Datei an, was zwischen Ihrem Arbeitsverzeichnis und der bereitgestellten Kopie geändert wurde: geändert, neu, gelöscht, unverändert. Er funktioniert offline und vergleicht mit dem Status der Cloud zum Zeitpunkt Ihres letzten push, pull oder fetch. Er warnt auch vor verirrten .js-Dateien in tgcloud/, die nicht bereitgestellt werden.
Für ein Projekt mit einer Mini-App zeigt status auch an, was sich seit der letzten Bereitstellung im Build-Ordner geändert hat.
diff
npx tgcloud diff
Ähnlich wie status, aber zeigt die tatsächlichen zeilenweisen Unterschiede für geänderte Module. Ebenfalls offline.
push
npx tgcloud push [files...]
Stellt Ihr Projekt atomar in der Cloud bereit.
Ohne Argumente stellt es das gesamte Projekt bereit, und der bereitgestellte Zustand spiegelt Ihren Ordner exakt wider – Module, die Sie lokal gelöscht haben, werden in der Cloud entfernt.
Mit Datei- oder Verzeichnisargumenten (npx tgcloud push tgcloud/handlers/message.js, npx tgcloud push handlers/) werden nur die Änderungen in diesen Dateien bereitgestellt. Ein gezieltes Push löscht niemals andere Module. Ein Ziel kann als Dateipfad (tgcloud/handlers/message.js), als Pfad innerhalb von tgcloud/ (handlers/message.js) oder als Modulname (handlers/message) angegeben werden – sie bedeuten alle dasselbe Modul.
Seine einzige Option ist --force – um die Prüfung auf Gleichzeitigkeit zu überspringen und den Inhalt der Cloud zu überschreiben. Verwenden Sie es nur, wenn Sie sicher sind (siehe
Synchron bleiben
).
Wenn tgcloud.jsonc einen statischen Ordner benennt, stellt ein vollständiger push ihn im selben Stapel bereit – nur die geänderten Dateien werden hochgeladen – und gibt die Adresse der Website aus. Bauen Sie zuerst, denn push führt niemals Ihren Build aus (npm run deploy erledigt beides). npx tgcloud push dist/ stellt nur die statischen Dateien bereit, und ein Push, der nur Module benennt, lässt diese unberührt.
Nach einer Bereitstellung, wenn schema.js geändert wurde und die Datenbank nicht synchron ist, druckt push eine Zusammenfassung der ausstehenden Änderungen und schlägt npx tgcloud migrate vor. Es wendet sie niemals selbst an.
migrate
npx tgcloud migrate
Wendet Ihre Schemaänderungen auf die Datenbank an. Es berechnet den Unterschied zwischen schema.js und der Live-Datenbank und führt Sie dann Schritt für Schritt durch, mit einem laufenden [N/M]-Zähler:
- Eine kurze Zusammenfassung aller ausstehenden Änderungen.
- Sichere Änderungen, die auf Ihre Bestätigung hin zusammen in einem Schritt angewendet werden.
- Warnungen (Löschungen, langsame Operationen), einzeln, jede separat bestätigt.
- Manuelle Änderungen, angezeigt mit einem Grund und einem vorgeschlagenen Vorgehen, nicht automatisch angewendet.
- Undokumentierte Objekte (in der Datenbank, aber nicht in Ihrem Schema), zur Kenntnisnahme angezeigt.
Es endet mit einer Zusammenfassung: angewendet, übersprungen, auf manuelle Korrektur wartend, nicht im Schema.
Siehe Migrationen für das Modell.
Optionen: --dry-run (alles drucken, nichts anwenden), --safe (sichere Änderungen automatisch anwenden, Warnungen überspringen), --yes (sichere Änderungen und jede Warnung automatisch anwenden, manuelle überspringen – mit Vorsicht verwenden), --local (Unterschiede zu Ihrer lokalen schema.js anstelle der bereitgestellten). Ohne Flag benötigt migrate ein Terminal und schlägt in einer nicht-interaktiven Umgebung fehl, anstatt zu raten.
run
npx tgcloud run <module> [args] [--ctx <json5>]
Führt einen Handler oder einen Endpunkt auf der Plattform ohne Bereitstellung aus, unter Verwendung Ihrer aktuellen lokalen Dateien. Dies ist die schnelle innere Schleife zum Testen von Logik.
<module>– ein einfacher Name (gesucht unterhandlers/undendpoints/) oder ein Pfad wiehandlers/message. Ein einfacher Name, der in beiden Verzeichnissen existiert, muss mit seinem Verzeichnis angegeben werden.[args]– die an den Handler übergebene Nutzlast, geschrieben in JSON5 , damit Sie Anführungszeichen bei Schlüsseln weglassen können. Es ist das Update-Typ-Objekt, das Ihr Handler erhält (z.B. eine Nachricht fürhandlers/message), oder dieinputeines Endpunkts, und es muss ein Objekt sein – kein Array oder ein einfacher String.--ctx <json5>– das Kontextobjekt des Moduls (sein zweites Argument), ebenfalls JSON5. Verwenden Sie es, um das bereitzustellen, was Ihr Code vonctxliest – das rohe Update für einen Handler,--ctx '{ update: { update_id: 1 } }', oder die Init-Daten für einen Endpunkt,--ctx '{ initData: { user: { id: 1 } } }'.runverifiziert es nicht, da Sie Ihre Logik testen, nicht die Prüfungen der Plattform.
npx tgcloud run handlers/message '{ chat: { id: 1 }, text: "hallo" }'
Die Plattform führt das Modul gegen den Modulraum aus, der aus Ihrem lokalen Projekt zusammengestellt wurde (sodass lokal geänderter lib/-Code ebenfalls verwendet wird), und gibt den Rückgabewert, alles mit console.* protokollierte und die verstrichene Zeit zurück. Lesen Sie große Argumente aus einer Datei mit npx tgcloud run handlers/message "$(cat message.json5)".
fetch
npx tgcloud fetch
Prüft, was derzeit bereitgestellt ist, ohne Ihre Arbeitsdateien zu berühren. Danach vergleichen status und diff mit dem neuesten Cloud-Status. Nützlich, um einen Konflikt zu betrachten, bevor Sie entscheiden, wie Sie ihn lösen.
pull
npx tgcloud pull
Bringt Ihre lokalen Dateien mit der Cloud in Einklang, indem Ihre Arbeitsdateien auf den bereitgestellten Zustand aktualisiert werden.
pull schreibt den Build-Ordner Ihrer Mini-App niemals.
reset
npx tgcloud reset
Verwirft Ihre lokalen Änderungen und stellt die Module aus dem zuletzt bekannten Cloud-Status wieder her. Verwenden Sie es, um ein Experiment wegzuwerfen. Der Build-Ordner der Mini-App wird nicht berührt.
webhook
npx tgcloud webhook
npx tgcloud webhook sync [--drop-pending]
Telegram liefert Updates über einen Webhook an Ihren Bot, der von der Plattform für Sie verwaltet wird – Sie müssen ihn nie von Hand auf etwas zeigen. npx tgcloud webhook zeigt seinen aktuellen Status an: die URL, die allowed_updates-Liste, wie viele Updates ausstehen, den letzten Lieferfehler (falls vorhanden) und ob er mit Ihren bereitgestellten Handlern synchron ist.
„Synchron“ bedeutet, dass der Webhook auf die Plattform zeigt und seine allowed_updates mit den von Ihnen bereitgestellten Handlern übereinstimmen – sodass Telegram genau die Update-Typen liefert, die Sie verarbeiten, und nichts anderes. Das Bereitstellen eines neuen Handlers (oder das Entfernen eines) kann den Webhook unsynchronisieren, bis er erneut aktualisiert wird; npx tgcloud status kennzeichnet dies ebenfalls.
npx tgcloud webhook sync behebt dies – er zeigt den Webhook erneut auf die Plattform und baut allowed_updates aus Ihren bereitgestellten Handlern neu auf. Fügen Sie --drop-pending hinzu, um Updates zu verwerfen, die Telegram bereits in die Warteschlange gestellt hatte, bevor die Synchronisierung erfolgte (andernfalls werden sie geliefert, sobald der Webhook wieder funktionsfähig ist).
completion
Hinweis: Die Tab-Vervollständigung funktioniert nur, wenn ein nacktes
tgcloudin IhremPATHvorhanden ist – installieren Sie es also global (npm install -g @tgcloud/cli) oder legen Sie das Binärprogramm anderweitig auf IhrenPATH. Es kann sich nicht innpxeinklinken.
tgcloud completion <bash|zsh|fish>
Gibt ein Shell-Vervollständigungsskript an stdout aus. Aktivieren Sie es einmal, dann vervollständigt <Tab> Befehle, Flags, Modulverzeichnisse, die Handler-Update-Typen, die Sie noch nicht haben, und Ihre lokalen ausführbaren Module.
# bash – benötigt das bash-completion-Paket:
echo 'eval "$(tgcloud completion bash)"' >> ~/.bashrc
# zsh – stellen Sie sicher, dass `autoload -U compinit && compinit` in Ihrem ~/.zshrc ausgeführt wird:
echo 'eval "$(tgcloud completion zsh)"' >> ~/.zshrc
# fish:
tgcloud completion fish > ~/.config/fish/completions/tgcloud.fish
Starten Sie Ihre Shell danach neu (oder laden Sie die Datei neu). Das Ausführen von tgcloud completion ohne Shell gibt diese Anweisungen erneut aus.
upgrade
npx tgcloud upgrade [--dry-run]
Einmalige Migration für Projekte, die vor CLI 0.2.0 erstellt wurden und schema.js, lib/ und handlers/ im Projektstammverzeichnis behielten. Sie verschiebt sie in den Ordner tgcloud/. Ihre Verbindung zum Bot bleibt erhalten und nichts muss neu bereitgestellt werden. --dry-run gibt die geplanten Verschiebungen aus, ohne etwas zu ändern. Andere Befehle laufen im alten Layout nicht, bis Sie ein Upgrade durchführen.
Synchron bleiben
Jedes Projekt hat eine monoton steigende Revision in der Cloud, die bei jedem Deploy inkrementiert wird. Die CLI merkt sich die Revision, mit der sie zuletzt synchronisiert hat, und sendet sie bei jedem push. Wenn die Cloud sich weiterentwickelt hat – weil eine andere Maschine oder ein Teamkollege bereitgestellt hat –, wird der Push abgelehnt, anstatt die Arbeit des anderen stillschweigend zu überschreiben, und die CLI bietet drei Wege nach vorne:
npx tgcloud fetch # sehen, was sich in der Cloud geändert hat, dann erneut prüfen
npx tgcloud pull # das Neueste in Ihre Arbeitsdateien holen
npx tgcloud push --force # den Cloud-Status überschreiben (gefährlich)
Diese Prüfung auf optimistische Nebenläufigkeit ist der Grund, warum Sie einen Bot teamübergreifend teilen können, ohne ein Schritt-für-Schritt-Bereitstellungsprozess. Befehle werden bei Fehlern mit einem nicht-null-Wert beendet – ein abgelehnter Deploy, eine fehlgeschlagene Migration, ein Authentifizierungsfehler, ein Modul, das während der run fehlgeschlagen ist –, sodass sie sich sauber in Skripten und CI-Pipelines zusammensetzen.