Telegram Serverless cho phép bạn chạy mã backend cho bot và Mini App của mình trực tiếp trên cơ sở hạ tầng của Telegram — không cần thiết lập máy chủ, không cần duy trì các container, không cần bận tâm đến việc mở rộng quy mô. Bạn chỉ cần viết các mô-đun JavaScript thuần túy, triển khai chúng bằng một lệnh duy nhất, và Telegram sẽ chạy chúng trong một sandbox V8 nhanh chóng, biệt lập nằm ngay bên cạnh Bot API và cơ sở dữ liệu tích hợp sẵn.
Nếu bạn từng phải kết nối bot với một VPS, một cloud function, hoặc một bảng điều khiển lưu trữ chỉ để phản hồi lệnh /start, thì đây là phần việc bạn không còn cần phải làm nữa.
Tại sao nên chọn serverless
Về cốt lõi, bot Telegram là một chương trình phản ứng với các cập nhật. Theo cách truyền thống, bạn phải lưu trữ chương trình đó ở một nơi luôn hoạt động, có thể truy cập được và bảo mật — rồi duy trì trạng thái đó. Telegram Serverless loại bỏ hoàn toàn lớp phức tạp này:
- Không hạ tầng. Không có máy móc nào cần thuê, vá lỗi hay giám sát. Mã của bạn chạy theo yêu cầu và tự động mở rộng quy mô cùng với bot của bạn.
- Bao gồm thông tin xác thực. Bot API của Telegram, cơ sở dữ liệu hỗ trợ SQLite và khả năng gọi HTTP ra bên ngoài đều có sẵn cho mọi mô-đun ngay khi bắt đầu — không cần cài đặt gì, không cần thiết lập thông tin xác thực.
- Thực thi nhanh, biệt lập. Mỗi lần gọi chạy trong một môi trường V8 isolate nhẹ, gần với các hệ thống của chính Telegram, vì vậy các cuộc gọi đến Bot API và cơ sở dữ liệu của bạn diễn ra nhanh chóng và đáng tin cậy.
- Quy trình làm việc của nhà phát triển thực thụ. Một dự án nằm trong một thư mục trên máy của bạn và được kiểm soát phiên bản. Bạn chỉnh sửa tệp, thấy chính xác những gì đã thay đổi, triển khai một cách nguyên tử (atomic) và cập nhật lược đồ cơ sở dữ liệu với các bản di chuyển (migrations) đã được xem xét — đúng như cách bạn làm việc với mọi thứ khác.
Mô hình tư duy: Cách dự án hoạt động
Bạn làm việc ở ba nơi và chúng khớp với nhau một cách rõ ràng:
| Nơi làm việc | Những gì ở đó |
|---|---|
| Thư mục dự án | Các mô-đun JavaScript — lược đồ, mã dùng chung, trình xử lý cập nhật, endpoint của Mini App — và frontend của Mini App (nếu có) |
| Đám mây (Cloud) | Bản sao đã triển khai của các mô-đun đó, cơ sở dữ liệu của bot và các tệp của Mini App |
tgcloud CLI |
Cầu nối — hiển thị sự khác biệt và đồng bộ hóa chúng |
Bạn không bao giờ phải SSH vào bất cứ thứ gì. Bạn chỉnh sửa tệp cục bộ, chạy npx tgcloud push, và nền tảng sẽ xử lý phần còn lại. Lưu lượng truy cập của bot được xử lý bởi các mô-đun đã triển khai; cơ sở dữ liệu của bạn được duy trì giữa các lần gọi.
Một dự án chỉ có bốn loại mã, tất cả nằm trong một thư mục tgcloud/:
tgcloud/
├─ endpoints/ # các hàm mà Mini App của bạn gọi
├─ handlers/ # các hàm phản ứng với các loại cập nhật Telegram
├─ lib/ # mã dùng chung mà bạn có thể import từ bất cứ đâu
└─ schema.js # các bảng cơ sở dữ liệu của bạn
Khi có cập nhật đến — một tin nhắn, một nút bấm, một inline query — Telegram sẽ định tuyến nó đến trình xử lý khớp (handlers/message.js, handlers/callback_query.js, …) và gọi hàm mặc định của nó. Hàm đó giao tiếp với Bot API và cơ sở dữ liệu thông qua SDK, sau đó kết thúc. Đó là toàn bộ vòng lặp. Một cập nhật không có trình xử lý khớp sẽ bị bỏ qua, vì vậy bạn chỉ cần thêm các trình xử lý mình cần.
Mini App là tùy chọn. Nếu bot của bạn có Mini App, frontend của nó sẽ nằm trong cùng dự án, bên cạnh tgcloud/. Serverless sẽ lưu trữ tệp build và chạy các endpoints/ mà nó gọi. Xem
Mini App front‑end
.
Demo nhanh
Đây là một bot demo hoàn chỉnh và đang hoạt động. Nó phản hồi mọi tin nhắn và ghi nhớ số lượng tin nhắn đã nhận từ mỗi cuộc trò chuyện.
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;
// Chèn bộ đếm, hoặc tăng nó nếu cuộc trò chuyện đã tồn tại — và lấy lại
// hàng kết quả trong cùng một câu lệnh qua .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: `Xin chào! Tôi đã nhận được ${row.seen} tin nhắn từ bạn.`,
});
}
Triển khai:
npx tgcloud push # tải lên các mô-đun
npx tgcloud migrate # tạo bảng `counters`
Đó là một bot trực tuyến với trạng thái bền vững và không cần máy chủ. Mọi thứ trong đó — api, db, DSL table() — đều được mô tả trong các phần dưới đây.
Serverless là một backend tổng quát cho bot và Mini App Telegram, không phải là một mẫu cho một loại ứng dụng cụ thể. Nó rất lý tưởng cho:
- Bot AI trò chuyện cần lưu trữ trạng thái theo từng người dùng trong cơ sở dữ liệu.
- Backend Mini App lưu trữ dữ liệu người dùng và phục vụ nội dung động.
- Trò chơi và Công cụ — bao gồm bảng xếp hạng, bài kiểm tra và nhiều hơn nữa.
- Tự động hóa và Tích hợp gọi các API HTTP bên thứ ba và đẩy kết quả vào các cuộc trò chuyện.
Bắt đầu
Hướng dẫn này sẽ đưa bạn từ một thư mục trống đến một bot trực tuyến có thể trả lời tin nhắn và lưu trữ dữ liệu. Nó yêu cầu bạn đã cài đặt
Node.js
phiên bản 18 trở lên và đã đăng ký bot với
@BotFather
. Đến cuối cùng, bạn sẽ sử dụng thành thạo mọi lệnh cần thiết hàng ngày: push, migrate, run, và status.
Trước tiên, hãy bật Serverless. Trong @BotFather , mở bot của bạn → Serverless và bật nó lên. Thao tác này sẽ kích hoạt tính năng cho bot của bạn và mở khóa mã thông báo truy cập CLI, các trình xử lý, thư viện và cơ sở dữ liệu.
1. Tạo dự án
Cách nhanh nhất để bắt đầu là sử dụng trình tạo dự án, nó sẽ tạo khung dự án và cài đặt CLI vào đó:
npm create @tgcloud/bot example_bot
cd example_bot
Đối số là thư mục đích: truyền . để tạo khung vào thư mục hiện tại, hoặc bất kỳ đường dẫn nào. Nó cũng hoạt động trong một thư mục đã có sẵn và không bao giờ ghi đè lên các tệp bạn đã có.
Việc này cho bạn một dự án sẵn sàng để chỉnh sửa:
example_bot/
├─ tgcloud/ # mọi thứ nền tảng chạy
│ ├─ handlers/
│ │ └─ message.js # trình xử lý tin nhắn mẫu (echo lại văn bản)
│ ├─ lib/ # các mô-đun dùng chung của bạn ở đây (trống khi bắt đầu)
│ └─ schema.js # các bảng cơ sở dữ liệu của bạn
├─ docs/
│ └─ tgcloud-sdk.md # tài liệu tham khảo SDK (cho bạn và các công cụ AI)
├─ AGENTS.md # hướng dẫn cho trợ lý AI lập trình
├─ package.json
└─ tgcloud.jsonc # cấu hình dự án
Các tệp được tạo sẵn đều có tài liệu tự giải thích — mỗi tệp chứa các ví dụ được chú thích về những gì bạn có thể làm tiếp theo.
CLI được cài đặt vào dự án như một dev-dependency cục bộ, vì vậy bạn chạy nó bằng npx tgcloud <command> (npx tìm bản copy trong node_modules của dự án của bạn), hoặc thông qua các phím tắt npm run mà khung dự án thêm vào package.json (npm run deploy, npm run status). Theo mặc định, không có tgcloud toàn cục nào trên PATH của bạn.
Bạn cũng có thể cài đặt nó toàn cục — npm install -g @tgcloud/cli — nếu bạn thích gõ tgcloud từ bất cứ đâu. Điều đó rất tiện lợi khi chạy tgcloud init trong bất kỳ thư mục trống nào, và đó là những gì tính năng tự động hoàn thành của shell cần. Dù thế nào bạn cũng nhận được cùng một dự án.
Đang xây dựng Mini App? Hãy bắt đầu từ frontend của nó trước. Tạo nó bằng công cụ thông thường của bạn, sau đó chạy trình tạo dự án trong thư mục đó. Xem Mini App front‑end .
2. Liên kết bot của bạn
Mỗi dự án gắn liền với một bot. Kết nối chúng bằng login, lệnh này sẽ yêu cầu mã thông báo truy cập CLI của bạn (
@BotFather
→ bot của bạn → Serverless → CLI Access → Access token — một mã thông báo riêng biệt với
mã thông báo API bot
) và lưu trữ cục bộ:
npx tgcloud login
Mã thông báo có dạng app<id>:<secret>. CLI lưu trữ nó trong .tgcloud/, tệp này đã được git‑ignored và không bao giờ in phần bí mật. Đăng nhập là lần duy nhất bạn được yêu cầu nhập nó — xem
Authentication
để biết cách mã thông báo được xử lý trong CI.
3. Xem xét xung quanh
Hai lệnh cho bạn biết tình trạng mọi thứ vào bất kỳ lúc nào, cả hai đều hoạt động offline:
npx tgcloud status # những gì đã thay đổi cục bộ so với bản đã triển khai
npx tgcloud diff # những thay đổi từng dòng
Ngay sau khi init, mọi thứ đều mới và chưa có gì được triển khai. status hiển thị các tệp khởi đầu đang chờ được tải lên.
4. Triển khai
Gửi các mô-đun của bạn lên đám mây:
npx tgcloud push
push tải lên mọi mô-đun đã thay đổi trong một đợt nguyên tử và cập nhật bản ghi cục bộ của bạn về những gì đám mây hiện đang giữ. Bot của bạn đã hoạt động: mở nó trong Telegram và gửi một tin nhắn — trình xử lý mẫu sẽ echo lại.
Triển khai không bao giờ chạm vào cơ sở dữ liệu. Việc đẩy mã và thay đổi lược đồ cơ sở dữ liệu là các bước tách biệt một cách có chủ đích, vì vậy việc triển khai mã không bao giờ có thể làm bạn bất ngờ với việc di chuyển dữ liệu. Đó là lý do cho bước tiếp theo.
5. Thêm bảng cơ sở dữ liệu
Hãy làm cho bot nhớ điều gì đó. Mở tgcloud/schema.js và khai báo một bảng:
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())`) ,
});
Triển khai lược đồ, sau đó áp dụng nó vào cơ sở dữ liệu:
npx tgcloud push # tải lên lược đồ mới
npx tgcloud migrate # tạo bảng `messages`
push báo cáo rằng lược đồ không đồng bộ và hiển thị thay đổi đang chờ xử lý, nhưng không áp dụng gì cả. migrate sẽ hướng dẫn bạn thực hiện thay đổi và, sau khi bạn xác nhận, sẽ tạo bảng. Mô hình hai bước này — và những gì xảy ra với các thay đổi rủi ro hơn như xóa — được đề cập trong
Migrations
.
6. Lưu trữ và đọc dữ liệu
Bây giờ hãy sử dụng bảng từ trình xử lý của bạn. Chỉnh sửa tgcloud/handlers/message.js:
import { api, db } from 'sdk';
import { eq } from 'sdk/db';
import { messages } from '../schema.js';
export default async function (message) {
// Lưu tin nhắn này.
await db.insert(messages)
.values({ chatId: message.chat.id, text: message.text })
.run();
// Đếm số lượng chúng ta đã lưu cho cuộc trò chuyện này.
const count = await db.$count(messages, eq(messages.chatId, message.chat.id));
await api.sendMessage({
chat_id: message.chat.id,
text: `Đã lưu. Đến nay đã có ${count} tin nhắn từ cuộc trò chuyện này.`,
});
}
Triển khai trình xử lý đã cập nhật bằng npx tgcloud push, sau đó gửi cho bot của bạn một vài tin nhắn và theo dõi số đếm tăng lên. Cơ sở dữ liệu tồn tại giữa các lần gọi — đó chính là bộ nhớ của bot bạn.
7. Kiểm tra mà không cần triển khai
Bạn không cần triển khai để thử một thay đổi. npx tgcloud run thực thi một trình xử lý trên nền tảng bằng cách sử dụng các tệp cục bộ của bạn, mà không xuất bản chúng:
npx tgcloud run handlers/message '{ chat: { id: 1 }, text: "hello" }'
Đối số là payload mà trình xử lý của bạn nhận được — đối với handlers/message là một
Message
— viết bằng
JSON5
(vì vậy bạn có thể bỏ qua dấu ngoặc kép cho khóa). Lệnh này in ra bất cứ điều gì trình xử lý ghi lại bằng console.*, giá trị trả về và thời gian thực hiện. Đây là vòng lặp chặt chẽ nhất để lặp lại logic — không triển khai, không chờ đợi tin nhắn thực.
8. Giữ đồng bộ
Khi bạn làm việc, một vài lệnh sẽ giữ cho dự án cục bộ và đám mây của bạn luôn khớp nhau: npx tgcloud status hiển thị những gì đã thay đổi, npx tgcloud push triển khai, npx tgcloud pull đưa dự án cục bộ của bạn khớp với đám mây, npx tgcloud fetch kiểm tra những gì đã triển khai mà không thay đổi tệp của bạn, và npx tgcloud reset loại bỏ các thay đổi cục bộ.
Nếu hai người (hoặc hai máy) triển khai vào cùng một bot, nền tảng sẽ phát hiện xung đột và
pushsẽ dừng lại để bạnpulltrước — bạn không bao giờ có thể ghi đè âm thầm lên công việc của người khác. Xem Staying in sync .
Xây dựng với AI
Bạn thích xây dựng với một trợ lý AI — hoặc lập trình viên duy nhất trong nhóm của bạn là AI? Bạn vẫn có thể xây dựng bot. Chúng tôi đã thực hiện bước đầu tiên để làm cho tác nhân AI cảm thấy như ở nhà trong dự án: mỗi dự án mới được tạo khung với AGENTS.md và tài liệu tham khảo docs/tgcloud-sdk.md mà các công cụ lập trình AI đọc tự động.
Kết hợp với một runtime nhỏ, khép kín — một SDK, không cần quản lý các gói npm — điều đó mang lại cho trợ lý một khởi đầu thuận lợi về các quy ước mà việc tạo mã chung thường bỏ sót ở đây: import tương đối với .js, không có khóa ngoại, mỗi lệnh db đều là async, một trình xử lý cho mỗi loại cập nhật, và luồng push/migrate hai bước.
Thử ngay:
npm create @tgcloud/bot my-bot
cd my-bot
opencode # hoặc Claude Code, Cursor, … — bất kỳ tác nhân nào đọc được AGENTS.md
Sau đó chỉ cần hỏi, bằng ngôn ngữ đơn giản:
Hãy viết một bot ghi nhớ danh sách việc cần làm của mỗi người — thêm một mục khi họ gửi văn bản, và hiển thị toàn bộ danh sách khi họ gửi /list.
Trợ lý chỉnh sửa schema.js và các trình xử lý cho bạn; bạn xem xét, kiểm tra thay đổi ngay lập tức với npx tgcloud run, sau đó đi vào thực tế với npx tgcloud push và npx tgcloud migrate. AGENTS.md là một phần của dự án — hãy chỉnh sửa nó khi bot phát triển để các hướng dẫn luôn chính xác.
Di chuyển với BotFather
Chỉ còn điện thoại trên tay? Toàn bộ dự án cũng nằm trong @BotFather — mở bot của bạn → Serverless và bạn sẽ nhận được mọi thứ mà CLI quản lý, trên màn hình cảm ứng:
- Handlers — tạo, chỉnh sửa và kiểm tra các trình xử lý cập nhật; BotFather giữ webhook đồng bộ với các trình xử lý bạn có (giống như trạng thái In sync / Out of sync mà CLI báo cáo).
- Endpoints — các hàm mà Mini App của bạn gọi.
- Library — các mô-đun dùng chung
lib/. - Database — chỉnh sửa
schema.jsbằng cú pháp Drizzle-like, xem xét các thay đổi đang chờ xử lý và áp dụng chúng; Save sẽ triển khai. - Mini App Files — frontend mà người dùng của bạn mở trong Telegram.
- CLI Access — lấy mã thông báo truy cập CLI tại đây khi bạn quay lại bàn phím.
Đó là cùng một dự án đám mây, vì vậy bạn có thể bắt đầu một trình xử lý trên điện thoại và dùng npx tgcloud pull về máy tính xách tay sau đó — không có gì bị buộc chặt vào một ứng dụng duy nhất. Việc chạy trình xử lý thậm chí còn hiển thị đầu ra Console của nó ngay trong cuộc trò chuyện, giống như
npx tgcloud run
.
Các dự án và mô-đun
Một dự án serverless là một thư mục bình thường được kiểm soát phiên bản. Mọi thứ nền tảng chạy nằm trong một thư mục, tgcloud/; phần còn lại của dự án là của bạn — frontend mini-app, cấu hình, tài liệu. Không có bước build cho bot, không có node_modules khi chạy và không có điểm truy cập máy chủ.
Giải phẫu một dự án
example_bot/
├─ tgcloud/ # phần được triển khai — không có gì khác
│ ├─ handlers/ # trình xử lý cập nhật — phẳng, chỉ một cấp
│ │ ├─ message.js
│ │ └─ callback_query.js
│ ├─ endpoints/ # các hàm Mini App gọi — phẳng, chỉ một cấp
│ │ └─ getProfile.js
│ ├─ lib/ # mô-đun dùng chung; cho phép thư mục con
│ │ ├─ reply.js
│ │ └─ internal/util.js
│ └─ schema.js # lược đồ cơ sở dữ liệu — một tệp
└─ .tgcloud/ # trạng thái CLI, git‑ignored
Chỉ các tệp .js bên trong tgcloud/ — schema.js, lib/, handlers/, endpoints/ — mới được triển khai. Mọi thứ khác — Markdown, tệp cấu hình, thư mục .tgcloud/, bất cứ thứ gì bên ngoài tgcloud/ — đều nằm lại trên máy của bạn. Tên mô-đun có liên quan đến tgcloud/. Tệp tgcloud/handlers/message.js là mô-đun handlers/message, và đó là tên mà npx tgcloud run sử dụng.
Nâng cấp từ dự án cũ? Các dự án được tạo trước CLI 0.2.0 giữ
schema.js,lib/vàhandlers/ở thư mục gốc của dự án. Chạynpx tgcloud upgrademột lần để chuyển chúng vàotgcloud/.
schema.js — cơ sở dữ liệu của bạn. Nó khai báo các bảng dưới dạng các export có tên bằng schema DSL và nằm trong tgcloud/ như một tệp duy nhất. Nó được triển khai như bất kỳ mô-đun nào khác, nhưng việc triển khai nó không bao giờ thay đổi cơ sở dữ liệu — các thay đổi lược đồ được áp dụng riêng biệt với npx tgcloud migrate. Xem
The database
.
lib/ — mã dùng chung, bất cứ thứ gì bạn muốn sử dụng lại trên các trình xử lý: trợ lý thuần túy, các lớp truy cập cơ sở dữ liệu, định dạng, tích hợp với các dịch vụ bên ngoài. lib/ là thư mục duy nhất có thể chứa thư mục con (lib/internal/util.js, lib/payments/stripe.js), vì vậy bạn có thể tổ chức codebase lớn hơn theo ý muốn. Các mô-đun trong lib/ không bao giờ được nền tảng gọi trực tiếp; chúng tồn tại để được import bởi các trình xử lý và bởi chính nhau.
handlers/ — các điểm truy cập của bot. Mỗi tệp tương ứng với một loại cập nhật Telegram, và nền tảng định tuyến mỗi cập nhật đến trình xử lý khớp:
| Tệp | Xử lý |
|---|---|
handlers/message.js |
Tin nhắn đến mới |
handlers/inline_query.js |
Các truy vấn inline mới |
handlers/callback_query.js |
Các truy vấn callback mới |
| … | bất kỳ loại cập nhật Bot API nào khác |
handlers/ là phẳng — không có thư mục con. export default của trình xử lý là hàm mà nền tảng gọi (xem
Handlers
).
Một loại cập nhật chỉ được xử lý nếu tệp trình xử lý của nó tồn tại và không rỗng. Nếu không có handlers/<type>.js — hoặc tệp trống — các cập nhật loại đó sẽ bị bỏ qua và nền tảng không chạy gì cho chúng. Vì vậy, chỉ giữ những trình xử lý bạn thực sự cần: mỗi trình xử lý bạn thêm vào là một loại cập nhật nữa mà bot của bạn thức dậy để xử lý, và việc bỏ qua phần còn lại có nghĩa là Telegram không khởi động mã của bạn cho các cập nhật mà dù sao bạn cũng sẽ loại bỏ.
Để tạo khung một trình xử lý mới, chạy
npx tgcloud add handlers/<type>.
endpoints/ — phía máy chủ của Mini App. Mỗi tệp là một hàm mà Mini App gọi theo tên, vì vậy endpoints/getProfile.js trả lời POST /api/getProfile. Nền tảng kiểm tra xem cuộc gọi có đến từ người dùng Telegram trong Mini App của bạn không trước khi mã của bạn chạy, và chuyển cho bạn người dùng đó. endpoints/ là phẳng, giống như handlers/. Xem
Endpoints
.
.tgcloud/ — trạng thái cục bộ của máy được quản lý hoàn toàn bởi CLI, bao gồm thông tin xác thực đã lưu của bạn. Nó được git‑ignored, và bạn không bao giờ nên tự đọc hoặc ghi vào nó — hãy sử dụng các lệnh CLI thay thế.
Hệ thống mô-đun
Tại runtime, một mô-đun có thể thấy chính xác hai thứ: SDK của nền tảng và các mô-đun khác trong dự án của bạn. Không có gói npm, không có hệ thống tệp và không có mạng ngoại trừ thông qua fetch của SDK.
Import các mô-đun của riêng bạn theo cách bạn làm trong bất kỳ dự án JavaScript nào — bằng đường dẫn tương đối, với phần mở rộng .js — và SDK bằng tên của nó:
// tgcloud/handlers/message.js
import { users } from '../schema.js'; // mô-đun schema
import { addItem } from '../lib/cart.js'; // mô-đun lib
import { format } from '../lib/internal/fmt.js'; // mô-đun lib lồng nhau
import { db, api, fetch } from 'sdk'; // SDK của nền tảng
// tgcloud/lib/cart.js
import { format } from './internal/fmt.js'; // hàng xóm
import { users } from '../schema.js';
Một import tương đối phải kết thúc bằng .js và không thể dẫn ra ngoài tgcloud/:
import { users } from '../schema'; // thêm .js
import config from '../../config.js'; // bên ngoài tgcloud/
import { z } from 'zod'; // không có gói npm
Mỗi mô-đun cũng có một tên — đường dẫn của nó bên trong tgcloud/ không bao gồm phần mở rộng, chẳng hạn như schema, lib/cart hoặc handlers/message. Tên đó là những gì npx tgcloud run và đầu ra của CLI sử dụng, và một mô-đun cũng có thể được import bằng tên đó (from 'lib/cart', không có phần mở rộng). Hãy ưu tiên sử dụng import tương đối trong mã của bạn. Đó là những gì trình chỉnh sửa của bạn hiểu — jump‑to‑definition, autocomplete, rename.
Những gì có sẵn tại runtime chính xác là hai thứ: sdk và các mô-đun con của nó (sdk/db, sdk/api, sdk/fetch) — toàn bộ bề mặt nền tảng, xem
The SDK
— và các mô-đun của chính bạn trong tgcloud/. Đó là danh sách đầy đủ. Nếu mã của bạn import bất cứ thứ gì khác, nó sẽ không phân giải được. Sự ràng buộc này là điều giữ cho các mô-đun tải nhanh và an toàn khi chạy.
Handlers
Trình xử lý là một mô-đun trong handlers/ mà nền tảng gọi khi có cập nhật khớp đến.
// handlers/message.js
import { api } from 'sdk';
export default async function (message) {
await api.sendMessage({
chat_id: message.chat.id,
text: `Bạn đã nói: ${message.text ?? '(không có văn bản)'}`,
});
}
Một trình xử lý nhận payload của cập nhật làm đối số — nền tảng sẽ mở gói
Update
của Telegram cho bạn. handlers/message.js nhận được
Message
(tức là update.message); handlers/callback_query.js nhận được
CallbackQuery
; vân vân. Đối số thứ hai của trình xử lý là một đối tượng ngữ cảnh theo mỗi lần gọi, ctx. Nó chứa Update thô dưới dạng ctx.update — hãy sử dụng nó khi bạn cần thứ gì đó bên ngoài payload, như update_id.
Trình xử lý có thể là async (thường là vậy) và có thể trả về một giá trị. Nó tiếp cận Bot API, cơ sở dữ liệu và HTTP ngoại vi thông qua SDK.
Bạn không cần một cập nhật thực để kiểm tra trình xử lý. npx tgcloud run thực thi nó trên nền tảng với payload bạn cung cấp và mã cục bộ hiện tại của bạn:
npx tgcloud run handlers/message '{ chat: { id: 1 }, text: "hi" }'
Đối số là payload — cùng một đối tượng mà trình xử lý của bạn nhận được — dưới dạng JSON5. Để cung cấp ctx của trình xử lý (đối số thứ hai của nó), hãy thêm --ctx, ví dụ: --ctx '{ update: { update_id: 1 } }'. Lệnh này chạy dựa trên các tệp cục bộ của bạn, vì vậy bạn có thể thử các thay đổi trước khi triển khai chúng. Xem
run
.
Endpoints
Endpoint là một mô-đun trong endpoints/ mà Mini App của bạn gọi — nơi cho bất cứ thứ gì frontend không thể tự làm, như đọc và ghi cơ sở dữ liệu, gọi Bot API hoặc trò chuyện với các dịch vụ bên ngoài bằng các bí mật của bạn.
// 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('Không tìm thấy hồ sơ', { userId: user.id });
return profile;
}
Đối số đầu tiên, input, là đối tượng JSON mà Mini App đã gửi. Đối số thứ hai, ctx, chứa ctx.initData —
dữ liệu init
của Mini App (user, chat, start_param, auth_date, …), đã được nền tảng xác minh. Nó có hình dạng giống như Telegram.WebApp.initDataUnsafe trên client, nhưng ở đây bạn có thể tin tưởng nó. Một cuộc gọi không có dữ liệu init hợp lệ sẽ bị từ chối với mã 401 trước khi mã của bạn chạy, vì vậy ctx.initData.user thực sự là người đã mở Mini App của bạn. Endpoints chỉ có thể được gọi theo cách này — từ Mini App của bot của bạn, bởi người dùng Telegram — vì vậy không có API công cộng nào để bảo vệ và không cần xây dựng đăng nhập.
Bất cứ thứ gì endpoint trả về đều được gửi lại dưới dạng JSON. Để từ chối một cuộc gọi, hãy throw new EndpointError(mô tả, tham số?). Mini App nhận được mô tả và đối tượng parameters như nguyên bản. Nếu bạn cần phân nhánh dựa trên loại lỗi ở frontend, hãy đặt mã trong parameters — { code: 'OUT_OF_STOCK' }. Mọi ngoại lệ khác đều được báo cáo là lỗi nội bộ chung, và thông báo thực chỉ đi đến nhật ký của bạn, vì vậy thông tin nội bộ không bao giờ rò rỉ ra client.
Gọi một endpoint từ Mini App
Mini App gọi một endpoint theo tên với Telegram.WebApp.Serverless.call từ
Mini Apps SDK
chính thức, telegram-web-app.js — tập lệnh mà Mini App của bạn đã bao gồm. Nó gửi dữ liệu init cho bạn và gọi lại với một lỗi hoặc kết quả của endpoint:
Telegram.WebApp.Serverless.call('getProfile', { lang: 'en' }, function (err, profile) {
if (err) {
// err.message — mô tả; err.parameters — những gì EndpointError của bạn mang theo
if (err.type === 'ENDPOINT_ERROR') showMessage(err.message);
return;
}
render(profile);
});
Input là tùy chọn và phải là một đối tượng thuần túy. Nếu không có nó, cuộc gọi là Serverless.call('getProfile', callback).
err.type là 'ENDPOINT_ERROR' khi endpoint của bạn throw một EndpointError. Bất kỳ giá trị nào khác có nghĩa là nền tảng từ chối cuộc gọi hoặc endpoint bị lỗi bất ngờ, ví dụ 'UNAUTHORIZED' khi thiếu dữ liệu init hoặc không hợp lệ.
Tên endpoint giống như các định danh JavaScript — chữ cái, chữ số và _, không bắt đầu bằng chữ số (getProfile, place_order). Tạo khung một cái bằng
npx tgcloud add endpoints/<name>
, và thử nó mà không cần Mini App với
run
, tự truyền dữ liệu init:
npx tgcloud run endpoints/getProfile '{}' --ctx '{ initData: { user: { id: 1, first_name: "Ann" } } }'
Những gì được triển khai
Khi bạn npx tgcloud push, CLI thu thập mọi tệp .js trong schema.js, lib/, handlers/ và endpoints/, và gửi tập hợp chính xác đó làm không gian mô-đun của dự án bạn — cùng với thư mục build của Mini App, nếu tgcloud.jsonc có tên (xem bên dưới). Bất cứ thứ gì hiện diện trên đám mây nhưng vắng mặt trong dự án của bạn đều bị xóa, vì vậy trạng thái đã triển khai luôn phản chiếu thư mục của bạn — bao gồm cả việc xóa. Không có gì bên ngoài tgcloud/ được triển khai dưới dạng mã — phần còn lại của thư mục gốc dự án là của bạn. Bên trong tgcloud/, một tệp .js không nằm ở một trong các vị trí đó (tgcloud/util.js, tgcloud/helpers/x.js) sẽ bị gắn cờ, vì vậy nó không âm thầm bị bỏ qua triển khai. Markdown, tệp dấu chấm và .tgcloud/ không bao giờ được triển khai.
Mini App front-end
Thư mục gốc dự án là của bạn, và đó là nơi tự nhiên cho frontend Mini App của bạn — được xây dựng bằng React, Vue, Svelte hoặc HTML thuần túy, bởi Vite hoặc bất kỳ bộ đóng gói nào khác. Serverless lưu trữ đầu ra build của nó bên cạnh bot của bạn. Một lệnh push triển khai mã và các tệp tĩnh cùng nhau, và ứng dụng được phục vụ tại https://app<app_id>.tgcloud.ai/ (push in ra địa chỉ chính xác — sử dụng nó làm URL Mini App của bạn trong
@BotFather
).
Báo cáo lạm dụng. Các Mini App trên tgcloud.ai được xuất bản bởi các nhà phát triển độc lập. Để báo cáo nội dung bất hợp pháp hoặc lạm dụng trên tên miền phụ
app<app_id>.tgcloud.ai, hãy gửi email tới [email protected] .
Để bắt đầu, hãy tạo frontend trước, với bất kỳ công cụ nào bạn thích, sau đó thêm Serverless lên trên nó:
npm create vite@latest example_app # hoặc create-vue, …
cd example_app
npm create @tgcloud/bot .
Trình tạo dự án không bao giờ ghi đè lên các tệp của bạn. Nó thêm thư mục tgcloud/ bên cạnh frontend, nhận dạng thư mục build và thêm tập lệnh npm run deploy để build ứng dụng và triển khai nó cùng với bot.
Những gì được lưu trữ được đặt trong tgcloud.jsonc, tệp cấu hình dự án tại thư mục gốc dự án:
{
"$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 }]
}
}
Chỉ "source" là bắt buộc. Đó là thư mục build, tương đối với thư mục gốc dự án. Các khóa khác của "static" định cấu hình cách nó được phục vụ (bên dưới). init tạo tgcloud.jsonc cho bạn. Khi nó nhận dạng được frontend, nó cũng điền vào "source":
| Front‑end | init đặt "source" thành |
|---|---|
| Vite — React, Vue, Svelte, Solid, … | dist |
| Next.js, Nuxt, SvelteKit, React Router, TanStack Start, Angular | không có gì — hãy cấu hình export tĩnh trước, sau đó tự đặt "source" thành thư mục đó |
Với bất kỳ thiết lập nào khác, hãy tự đặt "source" thành thư mục build của bạn.
Dòng $schema cung cấp cho trình chỉnh sửa của bạn tính năng tự động hoàn thành và xác thực cho mọi khóa. Tệp là JSON với các bình luận và dấu phẩy ở cuối được cho phép. Nếu một công cụ khác của bạn phải đọc nó dưới dạng JSON thuần túy, hãy đặt tên là tgcloud.json thay thế — thì nó phải là JSON nghiêm ngặt.
Build trước khi bạn push. push tải lên thư mục chính xác như trên đĩa — ngoại trừ các tệp và thư mục có tên bắt đầu bằng dấu chấm, bị bỏ qua — và không bao giờ chạy build của bạn, vì vậy lệnh npx tgcloud push thuần túy sau khi chỉnh sửa frontend sẽ triển khai bản build trước đó. Hãy sử dụng npm run deploy (nó chạy tập lệnh build của bạn, sau đó là tgcloud push), hoặc tự build trước.
Các tùy chọn phục vụ:
spa— phục vụindex.htmlcho các đường dẫn không tồn tại và không có phần mở rộng tệp, vì vậy bộ định tuyến phía client hoạt động khi tải lại. Các đường dẫn có phần mở rộng vẫn bị404.immutable— các mẫu cho các tệp có hash nội dung (/assets/*của Vite) mà trình duyệt có thể lưu vào bộ nhớ cache trong một năm. Chỉ sử dụng nó cho các tệp có hash nội dung. Bất kỳ tệp nào khác khớp ở đây sẽ giữ trạng thái cũ trong trình duyệt trong một năm và không có cách nào để thu hồi nó.headers— tiêu đề phản hồi cho các đường dẫn mà quy tắc khớp (đường dẫn chính xác hoặc tiền tố kết thúc bằng/*). Giá trịnullloại bỏ một tiêu đề.redirects— các quy tắc chuyển hướng, nơi khớp đầu tiên thắng. Một tệp hiện có luôn thắng một quy tắc.statuslà301trừ khi bạn đặt khác."static": "dist"— viết tắt cho{ "source": "dist" }khi bạn không cần gì khác."static": false— loại bỏ trang web đã triển khai trong lần push tiếp theo. Việc xóa khóa này chỉ khiến dự án này không quản lý các tệp tĩnh nữa, và những gì đã triển khai vẫn còn đó.
Trang web được phục vụ từ gốc của máy chủ riêng, vì vậy base (/) mặc định của bộ đóng gói của bạn là chính xác. Telegram Web mở Mini Apps trong iframe, vì vậy đừng thêm X-Frame-Options hoặc chính sách frame-ancestors hạn chế.
Máy chủ tương tự trả lời các
endpoints
của bạn dưới /api/ — đó là nơi Telegram.WebApp.Serverless.call('getProfile', …) đi đến. Đừng đặt các tệp của riêng bạn dưới /api/ trong thư mục build.
Cơ sở dữ liệu
Mỗi bot có cơ sở dữ liệu riêng — một cửa hàng được hỗ trợ bởi SQLite tồn tại giữa các lần gọi và có sẵn cho mọi mô-đun thông qua db. Bạn mô tả các bảng của mình trong schema.js bằng một DSL nhỏ, có định kiểu; bạn đọc và ghi chúng bằng trình tạo truy vấn trôi chảy (fluent query builder); và bạn phát triển chúng với các bản di chuyển đã được xem xét.
Bạn biết Drizzle ORM không? Vậy bạn đã biết cách giao tiếp với cơ sở dữ liệu ở đây rồi. Schema DSL và trình tạo truy vấn tuân theo Drizzle — trình tạo cột,
select().from().where(), các toán tử,onConflictDoUpdate,.returning(), và thẻsqlđều hoạt động theo cách bạn mong đợi, vì vậy việc đọc và ghi dữ liệu là API quen thuộc mà bạn đã sử dụng. Bạn chỉ cần import nó từsdk/db, và một vài đặc điểm cụ thể của nền tảng (đáng chú ý nhất là không có khóa ngoại ) được chỉ ra ở nơi chúng xuất hiện.
Khai báo bảng
Các bảng là export có tên trong schema.js. Gọi table() sẽ xây dựng một mô tả tại thời điểm tải (nó không chạm vào cơ sở dữ liệu); nền tảng khám phá các bảng đã export khi bạn triển khai schema.js và di chuyển cơ sở dữ liệu để khớp.
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?):
namelà tên bảng SQL;columnslà bản đồ của thuộc tính JS → định nghĩa cột;extraslà một callback tùy chọn(t) => ({ … })nơithiển thị các cột (t.createdlà một tham chiếu đến cột đó) — khai báo các chỉ mục và ràng buộc cấp bảng ở đây.
Kiểu cột
| Factory | Kiểu SQLite | Ghi chú |
|---|---|---|
text() |
TEXT | |
integer() |
INTEGER | |
real() |
REAL | alias float() |
numeric() |
NUMERIC | |
blob() |
BLOB | đọc/ghi Uint8Array |
boolean() |
INTEGER | lưu trữ dưới dạng 0/1, đọc thành true/false |
json() |
TEXT | tự động JSON.stringify / JSON.parse |
Đối số tên cột là tùy chọn — bỏ qua nó và khóa JS sẽ được sử dụng. Tùy chọn
modekiểm soát cách các giá trị chuyển đổi giữa SQLite và JavaScript.
mode |
Lưu trữ dạng | Giá trị JS |
|---|---|---|
boolean |
INTEGER 0/1 | boolean |
json |
TEXT (JSON) | bất kỳ object/array nào |
timestamp |
INTEGER (unix seconds) | Date |
timestamp_ms |
INTEGER (unix ms) | Date |
bytes |
BLOB | Uint8Array |
boolean() và json() là viết tắt của integer(name, { mode: 'boolean' }) và text(name, { mode: 'json' }).
blob() đọc và ghi một Uint8Array — runtime không có Node Buffer (và Buffer là một lớp con của Uint8Array, vì vậy đây là loại cơ sở di động). mode ở trên chỉ điều khiển cách một giá trị được mã hóa, độc lập với kiểu lưu trữ của cột: vì vậy blob('col', { mode: 'json' }) là đối tác BLOB của json() — cùng mã hóa JSON, được giữ trong một cột BLOB thay vì TEXT.
TLDR:
blob()sử dụngUint8Array.modeđiều khiển mã hóa, không phải lưu trữ, vì vậyblob(..., { mode: 'json' })lưu trữ JSON dưới dạng BLOB, trong khijson()lưu trữ nó dưới dạng TEXT.
Công cụ sửa đổi cột
Các công cụ sửa đổi cột nối vào một cột.
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' }) // hoặc 'virtual'
text('email').deprecated('replaced by login') // đánh dấu cột để xóa
.default()trên một cộtjson()sẽ tự động mã hóa giá trị cho bạn; mộtsql`…`default được truyền qua nguyên văn..deprecated()là bước cuối cùng — xem Migrations .
Chỉ mục và ràng buộc
Chỉ mục và ràng buộc được khai báo trong callback extras, nơi các cột nằm trong phạm vi.
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})`) , // chỉ mục biểu thức
active: index('idx_active').on(t.userId).where(sql`done = 0`) , // chỉ mục một phần
}));
Các công cụ sửa đổi cấp bảng nối sau table(...): .strict(), .withoutRowid(), .deprecated('lý do').
Không có khóa ngoại
Runtime chạy với PRAGMA foreign_keys tắt. Một khóa ngoại được khai báo sẽ âm thầm trơ trọi — không có cascade, không có bảo vệ mồ côi — điều này còn tệ hơn là không có gì cả, vì vậy DSL khiến nó trở nên bất khả thi. Cụ thể, .references() và foreignKey() cấp bảng sẽ throw lỗi khi được khai báo, và một lược đồ sử dụng chúng sẽ không triển khai được.
Bạn nên mô hình hóa các mối quan hệ bằng các cột thuần túy (userId: integer('user_id')) và thực thi tính toàn vẹn trong mã ứng dụng của bạn: chèn cha trước con, xóa con trước cha, xử lý lỗi đúng cách và quét các mục mồ côi bằng LEFT JOIN … WHERE parent.id IS NULL khi bạn cần.
Sự vắng mặt của khóa ngoại là một sự ràng buộc có chủ đích, không phải sơ suất. Hãy tính đến nó ngay từ đầu khi lập kế hoạch cho bot của bạn.
Truy vấn
db là một trình tạo truy vấn trôi chảy. Mỗi truy vấn đều bất đồng bộ — các phương thức kết thúc (.all(), .get(), .values(), .run()) trả về Promises, vì vậy luôn luôn 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(); // tất cả các hàng
await db.select().from(todos).where(eq(todos.id, 1)).get(); // hàng đầu tiên hoặc null
await db.select().from(todos).values(); // các hàng dưới dạng mảng giá trị
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();
// custom projection: { 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();
// row count — một trợ lý, không phải một điểm kết thúc trình tạo:
await db.$count(todos); // tất cả các hàng
await db.$count(todos, eq(todos.done, false)); // với một bộ lọc
- Có thể nối chuỗi:
.where(),.orderBy(),.limit(),.offset(),.groupBy(),.having(),.distinct(). - Kết thúc:
.all(),.get(),.values(). - Đếm hàng với
db.$count(table, where?)(hoặccount()trong một projection).
Insert, update, delete:
await db.insert(todos).values({ userId: 1, text: 'Mua sữa' }).run();
await db.insert(todos).values([{ text: 'A' }, { text: 'B' }]).run(); // hàng loạt
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();
Lưu ý rằng insert hàng loạt là một câu lệnh, vì vậy nó bị giới hạn bởi giới hạn biến của SQLite (
hàng × cột); nếu bạn vượt quá, việc insert sẽ bị lỗi — hãy tự chia nhỏ chúng.
Các toán tử được import từ 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) với nhiều đối số giống như and(a, b). Đối số thứ hai của so sánh theo mặc định là một giá trị, nhưng có thể là một cột khác hoặc sql`…` — ví dụ eq(a.x, b.y). Các hàm tổng hợp (count/sum/avg/min/max) là các đoạn sql cho một projection .select({ … }).
Raw SQL — khi trình tạo không đủ, hãy sử dụng raw SQL. Chế độ được chọn theo phương thức: db.run cho ghi, db.all cho nhiều hàng, db.get cho một hàng, db.values cho các hàng dưới dạng mảng vị trí.
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, 'mua sữa']]
Một lần ghi trả về một kết quả run — rowsAffected, lastInsertRowid, và rows, mang theo một mệnh đề RETURNING nếu SQL có một mệnh đề. Hãy sử dụng db.values khi một truy vấn dự báo hai cột cùng tên (một join như SELECT u.id, o.id): db.all khóa các hàng theo tên cột, vì vậy id thứ hai sẽ ghi đè lên id thứ nhất.
Thẻ sql`…` chuyển ${value} thành một tham số ràng buộc và ${table.column} thành một định danh, và nối các đoạn sql lồng nhau. Sử dụng sql.raw('…') cho một chữ nguyên văn không có tham số.
Các truy vấn thô không được ràng buộc với một bảng, vì vậy các hàng của chúng quay lại mà không có chuyển đổi chế độ — boolean là
0/1, cột JSON là một chuỗi, timestamp là một số. Chỉ trình tạo ràng buộc bảng mới chuyển đổi giá trị.
Migrations
Cơ sở dữ liệu của bạn thay đổi khi bot phát triển. Nền tảng giữ cho điều đó an toàn bằng cách tách biệt triển khai mã khỏi thay đổi dữ liệu, và bằng cách phân loại mọi thay đổi lược đồ theo mức độ rủi ro.
Triển khai không bao giờ chạm vào cơ sở dữ liệu. Khi bạn npx tgcloud push một schema.js đã thay đổi, nền tảng sẽ ghi lại lược đồ mới và cho bạn biết cơ sở dữ liệu sẽ thay đổi những gì — nhưng không áp dụng gì cả:
npx tgcloud push # triển khai schema.js; báo cáo các thay đổi DB đang chờ
npx tgcloud migrate # xem xét và áp dụng chúng
npx tgcloud migrate tính toán sự khác biệt giữa lược đồ của bạn và cơ sở dữ liệu trực tuyến và hướng dẫn bạn thực hiện. Bạn luôn được hỏi trước khi bất kỳ điều gì được áp dụng. Điều này có nghĩa là một lần triển khai mã thông thường không bao giờ có thể kích hoạt di chuyển dữ liệu do nhầm lẫn.
Mỗi thay đổi đang chờ xử lý mang một trạng thái xác định cách migrate xử lý nó:
| Trạng thái | Ý nghĩa | Trong migrate |
|---|---|---|
| safe | Có tính cộng dồn và không chặn — một bảng, cột hoặc chỉ mục mới | Được áp dụng cùng nhau trong một bước, khi xác nhận |
| warning | Có khả năng gây hại hoặc chậm — xóa một thứ gì đó, hoặc chỉ mục trên bảng lớn | Được hiển thị từng cái một, mỗi cái xác nhận riêng |
| manual | Không thể thực hiện tự động — ví dụ: thay đổi kiểu của cột | Hiển thị với hướng dẫn; bạn thực hiện bằng tay |
| undocumented | Tồn tại trong cơ sở dữ liệu nhưng không có trong lược đồ của bạn | Hiển thị để biết; không được áp dụng |
Các thay đổi an toàn nhanh chóng và có thể đảo ngược về mặt tinh thần, vì vậy chúng đi qua cùng nhau. Mỗi cảnh báo là một sự xác nhận cá nhân, có chủ đích — không có “áp dụng tất cả” cho các thay đổi phá hoại.
Các thay đổi thủ công đi kèm với lý do và hành động được đề xuất. migrate kết thúc với một bản tóm tắt: bao nhiêu thay đổi đã được áp dụng, bỏ qua, chờ sửa lỗi thủ công, hoặc không có trong lược đồ của bạn.
Xem
migrateđể biết các cờ (--dry-run,--safe,--yes,--local).
Xóa bỏ mọi thứ
Việc xóa một bảng hoặc cột khỏi schema.js không xóa nó — điều đó sẽ khiến việc xóa nhầm trở nên thảm họa. Để xóa một thứ gì đó, hãy đánh dấu nó là deprecated:
// xóa một cột
text('email').deprecated('replaced by login')
// xóa cả một bảng
export const oldSessions = table('old_sessions', { /* … */ }).deprecated('unused');
Trong lần migrate tiếp theo, đối tượng deprecated hiển thị dưới dạng cảnh báo xóa mà bạn xác nhận riêng. Khi nó đã biến mất, hãy xóa khai báo.
Thay đổi kiểu của cột
Các thay đổi kiểu là thủ công — SQLite không phải lúc nào cũng có thể thực hiện chúng tại chỗ, và việc ép kiểu các giá trị hiện có là một quyết định phán đoán. migrate sẽ hiển thị thay đổi và lý do của nó; hãy tự thực hiện với raw SQL (db.run(...)), thường bằng cách tạo một cột hoặc bảng mới, sao chép dữ liệu và hoán đổi.
SDK
Tại runtime, một mô-đun có một thư viện: sdk. Nó gộp ba thứ mà một bot backend cần — cơ sở dữ liệu, Telegram Bot API, và HTTP ngoại vi — không cần cài đặt và không cần cấu hình thông tin xác thực. Cơ sở dữ liệu (db) được đề cập trong
The database
; phần này bao gồm api, fetch, và global console.
import { db, api, fetch, InputFile, BotApiError, EndpointError } from 'sdk'; // toàn bộ bề mặt
// hoặc từ các mô-đun con:
import { table, integer, text, eq, sql } from 'sdk/db';
import { api } from 'sdk/api';
import { fetch } from 'sdk/fetch';
| Import | Nó là gì |
|---|---|
db |
Cơ sở dữ liệu — trình tạo truy vấn và schema DSL → The database |
api |
Telegram Bot API — api.sendMessage(...) →
bên dưới
|
fetch |
HTTP ngoại vi → bên dưới |
InputFile |
Một tệp (bytes + tên) để tải lên — hoạt động với cả api và fetch →
Files
|
EndpointError |
Từ chối cuộc gọi của Mini App với thông báo nó có thể hiển thị → Endpoints |
Import các mô-đun dự án của riêng bạn bằng đường dẫn tương đối với phần mở rộng .js (from '../schema.js', from '../lib/cart.js'). Xem
The module system
.
The Bot API
api cung cấp cho bạn toàn bộ
Telegram Bot API
. Gọi bất kỳ phương thức nào dưới dạng api.<phương thức>(params). Mọi phương thức Bot API hiện tại — và tương lai — đều hoạt động mà không cần cập nhật SDK.
import { api } from 'sdk';
const me = await api.getMe(); // → kết quả đã mở gói
await api.sendMessage({ chat_id: id, text: 'Hello!' });
await api.editMessageText({ chat_id, message_id, text: 'Đã cập nhật' });
await api.answerCallbackQuery({ callback_query_id, text: 'Xong' });
Phong bì phản hồi được mở gói. Bot API thường gói kết quả trong { ok: true, result: … }. api trả về trực tiếp result — getMe() phân giải thành đối tượng người dùng, không phải một trình bao bọc. Các tham số sử dụng tên snake_case của chính Bot API (chat_id, message_id, reply_markup, …).
Các lỗi throw BotApiError. Khi Bot API trả về { ok: false }, cuộc gọi throw một BotApiError thay vì trả về một giá trị falsy, vì vậy bạn không thể vô tình bỏ qua nó. Lỗi mang theo .code (mã lỗi Bot API), .description (thông báo có thể đọc được), .method (phương thức nào bị lỗi) và .parameters (dữ liệu bổ sung như retry_after trên 429, hoặc migrate_to_chat_id). Hãy bắt nó để xử lý lỗi dự kiến và rethrow phần còn lại:
import { api, BotApiError } from 'sdk';
try {
await api.deleteMessage({ chat_id, message_id });
} catch (e) {
if (e instanceof BotApiError && e.code === 400) {
// 400 = tin nhắn đã biến mất; điều đó ổn ở đây.
} else {
throw e;
}
}
Files
Làm việc với bytes của tệp, không chỉ file_ids — tải lên tệp mới và tải xuống các tệp hiện có.
Upload — InputFile. Gói bytes thô trong một InputFile (một trình giữ thuần túy: bytes, tên tệp và type MIME tùy chọn) và truyền nó đến bất cứ nơi nào phương thức Bot API mong đợi một tệp:
import { api, InputFile } from 'sdk';
const doc = new InputFile(bytes, 'report.pdf', { type: 'application/pdf' });
await api.sendDocument({ chat_id, document: doc });
// các tệp lồng trong params cũng hoạt động — ví dụ: một 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' }) },
]});
Các bytes được truyền đến Telegram như một lần tải lên multipart thực — tệp không được buffer toàn bộ vào một cuộc gọi duy nhất đến nền tảng — nhưng các giới hạn Bot API của Telegram vẫn được áp dụng: một bot hiện có thể tải lên tới 50 MB mỗi tệp (và 10 MB cho ảnh).
Download — theo file_id. Hai trợ lý kéo bytes của tệp về. Nền tảng phân giải file_id cho bạn (nó chạy getFile và tìm nạp nội dung đằng sau hậu trường), vì vậy bạn không bao giờ chạm vào mã thông báo bot hoặc URL tải xuống:
const bytes = await api.getFileContent(file_id); // → Uint8Array (toàn bộ tệp)
const { file, body } = await api.getFileStream(file_id); // truyền phát nó thay thế
file.file_unique_id; // thông tin getFile
for await (const chunk of body) { /* Uint8Array */ } // hoặc buffer nó: await stream.bytes()
Bot API của Telegram giới hạn tải xuống
getFileở mức 20 MB, vì vậy đó là giới hạn cho cả hai trợ lý.
HTTP
fetch là một client giống như fetch để gọi thế giới bên ngoài — API bên thứ ba, webhook, bất cứ thứ gì qua 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();
Phản hồi phản chiếu nền tảng web: res.status, res.statusText, res.ok (true cho 200–299), res.url, res.headers (.get(), .has(), .keys(), .entries()), và các trình đọc nội dung await res.json() / await res.text().
Bạn cũng có thể đọc nội dung tăng dần dưới dạng stream —
for await (const chunk of res.body) { … }— đó là cách bạn tiêu thụ các sự kiện được gửi từ máy chủ hoặc đầu ra từng mã thông báo từ AI APIs.
Các trợ lý nội dung đặt Content-Type khớp cho bạn:
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
Nội dung yêu cầu cũng có thể mang theo tệp và bytes thô: truyền một Uint8Array, một InputFile, hoặc một FormData (cho multipart/form-data) làm body, và các bytes được truyền ra ngoài — vì vậy bạn có thể tải lên một API bên thứ ba theo cách tương tự như cách bạn gửi tệp đến 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', 'từ bot của tôi');
const res = await fetch('https://api.example.com/upload', { method: 'POST', body: form });
Nếu không, nó hoạt động như fetch tiêu chuẩn mà bạn đã biết, với hai ràng buộc:
- Phản hồi được đọc dưới dạng văn bản —
await res.json()/await res.text(), hoặc truyền phát từng phần vớires.body. - Tổng phản hồi bị giới hạn ở mức 30 MB. Giới hạn đó bao gồm toàn bộ phản hồi — truyền phát với
res.bodycho phép bạn xử lý một nội dung lớn tăng dần, nhưng nó không nâng giới hạn.
Logging
Global console tiêu chuẩn có sẵn — không có gì để import, nó chỉ ở đó như trong bất kỳ JavaScript nào. Đầu ra của nó được thu thập và hiển thị bởi npx tgcloud run, điều này làm cho nó trở thành công cụ gỡ lỗi chính của bạn trong quá trình phát triển.
console.log('đang xử lý', { chatId: id }); // log / debug — thuần túy
console.info('đã bắt đầu'); // info — xanh dương
console.warn('giới hạn tốc độ'); // warn — vàng
console.error(err); // error — đỏ, với stack trace
Mỗi dòng được gắn thẻ với [file:line] mà nó đến từ đó. console.error và console.trace nối thêm toàn bộ stack, trong khi console.warn thì không. Khi bạn npx tgcloud run một mô-đun, các dòng này được in với tiền tố màu theo cấp độ, thời gian kể từ khi chạy bắt đầu, và nguồn gốc — xem
run
.
Giao diện dòng lệnh
tgcloud là cầu nối giữa thư mục dự án của bạn và đám mây. Nó tạo khung dự án, hiển thị những gì đã thay đổi, triển khai, chạy mô-đun và áp dụng di chuyển cơ sở dữ liệu. Nó yêu cầu
Node.js
18 trở lên. Hai cách để lấy nó:
# Khuyên dùng — tạo một dự án với CLI được cài đặt vào đó:
npm create @tgcloud/bot example_bot
# Hoặc cài đặt CLI toàn cục và init một thư mục trống:
npm install -g @tgcloud/cli
tgcloud init
Gói npm là @tgcloud/cli, và lệnh nó cài đặt là tgcloud. Mọi lệnh đều hoạt động từ bất kỳ thư mục con nào của dự án.
| Lệnh | Mục đích |
|---|---|
init
|
Tạo khung dự án mới trong thư mục hiện tại |
add
|
Tạo khung mô-đun mới (handler, endpoint hoặc mô-đun lib) |
login
|
Liên kết dự án với một bot (lưu token) |
status
|
Hiển thị những gì đã thay đổi cục bộ so với đám mây |
diff
|
Hiển thị thay đổi từng dòng |
push
|
Triển khai các mô-đun đã thay đổi và build của Mini App |
migrate
|
Áp dụng thay đổi lược đồ vào cơ sở dữ liệu |
run
|
Thực thi mô-đun trên nền tảng mà không cần triển khai |
fetch
|
Kiểm tra những gì đã triển khai mà không thay đổi tệp cục bộ |
pull
|
Đưa các tệp cục bộ khớp với đám mây |
reset
|
Loại bỏ thay đổi cục bộ; khôi phục từ trạng thái đám mây |
webhook
|
Kiểm tra và đồng bộ lại webhook do nền tảng quản lý |
completion
|
In tập lệnh tự động hoàn thành shell (bash/zsh/fish) |
upgrade
|
Di chuyển bố cục dự án trước 0.2.0 vào tgcloud/ |
Authentication
Một dự án gắn liền với một bot bằng token của nó, có dạng app<id>:<secret>. Phần app<id> là công khai và có thể được in ra; secret không bao giờ xuất hiện trong nhật ký hoặc lỗi.
Token được phân giải theo thứ tự này:
TGCLOUD_TOKENbiến môi trường — cho CI; không bao giờ ghi vào đĩa..tgcloud/credentials— được ghi bởinpx tgcloud login.- Không có cái nào — lỗi chỉ cho bạn đến
npx tgcloud login.
CLI không bao giờ yêu cầu token giữa lệnh. Đăng nhập luôn là bước login rõ ràng. Nếu một token đã lưu không hợp lệ, CLI sẽ xóa nó và yêu cầu bạn login lại.
init
npx tgcloud init
Tạo khung dự án mới trong thư mục hiện tại: thư mục tgcloud/ với schema.js, lib/, handlers/ và trình xử lý khởi đầu, cộng với tgcloud.jsonc, AGENTS.md, docs/, và thư mục trạng thái .tgcloud/. Chạy nó trong thư mục gốc của một dự án frontend hiện có — nó thêm thư mục tgcloud/ và tài liệu, không bao giờ ghi đè tệp của bạn. Nếu nó nhận dạng được frontend, nó cũng trỏ tgcloud.jsonc vào thư mục build và thêm tập lệnh deploy (build, sau đó push) vào package.json của bạn — chỉ khi bạn chưa có tập lệnh deploy. Xem
Mini App front‑end
.
init từ chối tạo một dự án bên trong một dự án khác. Chạy lại nó trong thư mục gốc của dự án là ổn và chỉ thêm những gì còn thiếu.
add
npx tgcloud add <target>
Tạo khung một mô-đun mới duy nhất, đã kết nối và sẵn sàng chỉnh sửa — handler, endpoint hoặc mô-đun lib/.
npx tgcloud add handlers/callback_query # trình xử lý cập nhật mới
npx tgcloud add endpoints/getProfile # endpoint mới cho Mini App
npx tgcloud add lib/cart # mô-đun dùng chung mới
Lưu ý rằng
addkhông bao giờ ghi đè một tệp hiện có.
<target> là đường dẫn của mô-đun (dấu .js ở cuối là tùy chọn). Đối với handlers/, tên phải là một loại cập nhật Telegram. Nền tảng quảng cáo tập hợp hợp lệ, vì vậy một tên không hợp lệ bị từ chối ngay từ đầu. handlers/ và endpoints/ là phẳng, trong khi lib/ có thể được lồng (lib/payments/stripe).
Tên mô-đun là bắt buộc. Cung cấp chỉ thư mục là lỗi — nhưng là lỗi hữu ích: đối với handlers/, nó liệt kê các loại cập nhật bạn chưa có, để bạn có thể sao chép một cái.
$ npx tgcloud add handlers
Lỗi: Chỉ định tên, ví dụ: "handlers/callback_query". Các loại handlers/ khả dụng: callback_query, inline_query, chat_member, …
<Tab>hoàn thành cung cấp cùng một tập hợp — xemcompletion.
Tệp được tạo có export default trực tiếp, vì vậy trình xử lý hoạt động ngay khi bạn triển khai — không có gì để uncomment. Triển khai mô-đun mới với
push
.
login
npx tgcloud login
Yêu cầu mã thông báo truy cập CLI của bạn — từ
@BotFather
→ bot của bạn → Serverless → CLI Access → Access token, một mã thông báo riêng biệt với
mã thông báo API bot
— xác thực nó với nền tảng và lưu vào .tgcloud/credentials.
loginlà lệnh duy nhất yêu cầu mã thông báo, và nó yêu cầu một terminal tương tác. Trong CI, hãy đặtTGCLOUD_TOKENthay thế.
status
npx tgcloud status
Hiển thị, theo tệp, những gì đã thay đổi giữa thư mục làm việc của bạn và bản sao đã triển khai: đã sửa đổi, mới, đã xóa, không thay đổi. Nó hoạt động ngoại tuyến, so sánh với trạng thái đám mây từ lần push, pull hoặc fetch cuối cùng của bạn. Nó cũng cảnh báo về các tệp .js lạ bên trong tgcloud/ sẽ không được triển khai.
Đối với dự án có Mini App, status cũng hiển thị những gì đã thay đổi trong thư mục build của nó kể từ lần triển khai cuối cùng.
diff
npx tgcloud diff
Giống như status, nhưng hiển thị sự khác biệt thực tế từng dòng cho các mô-đun đã thay đổi. Cũng hoạt động ngoại tuyến.
push
npx tgcloud push [files...]
Triển khai dự án của bạn lên đám mây trong một đợt nguyên tử.
Với không có đối số, nó triển khai toàn bộ dự án, và trạng thái triển khai được làm cho khớp chính xác với thư mục của bạn — các mô-đun bạn đã xóa cục bộ sẽ bị xóa trên đám mây.
Với các đối số tệp hoặc thư mục (npx tgcloud push tgcloud/handlers/message.js, npx tgcloud push handlers/), nó chỉ triển khai các thay đổi trong các tệp đó. Một push mục tiêu không bao giờ xóa các mô-đun khác. Một mục tiêu có thể được viết là đường dẫn tệp (tgcloud/handlers/message.js), đường dẫn bên trong tgcloud/ (handlers/message.js) hoặc tên mô-đun (handlers/message) — tất cả đều có nghĩa là cùng một mô-đun.
Tùy chọn duy nhất của nó là --force — để bỏ qua kiểm tra đồng thời và ghi đè bất cứ thứ gì trên đám mây. Chỉ sử dụng nó khi bạn chắc chắn (xem
Staying in sync
).
Nếu tgcloud.jsonc có tên thư mục tĩnh, một push đầy đủ sẽ triển khai nó trong cùng đợt — chỉ các tệp đã thay đổi mới được tải lên — và in địa chỉ của trang web. Build trước, vì push không bao giờ chạy build của bạn (npm run deploy làm cả hai). npx tgcloud push dist/ triển khai riêng các tệp tĩnh, và một push chỉ nêu tên các mô-đun sẽ để nguyên chúng.
Sau khi triển khai, nếu schema.js đã thay đổi và cơ sở dữ liệu không đồng bộ, push in ra bản tóm tắt các thay đổi đang chờ và gợi ý npx tgcloud migrate. Nó không bao giờ tự áp dụng chúng.
migrate
npx tgcloud migrate
Áp dụng thay đổi lược đồ vào cơ sở dữ liệu. Nó tính toán sự khác biệt giữa schema.js và cơ sở dữ liệu trực tuyến, sau đó hướng dẫn bạn từng bước một với bộ đếm [N/M] đang chạy:
- Bản tóm tắt ngắn gọn tất cả các thay đổi đang chờ.
- Các thay đổi an toàn, được áp dụng cùng nhau trong một bước duy nhất theo xác nhận của bạn.
- Các cảnh báo (xóa, hoạt động chậm), từng cái một, mỗi cái xác nhận riêng.
- Các thay đổi thủ công, hiển thị với lý do và hành động đề xuất, không áp dụng tự động.
- Các đối tượng không có tài liệu (trong cơ sở dữ liệu nhưng không có trong lược đồ của bạn), hiển thị để biết.
Nó kết thúc với bản tóm tắt: đã áp dụng, bỏ qua, chờ sửa lỗi thủ công, không có trong lược đồ.
Xem Migrations để biết mô hình.
Các tùy chọn: --dry-run (in mọi thứ, không áp dụng gì), --safe (tự động áp dụng thay đổi an toàn, bỏ qua cảnh báo), --yes (tự động áp dụng thay đổi an toàn và mọi cảnh báo, bỏ qua thủ công — sử dụng cẩn thận), --local (so sánh với schema.js cục bộ thay vì cái đã triển khai). Nếu không có cờ, migrate yêu cầu terminal và lỗi trong môi trường không tương tác thay vì đoán.
run
npx tgcloud run <module> [args] [--ctx <json5>]
Thực thi một handler hoặc endpoint trên nền tảng mà không triển khai, sử dụng các tệp cục bộ hiện tại của bạn. Đây là vòng lặp bên trong nhanh để kiểm tra logic.
<module>— tên trần (được tìm kiếm dướihandlers/vàendpoints/) hoặc đường dẫn nhưhandlers/message. Một tên trần tồn tại trong cả hai thư mục phải được đánh vần với thư mục của nó.[args]— payload truyền cho handler, viết bằng JSON5 để bạn có thể bỏ qua dấu ngoặc kép khóa. Đó là đối tượng loại cập nhật mà handler của bạn nhận được (ví dụ: Message chohandlers/message), hoặcinputcủa endpoint, và nó phải là một đối tượng — không phải mảng hoặc chuỗi trần.--ctx <json5>— đối tượng ngữ cảnh của mô-đun (đối số thứ hai của nó), cũng là JSON5. Sử dụng nó để cung cấp những gì mã của bạn đọc từctx— cập nhật thô cho handler,--ctx '{ update: { update_id: 1 } }', hoặc dữ liệu init cho endpoint,--ctx '{ initData: { user: { id: 1 } } }'.runkhông xác minh nó, vì bạn đang kiểm tra logic của mình, không phải các kiểm tra của nền tảng.
npx tgcloud run handlers/message '{ chat: { id: 1 }, text: "hi" }'
Nền tảng chạy mô-đun dựa trên không gian mô-đun được lắp ráp từ dự án cục bộ của bạn (vì vậy mã lib/ đã thay đổi cục bộ cũng được sử dụng) và trả về giá trị trả về, bất kỳ thứ gì được ghi lại bằng console.*, và thời gian trôi qua. Đọc các đối số lớn từ một tệp với npx tgcloud run handlers/message "$(cat message.json5)".
fetch
npx tgcloud fetch
Kiểm tra những gì hiện đang được triển khai, mà không chạm vào các tệp làm việc của bạn. Sau đó, status và diff so sánh với trạng thái đám mây mới nhất. Hữu ích để xem xung đột trước khi quyết định cách giải quyết.
pull
npx tgcloud pull
Đưa dự án cục bộ của bạn khớp với đám mây bằng cách cập nhật các tệp làm việc của bạn sang trạng thái đã triển khai.
pull không bao giờ ghi lại thư mục build Mini App của bạn.
reset
npx tgcloud reset
Loại bỏ các thay đổi cục bộ của bạn và khôi phục các mô-đun từ trạng thái đám mây được biết cuối cùng. Sử dụng nó để vứt bỏ một thử nghiệm. Thư mục build của Mini App không bị chạm vào.
webhook
npx tgcloud webhook
npx tgcloud webhook sync [--drop-pending]
Telegram gửi các cập nhật đến bot của bạn thông qua webhook, cái mà nền tảng quản lý cho bạn — bạn không bao giờ tự trỏ nó đến bất cứ đâu. npx tgcloud webhook hiển thị trạng thái hiện tại của nó: URL, danh sách allowed_updates, bao nhiêu cập nhật đang chờ, lỗi gửi cuối cùng (nếu có), và liệu nó có in sync với các handler đã triển khai của bạn hay không.
“In sync” có nghĩa là webhook trỏ đến nền tảng và allowed_updates của nó khớp với các handler bạn đã triển khai — vì vậy Telegram gửi chính xác các loại cập nhật bạn xử lý, và không có gì khác. Triển khai một handler mới (hoặc xóa một cái) có thể khiến webhook không đồng bộ cho đến khi nó được làm mới; npx tgcloud status cũng gắn cờ điều này.
npx tgcloud webhook sync sửa nó — nó trỏ lại webhook đến nền tảng và xây dựng lại allowed_updates từ các handler đã triển khai của bạn. Thêm --drop-pending để loại bỏ các cập nhật Telegram đã xếp hàng trước khi đồng bộ (nếu không chúng sẽ được gửi sau khi webhook hoạt động trở lại).
completion
Lưu ý: tab‑completion chỉ hoạt động khi
tgcloudtrần nằm trênPATHcủa bạn — vì vậy hãy cài đặt nó toàn cục (npm install -g @tgcloud/cli) hoặc đưa binary vàoPATH. Nó không thể móc vàonpx.
tgcloud completion <bash|zsh|fish>
In tập lệnh tự động hoàn thành shell ra stdout. Kích hoạt nó một lần, sau đó <Tab> hoàn thành các lệnh, cờ, thư mục mô-đun, các loại cập nhật handler bạn chưa có, và các mô-đun có thể chạy cục bộ của bạn.
# bash — cần gói bash-completion:
echo 'eval "$(tgcloud completion bash)"' >> ~/.bashrc
# zsh — đảm bảo `autoload -U compinit && compinit` chạy trong ~/.zshrc:
echo 'eval "$(tgcloud completion zsh)"' >> ~/.zshrc
# fish:
tgcloud completion fish > ~/.config/fish/completions/tgcloud.fish
Khởi động lại shell của bạn (hoặc re‑source tệp) sau đó. Chạy tgcloud completion mà không có shell sẽ in lại các hướng dẫn này.
upgrade
npx tgcloud upgrade [--dry-run]
Di chuyển một lần cho các dự án được tạo trước CLI 0.2.0, vốn giữ schema.js, lib/ và handlers/ ở thư mục gốc dự án. Nó di chuyển chúng vào tgcloud/. Liên kết của bạn với bot được giữ nguyên, và không có gì cần phải triển khai lại. --dry-run in ra các bước di chuyển dự kiến mà không chạm vào gì cả. Các lệnh khác không chạy trên bố cục cũ cho đến khi bạn nâng cấp.
Staying in sync
Mỗi dự án có một revision tăng đơn điệu trên đám mây, tăng lên sau mỗi lần triển khai. CLI nhớ revision cuối cùng nó đã đồng bộ và gửi nó sau mỗi lần push. Nếu đám mây đã đi trước — vì một máy hoặc đồng đội khác đã triển khai — push sẽ bị từ chối thay vì ghi đè âm thầm lên công việc của họ, và CLI cung cấp ba cách để đi tiếp:
npx tgcloud fetch # xem những gì đã thay đổi trên đám mây, sau đó kiểm tra lại
npx tgcloud pull # đưa những cái mới nhất vào các tệp làm việc của bạn
npx tgcloud push --force # ghi đè trạng thái đám mây (nguy hiểm)
Kiểm tra optimistic‑concurrency này là lý do tại sao bạn có thể chia sẻ một bot trong nhóm mà không cần quá trình triển khai lockstep. Các lệnh thoát non‑zero khi thất bại — một triển khai bị từ chối, một di chuyển thất bại, lỗi xác thực, một mô-đun bị throw trong run — vì vậy chúng cấu thành gọn gàng trong các tập lệnh và pipeline CI.