Pengembangan bot
Membuat bot
Buat bot dengan @BotFather, terima dan balas pesan, lalu tangani tombol, perintah, foto, hingga grup โ tutorial yang bisa diikuti dari awal sampai akhir.
Setelah menyelesaikan tutorial ini, Anda akan punya bot yang membalas pesan, memasang tombol, menangani menu perintah dan foto, serta bekerja di grup. Yang Anda perlukan adalah aplikasi Pabal, komputer untuk menjalankan program bot, dan salah satu dari Python 3.10 ke atas atau Node.js 18 ke atas.
Cara kerja bot
Bot di Pabal adalah akun yang dikendalikan oleh program. Pesan yang dikirim ke bot menumpuk di kotak pesan bot, lalu program bot bertanya ke server "Ada yang baru?" (getUpdates), mengambilnya, dan mengirim balasan (sendMessage). Program bot berjalan di luar server, yaitu di komputer Anda.
Penjelasan diagram
- Tiga jalur: di kiri orang yang memakai aplikasi, di tengah server Pabal, di kanan program bot Anda. Garis putus-putus vertikal menunjukkan arah berjalannya waktu.
- Panah abu-abu adalah permintaan yang dimulai oleh bot, sedangkan panah biru adalah pesan yang bergerak sebagai hasilnya. Program bot hanya mengirim permintaan; server tidak menghubungi bot lebih dulu (kecuali Anda memakai webhook).
- Penantian pada โ (long polling) adalah intinya: jika Anda memberi
timeout=30, saat tidak ada update baru server tidak langsung mengembalikan jawaban kosong, melainkan menahannya hingga 30 detik dan mengembalikannya (โข) begitu ada pesan masuk (โก). Karena itu respons terasa cepat dan jumlah permintaan sedikit. - offset pada โฅ adalah tanda "sudah diterima": jika Anda mengirim
update_idterakhir yang diproses ditambah 1, update dengan nomor itu dan sebelumnya dihapus dari server. Jika offset tidak dinaikkan, Anda akan terus menerima update yang sama. - โค melewati jalur yang sama dengan pesan dari manusia: balasan bot di-push ke semua perangkat aplikasi dan juga muncul di daftar obrolan.
1. Membuat bot dengan @BotFather
Bot dibuat dengan mengobrol bersama @BotFather di dalam aplikasi Pabal. BotFather adalah bot yang sudah terpasang di dalam server Pabal.
- Ketik
BotFatherdi kotak pencarian aplikasi dan buka BotFather. Tekan Mulai, dan daftar perintah akan dikirim. - Kirim
/newbot. - Kirim nama bot. Karena ini nama yang tampil di daftar obrolan, Anda bebas memakai huruf apa saja, bukan hanya huruf Latin.
- Kirim nama pengguna bot. Panjangnya 5โ32 karakter berupa huruf Latin, angka, dan garis bawah, diawali huruf Latin, dan wajib diakhiri
bot. - Selesai begitu balasan berisi token datang. Salin dan simpan token itu.
Saat ini BotFather membalas dalam bahasa Korea. Artinya: pertama ia meminta nama bot (nama yang tampil di daftar obrolan), lalu meminta nama pengguna bot, dan terakhir memberi tahu bahwa bot @hello_test_bot sudah dibuat dan bisa dicari untuk memulai obrolan, beserta token botnya โ yang harus Anda simpan dengan aman seperti kata sandi.
| Perintah BotFather | Fungsinya |
|---|---|
/newbot | Membuat bot baru (nama โ nama pengguna โ token) |
/mybots | Daftar bot yang Anda buat |
/token | Melihat lagi token bot |
/revoke | Menerbitkan ulang token โ token lama langsung tidak berlaku, dan koneksi yang tersambung dengan token lama juga diputus |
/setcommands | Mengatur menu perintah (perintah - deskripsi, satu per baris) |
/deletebot | Menghapus bot โ konfirmasi dengan mengirim ๋ค, ์ญ์ ํฉ๋๋ค (artinya "Ya, hapus"). Nama penggunanya dibebaskan agar bisa dipakai lagi |
/cancel | Membatalkan proses yang sedang berjalan |
Jika Anda menyertakan nama pengguna, seperti /token @hello_test_bot, langkah "Bot yang mana?" akan dilewati.
2. Menangani token
Token berbentuk <ID_bot>:<rahasia>. Angka di depan adalah ID pengguna bot, dan bagian belakang adalah rahasianya. Satu token cukup untuk mengendalikan bot sepenuhnya, jadi perlakukan token seperti kata sandi.
- Jangan tulis token di dalam kode; simpan di variabel lingkungan (
BOT_TOKEN) atau di penyimpanan rahasia. Jangan unggah ke repositori publik. - Jika bocor, kirim
/revokeke BotFather. Token lama langsung ditolak (401 Unauthorized), dan sesi bot yang tersambung ke MTProto dengan token lama juga diputus. - Token masuk ke dalam alamat (URL), jadi pastikan program bot Anda tidak mencatat alamat permintaan ke log. Server Pabal juga tidak mencatat alamat Bot API ke log.
3. Permintaan pertama โ getMe
Alamat setiap permintaan adalah https://pabal.me/bot<token>/<metode>. Mari periksa apakah token Anda benar dengan getMe.
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 ke atas โ fetch sudah tersedia
const res = await fetch(`https://pabal.me/bot${process.env.BOT_TOKEN}/getMe`);
console.log(await res.json());Jika berhasil, hasilnya seperti ini. Setiap respons adalah JSON yang berisi ok dan result (jika berhasil) atau error_code dan description (jika gagal).
{
"ok": true,
"result": {
"id": 100003,
"is_bot": true,
"first_name": "Bot Halo",
"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
}
}
Jika token salah, Anda menerima HTTP 401 beserta {"ok": false, "error_code": 401, "description": "Unauthorized"}.
4. Menerima pesan โ getUpdates
Buka bot di aplikasi dan tekan Mulai atau kirim pesan apa saja, lalu ambil update barunya.
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": "Hana" },
"chat": { "id": 100001, "first_name": "Hana", "type": "private" },
"date": 1789805661,
"text": "/start",
"entities": [ { "type": "bot_command", "offset": 0, "length": 6 } ]
}
}
]
}
- update_id: nomor yang bertambah 1 untuk setiap update. Setelah memproses, beri
offset=update_id+1pada permintaan berikutnya, maka update dengan nomor itu dan sebelumnya dihapus sebagai "sudah diterima". - timeout: jumlah detik untuk menunggu saat tidak ada update baru (0โ50). Jika 0, daftar kosong langsung dikembalikan. Disarankan 25โ30.
- chat.id: tujuan pengiriman balasan. Untuk obrolan 1:1 berupa ID orang tersebut (bilangan positif), untuk grup berupa bilangan negatif.
- Ada tiga jenis update yang bisa diterima:
message(pesan baru),edited_message(pesan yang diedit), dancallback_query(tombol ditekan).
Update yang belum diambil menumpuk di memori server, hingga 1.000 update terbaru per bot. Jika server dimulai ulang, update yang belum diambil hilang (pesannya sendiri tetap ada di obrolan). Jangan biarkan bot Anda mati terlalu lama.
5. Mengirim balasan โ sendMessage
curl -s "https://pabal.me/bot$BOT_TOKEN/sendMessage" \
-H 'Content-Type: application/json' \
-d '{"chat_id": 100001, "text": "Halo!"}'requests.post(f"https://pabal.me/bot{os.environ['BOT_TOKEN']}/sendMessage",
json={"chat_id": 100001, "text": "Halo!"}, 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: 'Halo!' }),
});Parameter bisa dikirim dengan cara mana pun yang paling nyaman: badan JSON, formulir (application/x-www-form-urlencoded), multipart/form-data saat mengunggah berkas, atau query string di belakang alamat. Hasilnya adalah pesan yang dikirim (Message).
Bot hanya bisa mengirim pesan kepada seseorang setelah orang itu setidaknya sekali mengirim pesan ke bot. Jika tidak, hasilnya 403 Forbidden: bot can't initiate conversation with a user. Aturannya sama dengan Telegram.
6. Menyelesaikan bot yang menirukan pesan
Ulangi menerima dan mengirim terus-menerus, maka jadilah sebuah bot. Berikut kode lengkapnya, ditulis tanpa pustaka.
# echo.py โ pip install requests
# Jalankan: 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 sudah berjalan. Tekan Ctrl+C untuk berhenti")
while True:
for update in call("getUpdates", offset=offset, timeout=30):
offset = update["update_id"] + 1 # tandai sudah diterima
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 ke atas, tanpa pustaka
// Jalankan: 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 sudah berjalan. Tekan Ctrl+C untuk berhenti');
for (;;) {
const updates = await call('getUpdates', { offset, timeout: 30 });
for (const update of updates) {
offset = update.update_id + 1; // tandai sudah diterima
const message = update.message;
if (message?.text) {
await call('sendMessage', { chat_id: message.chat.id, text: message.text });
}
}
}7. Membuat bot dengan pustaka
Pustaka bot untuk Telegram punya pengaturan untuk mengganti alamat server. Dengan satu pengaturan itu, bot berjalan apa adanya di Pabal. Saat memindahkan bot yang dibuat untuk Telegram pun, cukup ganti pengaturan ini dan dapatkan token baru dari BotFather di Pabal.
| Pustaka | Pengaturan yang diganti | Versi yang diuji |
|---|---|---|
| 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 |
| Tanpa pustaka (HTTP) | Bagian depan alamat https://api.telegram.org โ https://pabal.me | โ |
# hello_bot.py โ pip install python-telegram-bot
# Jalankan: 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("๐ Suka", callback_data="like"),
InlineKeyboardButton("๐ข Tambah angka", callback_data="count")]])
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text("Halo! Coba tekan tombolnya.", reply_markup=buttons())
async def button(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
if query.data == "like":
await query.answer("Terima kasih!") # teks yang muncul sebentar di layar orang yang menekan
else:
n = context.chat_data.get("n", 0) + 1
context.chat_data["n"] = n
await query.answer() # jawab dulu
await query.edit_message_text(f"Angka: {n}", reply_markup=buttons()) # lalu edit pesannya
async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text(f"Anda bilang: '{update.message.text}'.")
def main():
app = (Application.builder().token(os.environ["BOT_TOKEN"])
.base_url(f"{SERVER}/bot") # Pabal, bukan 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 sudah berjalan. Tekan Ctrl+C untuk berhenti")
app.run_polling()
if __name__ == "__main__":
main()# echo_aiogram.py โ pip install aiogram
# Jalankan: 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("Halo! Coba kirim pesan apa saja.")
@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 sudah berjalan. Tekan Ctrl+C untuk berhenti")
await dp.start_polling(bot)
asyncio.run(main())8. Tombol dan callback
Untuk memasang tombol inline pada pesan, berikan inline_keyboard (array baris; setiap baris adalah array tombol) di reply_markup. Ada dua jenis tombol: tombol callback_data yang memberi tahu bot saat ditekan, dan tombol url yang membuka tautan.
curl -s "https://pabal.me/bot$BOT_TOKEN/sendMessage" -H 'Content-Type: application/json' -d '{
"chat_id": 100001,
"text": "Silakan pilih",
"reply_markup": {
"inline_keyboard": [
[ {"text": "๐ Suka", "callback_data": "like"}, {"text": "๐ Kurang suka", "callback_data": "dislike"} ],
[ {"text": "Buka dokumentasi Pabal", "url": "https://pabal.me/docs/"} ]
]
}
}'
Saat seseorang menekan tombol callback_data, bot menerima update callback_query.
{
"update_id": 7,
"callback_query": {
"id": "5812039457730125441",
"from": { "id": 100001, "is_bot": false, "first_name": "Hana" },
"message": { "message_id": 4, "chat": { "id": 100001, "type": "private", "first_name": "Hana" }, "text": "Silakan pilih", โฆ },
"chat_instance": "8413962145072395171",
"data": "like"
}
}
Bot harus menjawab dengan answerCallbackQuery dalam 10 detik. Selama itu aplikasi menampilkan ikon jam pada tombol dan menunggu.
# teks yang muncul sebentar di atas layar (dengan show_alert: true, muncul jendela konfirmasi)
curl -s "https://pabal.me/bot$BOT_TOKEN/answerCallbackQuery" -H 'Content-Type: application/json' \
-d '{"callback_query_id": "5812039457730125441", "text": "Terima kasih!"}'
# ganti teks dan tombol pada pesan yang ditekan
curl -s "https://pabal.me/bot$BOT_TOKEN/editMessageText" -H 'Content-Type: application/json' \
-d '{"chat_id": 100001, "message_id": 4, "text": "Anda menekan Suka ๐"}'
callback_databerukuran 1โ64 byte. Satu pesan bisa memuat hingga 100 tombol.- Jika callback tidak dijawab, aplikasi berhenti menunggu setelah 10 detik. Jika bot sedang mati, server langsung mengakhirinya.
- Hanya jawaban pertama yang berlaku. Beberapa pustaka mengirim jawaban kosong lebih dulu saat mengedit pesan, jadi kirim jawaban yang berisi teks lebih dulu.
- Jika Anda mengedit dengan teks dan tombol yang sama persis, hasilnya
400 Bad Request: message is not modified(sama seperti Telegram).
Keyboard di bawah kolom input
Jika Anda memberikan keyboard, papan tombol muncul menggantikan kolom input, dan saat ditekan teks tombol itu dikirim sebagai pesan. Sembunyikan dengan remove_keyboard, dan aktifkan mode balas dengan force_reply.
{
"chat_id": 100001,
"text": "Pilih yang mana?",
"reply_markup": {
"keyboard": [ [ {"text": "Ya"}, {"text": "Tidak"} ], [ {"text": "Kirim lokasi saya", "request_location": true} ] ],
"resize_keyboard": true,
"one_time_keyboard": true
}
}
9. Menu perintah
Ini adalah daftar yang muncul saat Anda menekan / di jendela obrolan atau menekan tombol Menu. Tetapkan lewat kode atau lewat /setcommands di BotFather.
curl -s "https://pabal.me/bot$BOT_TOKEN/setMyCommands" -H 'Content-Type: application/json' -d '{
"commands": [
{"command": "start", "description": "Mulai"},
{"command": "help", "description": "Bantuan"}
]
}'
Perintah terdiri dari 1โ32 karakter berupa huruf Latin kecil, angka, dan garis bawah; deskripsinya 1โ256 karakter; maksimal 100 perintah. Jika Anda memberikan language_code, daftar per bahasa disimpan terpisah, tetapi saat ini aplikasi hanya menampilkan daftar bawaan yang ditetapkan tanpa kode bahasa. Perintah seperti /start yang dikirim seseorang ditandai sebagai bot_command di entities pesan.
10. Mengirim dan menerima foto
Mengirim
Unggah berkas dengan multipart/form-data, atau pakai ulang file_id foto yang pernah diterima. Ukuran foto hingga 10MB, dan keterangan (caption) hingga 1.024 karakter. Pengiriman lewat URL belum didukung.
curl -s "https://pabal.me/bot$BOT_TOKEN/sendPhoto" \
-F chat_id=100001 -F caption='Foto hari ini' -F photo=@sunset.jpgwith open("sunset.jpg", "rb") as f:
requests.post(f"{API}/sendPhoto", data={"chat_id": 100001, "caption": "Foto hari ini"},
files={"photo": f}, timeout=60)Menerima
Foto yang dikirim seseorang datang sebagai photo pada pesan (daftar per ukuran; di Pabal hanya satu, yaitu ukuran asli). Dapatkan jalurnya dengan getFile, lalu unduh.
# 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) unduh โ alamatnya memuat /file/
curl -s -o photo.jpg "https://pabal.me/file/bot$BOT_TOKEN/photos/AQAAโฆ.jpg"
11. Di dalam grup
- Saat membuat grup di aplikasi, atau lewat info grup โ Tambah anggota, cari nama pengguna bot lalu tambahkan.
- Bot yang masuk ke grup menerima semua pesan di grup (sama seperti "mode privasi mati" di Telegram).
chat.typebernilai"group", danchat.idberupa bilangan negatif. - Jika Anda memanggil
sendMessagedenganchat.iditu, pesan dikirim ke grup. Tombol, foto, dan pengeditan bekerja persis seperti di obrolan 1:1. - Jika bot keluar dari grup, mengirim ke grup itu menghasilkan
403 Forbidden: bot is not a member of the group chat.
12. Beralih ke webhook
Jika bot Anda berjalan di server yang punya alamat HTTPS publik, alih-alih bertanya dengan getUpdates, Anda bisa membuat server Pabal mengirim update baru ke alamat itu.
curl -s "https://pabal.me/bot$BOT_TOKEN/setWebhook" -H 'Content-Type: application/json' \
-d '{"url": "https://bot.example.com/pabal-webhook", "secret_token": "string-acak-yang-panjang"}'
Pengaturan, verifikasi, percobaan ulang, hingga membalas lewat respons dijelaskan di dokumen Webhook.
Bot yang tersambung lewat MTProto (Telethon)
Bot juga bisa tersambung lewat MTProto, sama seperti aplikasi. Ini praktis jika Anda sudah memakai alat untuk akun manusia (seperti Telethon). Karena akun botnya sama, Anda boleh memakainya bersamaan dengan HTTP.
# pip install telethon==1.42.0 โ pakai 1.42 (lihat penjelasan di bawah)
# Kunci publik server: unduh https://pabal.me/docs/server-key.pem ke folder yang sama
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) # kunci publik server 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 sudah berjalan. Tekan Ctrl+C untuk berhenti")
await client.run_until_disconnected()
asyncio.run(main())
- Pakai Telethon 1.42. Pabal berbicara dengan layer 216, sedangkan Telethon yang lebih baru mencoba membaca respons dengan layer yang lebih tinggi sehingga gagal sejak tahap masuk (
TypeNotFoundError). api_iddanapi_hashtidak diperiksa oleh Pabal, jadi nilai apa pun boleh.- Untuk bot MTProto, server mendorong pesan baru secara real-time (tidak perlu getUpdates atau webhook). Untuk menjawab callback, panggil
event.answer("โฆ")sebelumevent.edit(โฆ).
Aturan dan batas
| Butir | Nilai |
|---|---|
| Panjang teks pesan / keterangan foto | 4.096 karakter / 1.024 karakter |
| Ukuran foto (unggahan sendPhoto) | 10MB |
| Ukuran badan permintaan | 12MB |
timeout ยท limit getUpdates | 0โ50 detik ยท 1โ100 update |
| Penyimpanan update yang belum diambil | 1.000 update terbaru per bot, di memori server (hilang saat dimulai ulang) |
| Waktu tunggu jawaban callback | 10 detik |
callback_data ยท jumlah tombol | 1โ64 byte ยท 100 per pesan |
| Perintah | 1โ32 karakter huruf Latin kecil, angka, dan garis bawah; deskripsi 1โ256 karakter; maksimal 100 |
| Memulai percakapan lebih dulu | Tidak bisa โ orangnya harus mengirim pesan ke bot lebih dulu |
Pemecahan masalah
| Gejala | Penyebab dan solusi |
|---|---|
401 Unauthorized | Token salah atau sudah diganti dengan /revoke. Kirim /token ke BotFather untuk memeriksanya. |
404 Not Found: method not found | Metode itu belum didukung Pabal. Periksa daftar metode. |
403 Forbidden: bot can't initiate conversation with a user | Orang itu belum pernah mengirim pesan ke bot. Minta orang itu membuka bot di aplikasi dan menekan Mulai. |
409 Conflict: can't use getUpdates method while webhook is active | Webhook sedang terpasang. Panggil deleteWebhook, atau terima update lewat webhook. |
| Tombol ditekan tetapi tidak ada reaksi | Bot sedang mati, atau answerCallbackQuery tidak dipanggil. |
| Pesan yang sama terus diterima | offset tidak dinaikkan. Berikan update_id + 1 dari update yang sudah diproses pada permintaan berikutnya. |
| Format tebal atau tautan tidak berlaku | parse_mode belum didukung, jadi teks dikirim apa adanya. Perintah, @sebutan, URL, dan #tagar otomatis ditampilkan agar bisa diklik. |
| Bot diam saja di grup | Bot belum menjadi anggota grup. Tambahkan lewat info grup โ Tambah anggota. |