Phát triển bot
Tạo bot
Tạo bot bằng @BotFather, nhận và trả lời tin nhắn, rồi đến nút bấm, lệnh, ảnh và nhóm — hướng dẫn làm theo từ đầu đến cuối.
Khi hoàn thành hướng dẫn này, bạn sẽ có một con bot biết trả lời tin nhắn, gắn nút bấm, dùng menu lệnh và ảnh, và làm việc được cả trong nhóm. Bạn cần ứng dụng Pabal, một máy tính để chạy chương trình bot, và một trong hai: Python 3.10 trở lên hoặc Node.js 18 trở lên.
Bot hoạt động như thế nào
Bot của Pabal là một tài khoản do chương trình điều khiển. Tin nhắn gửi cho bot được xếp vào hộp thư của bot; chương trình bot hỏi máy chủ "Có gì mới không?" (getUpdates) để lấy về, rồi gửi câu trả lời (sendMessage). Chương trình bot chạy bên ngoài máy chủ, trên máy tính của bạn.
Giải thích sơ đồ
- Ba trục: bên trái là người dùng ứng dụng, ở giữa là máy chủ Pabal, bên phải là chương trình bot của bạn. Các đường chấm dọc là chiều thời gian trôi.
- Mũi tên xám là request do bot chủ động gửi, mũi tên xanh là tin nhắn được chuyển đi như kết quả của các request đó. Chương trình bot chỉ gửi request; máy chủ không chủ động liên lạc với bot trước (trừ khi dùng webhook).
- Việc chờ ở bước ① (long polling) là điểm mấu chốt: với
timeout=30, khi chưa có tin mới, máy chủ không trả về kết quả rỗng ngay mà giữ request tối đa 30 giây, và trả về (③) ngay khi có tin nhắn đến (②). Nhờ vậy bot phản hồi nhanh mà số request lại ít. - offset ở bước ⑥ là dấu "đã nhận": gửi
update_idcuối cùng đã xử lý cộng thêm 1 thì mọi update từ số đó trở xuống sẽ bị xóa khỏi máy chủ. Nếu không tăng offset, bạn sẽ nhận đi nhận lại cùng một update. - Bước ⑤ đi cùng đường với tin nhắn do người gửi: câu trả lời của bot được đẩy đến mọi thiết bị của người dùng và cũng hiện lên trong danh sách trò chuyện.
1. Tạo bot bằng @BotFather
Bot được tạo bằng cách trò chuyện với @BotFather trong ứng dụng Pabal. BotFather là một bot có sẵn bên trong máy chủ Pabal.
- Nhập
BotFathervào ô tìm kiếm của ứng dụng rồi mở BotFather. Bấm Bắt đầu, bạn sẽ nhận được danh sách lệnh. - Gửi
/newbot. - Gửi tên của bot. Đây là tên hiển thị trong danh sách trò chuyện nên có thể dùng tiếng Việt có dấu.
- Gửi tên người dùng của bot: dài 5–32 ký tự gồm chữ cái Latin, chữ số và dấu gạch dưới, bắt đầu bằng chữ cái, và bắt buộc phải kết thúc bằng
bot. - Khi nhận được câu trả lời có chứa token là xong. Hãy sao chép và giữ lại token.
Hiện tại BotFather trả lời bằng tiếng Hàn. Trong đoạn hội thoại trên, BotFather lần lượt hỏi tên của bot (tên hiển thị trong danh sách trò chuyện), rồi hỏi tên người dùng của bot, và cuối cùng báo đã tạo xong @hello_test_bot, gửi kèm token bot cùng lời nhắc hãy giữ token an toàn như mật khẩu.
| Lệnh BotFather | Tác dụng |
|---|---|
/newbot | Tạo bot mới (tên → tên người dùng → token) |
/mybots | Danh sách bot bạn đã tạo |
/token | Xem lại token của bot |
/revoke | Cấp lại token — token cũ mất hiệu lực ngay, các kết nối đang dùng token cũ cũng bị ngắt |
/setcommands | Thiết lập menu lệnh (mỗi dòng một mục lệnh - mô tả) |
/deletebot | Xóa bot — xác nhận bằng cách gửi 네, 삭제합니다 (nghĩa là "Vâng, xóa đi"). Tên người dùng được giải phóng để có thể dùng lại |
/cancel | Hủy thao tác đang làm dở |
Nếu gửi kèm tên người dùng như /token @hello_test_bot, bạn sẽ bỏ qua bước BotFather hỏi "Bot nào?" (어느 봇인가요?).
2. Xử lý token
Token có dạng <ID-bot>:<bí-mật>. Dãy số phía trước là ID người dùng của bot, phần phía sau là bí mật. Chỉ một token là đủ để điều khiển bot hoàn toàn, vì vậy hãy xử lý nó như một mật khẩu.
- Đừng viết token vào mã nguồn; hãy để trong biến môi trường (
BOT_TOKEN) hoặc kho bí mật. Đừng đẩy nó lên kho mã công khai. - Nếu bị lộ, gửi
/revokecho BotFather. Token cũ bị từ chối ngay (401 Unauthorized), và phiên bot đang kết nối MTProto bằng token cũ cũng bị ngắt. - Token nằm trong địa chỉ (URL), nên đừng để chương trình bot ghi địa chỉ request vào log. Máy chủ Pabal cũng không ghi địa chỉ Bot API vào log.
3. Request đầu tiên — getMe
Địa chỉ của mọi request là https://pabal.me/bot<token>/<phương-thức>. Hãy dùng getMe để kiểm tra token có đúng không.
export BOT_TOKEN='100003:AbCdEf…'
curl -s "https://pabal.me/bot$BOT_TOKEN/getMe"# pip install requests
import os
import requests
r = requests.get(f"https://pabal.me/bot{os.environ['BOT_TOKEN']}/getMe", timeout=10)
print(r.json())// Node.js 18 trở lên — có sẵn fetch
const res = await fetch(`https://pabal.me/bot${process.env.BOT_TOKEN}/getMe`);
console.log(await res.json());Nếu thành công, kết quả trông như sau. Mọi phản hồi đều là JSON chứa ok và result (khi thành công) hoặc error_code và description (khi thất bại).
{
"ok": true,
"result": {
"id": 100003,
"is_bot": true,
"first_name": "Bot Xin Chào",
"username": "hello_test_bot",
"can_join_groups": true,
"can_read_all_group_messages": true,
"supports_inline_queries": false,
"can_connect_to_business": false,
"has_main_web_app": false
}
}
Nếu token sai, bạn nhận HTTP 401 kèm {"ok": false, "error_code": 401, "description": "Unauthorized"}.
4. Nhận tin nhắn — getUpdates
Mở bot trong ứng dụng, bấm Bắt đầu hoặc gửi một tin bất kỳ, rồi lấy tin mới về.
curl -s "https://pabal.me/bot$BOT_TOKEN/getUpdates?timeout=30"
{
"ok": true,
"result": [
{
"update_id": 1,
"message": {
"message_id": 1,
"from": { "id": 100001, "is_bot": false, "first_name": "Mai" },
"chat": { "id": 100001, "first_name": "Mai", "type": "private" },
"date": 1789805661,
"text": "/start",
"entities": [ { "type": "bot_command", "offset": 0, "length": 6 } ]
}
}
]
}
- update_id: số thứ tự tăng thêm 1 sau mỗi update. Sau khi xử lý, truyền
offset=update_id+1ở request tiếp theo thì mọi update từ số đó trở xuống được coi là "đã nhận" và bị xóa. - timeout: số giây chờ khi chưa có tin mới (0–50). Nếu là 0 thì trả về danh sách rỗng ngay lập tức. Nên dùng 25–30.
- chat.id: nơi gửi câu trả lời. Với trò chuyện 1:1 đó là ID của người dùng (số dương), với nhóm là số âm.
- Có ba loại update có thể nhận:
message(tin nhắn mới),edited_message(tin nhắn đã sửa),callback_query(bấm nút).
Các update chưa được lấy về được giữ trong bộ nhớ máy chủ, tối đa 1.000 update gần nhất cho mỗi bot. Khi máy chủ khởi động lại, những update chưa lấy sẽ mất (bản thân tin nhắn vẫn còn trong cuộc trò chuyện). Đừng để bot tắt quá lâu.
5. Gửi câu trả lời — sendMessage
curl -s "https://pabal.me/bot$BOT_TOKEN/sendMessage" \
-H 'Content-Type: application/json' \
-d '{"chat_id": 100001, "text": "Chào bạn!"}'requests.post(f"https://pabal.me/bot{os.environ['BOT_TOKEN']}/sendMessage",
json={"chat_id": 100001, "text": "Chào bạn!"}, timeout=10)await fetch(`https://pabal.me/bot${process.env.BOT_TOKEN}/sendMessage`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ chat_id: 100001, text: 'Chào bạn!' }),
});Bạn có thể gửi tham số theo cách nào tiện nhất: phần thân JSON, form (application/x-www-form-urlencoded), multipart/form-data khi tải tệp lên, hoặc chuỗi truy vấn sau địa chỉ. Kết quả là tin nhắn đã gửi (Message).
Bot chỉ gửi được cho một người sau khi người đó đã gửi cho bot ít nhất một tin nhắn. Nếu không, bạn sẽ nhận 403 Forbidden: bot can't initiate conversation with a user. Quy tắc này giống Telegram.
6. Hoàn thành bot lặp lại lời bạn
Lặp đi lặp lại việc nhận và gửi là bạn đã có một con bot. Đây là toàn bộ mã, viết không cần thư viện.
# echo.py — pip install requests
# Chạy: BOT_TOKEN='100003:…' python3 echo.py
import os
import requests
API = f"https://pabal.me/bot{os.environ['BOT_TOKEN']}"
def call(method, **params):
r = requests.post(f"{API}/{method}", json=params, timeout=60)
data = r.json()
if not data["ok"]:
raise RuntimeError(f"{method}: {data['description']}")
return data["result"]
offset = 0
print("Bot đã chạy. Nhấn Ctrl+C để dừng")
while True:
for update in call("getUpdates", offset=offset, timeout=30):
offset = update["update_id"] + 1 # đánh dấu đã nhận
message = update.get("message")
if message and "text" in message:
call("sendMessage", chat_id=message["chat"]["id"], text=message["text"])// echo.mjs — Node.js 18 trở lên, không cần thư viện
// Chạy: BOT_TOKEN='100003:…' node echo.mjs
const API = `https://pabal.me/bot${process.env.BOT_TOKEN}`;
async function call(method, params = {}) {
const res = await fetch(`${API}/${method}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(params),
});
const data = await res.json();
if (!data.ok) throw new Error(`${method}: ${data.description}`);
return data.result;
}
let offset = 0;
console.log('Bot đã chạy. Nhấn Ctrl+C để dừng');
for (;;) {
const updates = await call('getUpdates', { offset, timeout: 30 });
for (const update of updates) {
offset = update.update_id + 1; // đánh dấu đã nhận
const message = update.message;
if (message?.text) {
await call('sendMessage', { chat_id: message.chat.id, text: message.text });
}
}
}7. Xây dựng bằng thư viện
Các thư viện bot cho Telegram đều có tùy chọn đổi địa chỉ máy chủ. Chỉ với tùy chọn đó, bot chạy được nguyên vẹn trên Pabal. Khi chuyển một bot viết cho Telegram sang, bạn cũng chỉ cần đổi mỗi thiết lập này, rồi lấy token mới từ BotFather của Pabal.
| Thư viện | Thiết lập cần đổi | Phiên bản đã kiểm tra |
|---|---|---|
| python-telegram-bot | .base_url("https://pabal.me/bot"), .base_file_url("https://pabal.me/file/bot") | 22.8 |
| aiogram | AiohttpSession(api=TelegramAPIServer.from_base("https://pabal.me")) | 3.31 |
| Không dùng thư viện (HTTP) | Phần đầu địa chỉ https://api.telegram.org → https://pabal.me | — |
# hello_bot.py — pip install python-telegram-bot
# Chạy: BOT_TOKEN='100003:…' python3 hello_bot.py
import os
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import (Application, CallbackQueryHandler, CommandHandler, ContextTypes,
MessageHandler, filters)
SERVER = "https://pabal.me"
def buttons():
return InlineKeyboardMarkup([[InlineKeyboardButton("👍 Thích", callback_data="like"),
InlineKeyboardButton("🔢 Tăng số", callback_data="count")]])
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text("Xin chào! Hãy thử bấm nút.", reply_markup=buttons())
async def button(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
if query.data == "like":
await query.answer("Cảm ơn bạn!") # dòng chữ hiện thoáng qua trên màn hình người bấm
else:
n = context.chat_data.get("n", 0) + 1
context.chat_data["n"] = n
await query.answer() # trả lời trước
await query.edit_message_text(f"Số: {n}", reply_markup=buttons()) # rồi sửa tin nhắn
async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text(f"Bạn vừa nói '{update.message.text}'.")
def main():
app = (Application.builder().token(os.environ["BOT_TOKEN"])
.base_url(f"{SERVER}/bot") # Pabal thay cho api.telegram.org
.base_file_url(f"{SERVER}/file/bot")
.build())
app.add_handler(CommandHandler("start", start))
app.add_handler(CallbackQueryHandler(button))
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))
print("Bot đã chạy. Nhấn Ctrl+C để dừng")
app.run_polling()
if __name__ == "__main__":
main()# echo_aiogram.py — pip install aiogram
# Chạy: BOT_TOKEN='100003:…' python3 echo_aiogram.py
import asyncio
import os
from aiogram import Bot, Dispatcher
from aiogram.client.session.aiohttp import AiohttpSession
from aiogram.client.telegram import TelegramAPIServer
from aiogram.filters import CommandStart
dp = Dispatcher()
@dp.message(CommandStart())
async def start(message):
await message.answer("Xin chào! Hãy gửi cho tôi bất cứ điều gì.")
@dp.message()
async def echo(message):
if message.text:
await message.answer(message.text)
async def main():
session = AiohttpSession(api=TelegramAPIServer.from_base("https://pabal.me")) # Pabal
bot = Bot(os.environ["BOT_TOKEN"], session=session)
print("Bot đã chạy. Nhấn Ctrl+C để dừng")
await dp.start_polling(bot)
asyncio.run(main())8. Nút bấm và callback
Để gắn nút inline vào tin nhắn, truyền inline_keyboard (mảng các hàng, mỗi hàng là mảng các nút) vào reply_markup. Có hai loại nút: nút callback_data báo cho bot biết khi được bấm, và nút url mở một liên kết.
curl -s "https://pabal.me/bot$BOT_TOKEN/sendMessage" -H 'Content-Type: application/json' -d '{
"chat_id": 100001,
"text": "Hãy chọn một",
"reply_markup": {
"inline_keyboard": [
[ {"text": "👍 Thích", "callback_data": "like"}, {"text": "👎 Không thích", "callback_data": "dislike"} ],
[ {"text": "Mở tài liệu Pabal", "url": "https://pabal.me/docs/"} ]
]
}
}'
Khi người dùng bấm nút callback_data, bot nhận được update callback_query.
{
"update_id": 7,
"callback_query": {
"id": "5812039457730125441",
"from": { "id": 100001, "is_bot": false, "first_name": "Mai" },
"message": { "message_id": 4, "chat": { "id": 100001, "type": "private", "first_name": "Mai" }, "text": "Hãy chọn một", … },
"chat_instance": "8413962145072395171",
"data": "like"
}
}
Bot phải trả lời bằng answerCallbackQuery trong vòng 10 giây. Trong lúc đó, ứng dụng hiện biểu tượng đồng hồ trên nút và chờ.
# Dòng chữ hiện thoáng qua ở đầu màn hình (show_alert: true thì là hộp thoại xác nhận)
curl -s "https://pabal.me/bot$BOT_TOKEN/answerCallbackQuery" -H 'Content-Type: application/json' \
-d '{"callback_query_id": "5812039457730125441", "text": "Cảm ơn bạn!"}'
# Đổi nội dung và nút của tin nhắn vừa được bấm
curl -s "https://pabal.me/bot$BOT_TOKEN/editMessageText" -H 'Content-Type: application/json' \
-d '{"chat_id": 100001, "message_id": 4, "text": "Bạn đã bấm Thích 👍"}'
callback_datadài 1–64 byte. Mỗi tin nhắn có tối đa 100 nút.- Nếu bot không trả lời callback, ứng dụng ngừng chờ sau 10 giây. Nếu bot đang tắt, máy chủ kết thúc ngay.
- Chỉ câu trả lời đầu tiên có hiệu lực. Có thư viện gửi trước một câu trả lời rỗng khi sửa tin nhắn, nên hãy gửi câu trả lời có nội dung trước.
- Sửa mà nội dung và nút không đổi sẽ nhận
400 Bad Request: message is not modified(giống Telegram).
Bàn phím dưới ô nhập
Truyền keyboard thì một bảng nút hiện ra thay cho ô nhập, bấm nút nào thì chữ trên nút đó được gửi đi thành tin nhắn. Dùng remove_keyboard để ẩn, và force_reply để bật chế độ trả lời.
{
"chat_id": 100001,
"text": "Bạn chọn bên nào?",
"reply_markup": {
"keyboard": [ [ {"text": "Có"}, {"text": "Không"} ], [ {"text": "Gửi vị trí của tôi", "request_location": true} ] ],
"resize_keyboard": true,
"one_time_keyboard": true
}
}
9. Menu lệnh
Đây là danh sách hiện ra khi bạn gõ / trong khung trò chuyện hoặc bấm nút Menu. Bạn thiết lập bằng mã hoặc bằng lệnh /setcommands của BotFather.
curl -s "https://pabal.me/bot$BOT_TOKEN/setMyCommands" -H 'Content-Type: application/json' -d '{
"commands": [
{"command": "start", "description": "Bắt đầu"},
{"command": "help", "description": "Trợ giúp"}
]
}'
Lệnh dài 1–32 ký tự gồm chữ Latin thường, chữ số và dấu gạch dưới; mô tả dài 1–256 ký tự; tối đa 100 lệnh. Nếu truyền language_code, danh sách riêng cho từng ngôn ngữ sẽ được lưu, nhưng hiện ứng dụng chỉ hiển thị danh sách mặc định (được đặt không kèm mã ngôn ngữ). Lệnh do người dùng gửi, như /start, được đánh dấu là bot_command trong entities của tin nhắn.
10. Gửi và nhận ảnh
Gửi
Tải tệp lên bằng multipart/form-data, hoặc dùng lại file_id của ảnh đã nhận trước đó. Ảnh tối đa 10MB, chú thích (caption) tối đa 1.024 ký tự. Gửi ảnh bằng URL hiện chưa được hỗ trợ.
curl -s "https://pabal.me/bot$BOT_TOKEN/sendPhoto" \
-F chat_id=100001 -F caption='Ảnh hôm nay' -F photo=@sunset.jpgwith open("sunset.jpg", "rb") as f:
requests.post(f"{API}/sendPhoto", data={"chat_id": 100001, "caption": "Ảnh hôm nay"},
files={"photo": f}, timeout=60)Nhận
Ảnh người dùng gửi đến nằm trong trường photo của tin nhắn (danh sách theo kích thước; Pabal chỉ có một ảnh gốc). Dùng getFile để lấy đường dẫn rồi tải xuống.
# 1) file_id → file_path
curl -s "https://pabal.me/bot$BOT_TOKEN/getFile?file_id=AQAAAAAAAAB7…"
# {"ok":true,"result":{"file_id":"AQAA…","file_unique_id":"AQAA…","file_size":48213,"file_path":"photos/AQAA….jpg"}}
# 2) Tải xuống — địa chỉ có thêm /file/
curl -s -o photo.jpg "https://pabal.me/file/bot$BOT_TOKEN/photos/AQAA….jpg"
11. Trong nhóm
- Khi tạo nhóm trong ứng dụng, hoặc ở Thông tin nhóm → Thêm thành viên, hãy tìm tên người dùng của bot và thêm vào.
- Bot trong nhóm nhận mọi tin nhắn của nhóm (giống "tắt chế độ riêng tư" trên Telegram).
chat.typelà"group",chat.idlà số âm. - Gọi
sendMessagevớichat.idđó là tin nhắn được gửi vào nhóm. Nút bấm, ảnh và sửa tin nhắn cũng hoạt động y như trò chuyện 1:1. - Khi bot đã rời nhóm, gửi vào nhóm đó sẽ nhận
403 Forbidden: bot is not a member of the group chat.
12. Chuyển sang webhook
Nếu bot chạy trên máy chủ có địa chỉ HTTPS công khai, thay vì hỏi bằng getUpdates, bạn có thể để máy chủ Pabal tự gửi tin mới đến địa chỉ đó.
curl -s "https://pabal.me/bot$BOT_TOKEN/setWebhook" -H 'Content-Type: application/json' \
-d '{"url": "https://bot.example.com/pabal-webhook", "secret_token": "chuỗi-ngẫu-nhiên-dài"}'
Thiết lập, xác minh, gửi lại và trả lời ngay trong phản hồi được trình bày trong tài liệu Webhook.
Bot kết nối qua MTProto (Telethon)
Bot cũng có thể kết nối qua MTProto giống ứng dụng. Cách này tiện nếu bạn đã dùng công cụ cho tài khoản người dùng (như Telethon). Vì vẫn là cùng một tài khoản bot, bạn có thể dùng xen kẽ với HTTP.
# pip install telethon==1.42.0 ← hãy dùng 1.42 (giải thích bên dưới)
# Khóa công khai máy chủ: tải https://pabal.me/docs/server-key.pem về cùng thư mục
import asyncio
import os
from telethon import TelegramClient, events
from telethon.crypto import rsa
from telethon.sessions import StringSession
rsa.add_key(open("server-key.pem").read(), old=False) # khóa công khai của máy chủ Pabal
client = TelegramClient(StringSession(), api_id=1, api_hash="0" * 32)
client.session.set_dc(2, "122.34.175.215", 8443)
@client.on(events.NewMessage(incoming=True))
async def echo(event):
await event.reply(event.raw_text)
async def main():
await client.start(bot_token=os.environ["BOT_TOKEN"]) # auth.importBotAuthorization
print("Bot đã chạy. Nhấn Ctrl+C để dừng")
await client.run_until_disconnected()
asyncio.run(main())
- Hãy dùng Telethon 1.42. Pabal dùng layer 216, còn các bản Telethon mới hơn cố đọc phản hồi theo layer cao hơn nên thất bại ngay từ bước đăng nhập (
TypeNotFoundError). api_idvàapi_hashkhông được Pabal kiểm tra, nên dùng giá trị nào cũng được.- Với bot MTProto, máy chủ đẩy tin nhắn mới đến theo thời gian thực (không cần getUpdates hay webhook). Khi trả lời callback, hãy gọi
event.answer("…")trướcevent.edit(…).
Quy tắc và giới hạn
| Mục | Giá trị |
|---|---|
| Số ký tự tin nhắn / chú thích ảnh | 4.096 ký tự / 1.024 ký tự |
| Kích thước ảnh (tải lên bằng sendPhoto) | 10MB |
| Kích thước phần thân request | 12MB |
timeout · limit của getUpdates | 0–50 giây · 1–100 update |
| Lưu giữ update chưa lấy | 1.000 update gần nhất cho mỗi bot, trong bộ nhớ máy chủ (mất khi khởi động lại) |
| Thời gian chờ trả lời callback | 10 giây |
callback_data · số nút | 1–64 byte · 100 nút mỗi tin nhắn |
| Lệnh | 1–32 ký tự gồm chữ Latin thường, chữ số, dấu gạch dưới; mô tả 1–256 ký tự; tối đa 100 lệnh |
| Bắt chuyện trước | Không được — người dùng phải gửi cho bot trước |
Xử lý sự cố
| Triệu chứng | Nguyên nhân và cách xử lý |
|---|---|
401 Unauthorized | Token sai hoặc đã bị đổi bằng /revoke. Gửi /token cho BotFather để kiểm tra. |
404 Not Found: method not found | Phương thức mà Pabal chưa hỗ trợ. Hãy xem danh sách phương thức. |
403 Forbidden: bot can't initiate conversation with a user | Người đó chưa từng nhắn cho bot. Hãy nhờ họ mở bot trong ứng dụng và bấm Bắt đầu. |
409 Conflict: can't use getUpdates method while webhook is active | Đang có webhook được thiết lập. Hãy gọi deleteWebhook, hoặc nhận qua webhook. |
| Bấm nút mà không thấy phản ứng | Bot đang tắt hoặc chưa gọi answerCallbackQuery. |
| Nhận đi nhận lại cùng một tin nhắn | Bạn chưa tăng offset. Hãy truyền update_id + 1 của update đã xử lý vào request tiếp theo. |
| Định dạng in đậm hay liên kết không có tác dụng | parse_mode chưa được hỗ trợ nên văn bản được gửi nguyên văn. Lệnh, lượt nhắc (@tên-người-dùng), URL và #hashtag được tự động hiển thị để bấm được. |
| Bot im lặng trong nhóm | Bot chưa phải là thành viên nhóm. Hãy thêm vào qua Thông tin nhóm → Thêm thành viên. |