Penangan Eksternal
Penangan eksternal adalah jembatan antara kode Anda dan Telegram. Ketika logika dari pembuat reaksi tidak mencukupi, Anda menyerahkan reaksi tertentu ke server Anda sendiri: server menerima peristiwa dan merespons dengan perintah dalam format Telegram Bot API, sementara GetMyBot menangani transport, pengiriman, dan semua hal yang berkaitan dengan Telegram.
Apa itu
Reaksi di GetMyBot dibuat secara visual: pemicu → kondisi → tindakan. Logika yang kompleks atau non-standar — basis data Anda sendiri, kalkulasi, panggilan ke sistem pihak ketiga, ML, skenario bercabang — sulit untuk diekspresikan di pembuat reaksi. Penangan eksternal menghilangkan batasan ini: Anda menghubungkan backend Anda sendiri dan menyerahkan seluruh reaksi kepadanya untuk diproses.
Layanan eksternal menerima peristiwa dari GetMyBot (pemicu aktif, pengguna membalas teks, menekan tombol) dan sebagai respons memerintahkan bot untuk mengirim sesuatu. GetMyBot mengeksekusi perintah dengan token botnya sendiri — di atas itu bekerja rate-limit, percobaan ulang, deduplikasi, dan log dialog.
Model kerja
- GetMyBot adalah server WebSocket. Layanan Anda sendiri yang terhubung ke server tersebut dan tidak memerlukan alamat publik atau endpoint webhook. GetMyBot tidak membuat koneksi keluar ke layanan Anda, sehingga tidak ada permukaan SSRF.
- Autentikasi dengan token integrasi. Layanan menyajikan token saat terhubung; GetMyBot memverifikasi dengan hash dan menjaga koneksi.
- Peristiwa dan perintah dalam format Telegram Bot API. Layanan menerima peristiwa dan merespons dengan array panggilan berformat "metode + parameter", persis seperti saat bekerja dengan bot biasa.
- Perintah dieksekusi oleh GetMyBot dengan tokennya sendiri. Layanan tidak pernah berkomunikasi langsung dengan Telegram: perintah divalidasi dan masuk ke outbox internal GetMyBot, dari mana dikirim dengan token bot beserta rate-limit, percobaan ulang, deduplikasi, dan log.
Satu koneksi melayani semua dialog dari satu integrasi — peristiwa dari berbagai pengguna dimultipleks dan dibedakan berdasarkan field session_id.
Mengatur integrasi
- Buka bagian Integrasi di menu sebelah kiri dan klik Tambah integrasi.
- Pilih layanan Penangan eksternal dan tentukan nama.
- Setelah dibuat, kartu akan menampilkan:
- Alamat WebSocket — berformat
wss://api.mybot.app/ext/ws/<integrationID>, di mana<integrationID>adalah UUID integrasi ini. Layanan Anda terhubung melalui alamat ini. Tanpa id dalam jalur, koneksi tidak akan terbentuk. - Token koneksi — ditampilkan sekali saja saat pembuatan. Salin dan simpan di tempat yang aman; token tidak dapat dilihat lagi (hanya disimpan dalam bentuk hash).
- Rotasi token — tombol menghasilkan token baru dan segera membatalkan token lama. Setelah rotasi, perbarui token di layanan Anda.
- Indikator online — menampilkan apakah layanan Anda saat ini mempertahankan koneksi aktif.
- Alamat WebSocket — berformat
Parameter integrasi tambahan yang mempengaruhi protokol:
session_ttl_seconds— waktu hidup sesi dalam detik (default3600).on_unavailable_reaction_id— reaksi cadangan yang dijalankan jika layanan offline saat pemicu aktif (lihat Keandalan).
Menautkan ke reaksi
Penangan eksternal tidak terhubung sebagai pemicu terpisah, melainkan sebagai tindakan. Di editor reaksi, tambahkan tindakan Serahkan ke penangan eksternal dan pilih integrasi yang diinginkan.
Dialog dapat dibuka oleh pemicu GetMyBot mana pun yang sudah ada — perintah, teks, penekanan tombol, parameter dari tautan, permintaan web masuk, jadwal. Ketika pemicu seperti itu aktif dan mencapai tindakan, GetMyBot membuka sesi proxy dan mengirim peristiwa session.open ke layanan.
Jika layanan tidak terhubung saat itu, GetMyBot akan menjalankan reaksi cadangan dari pengaturan integrasi (on_unavailable_reaction_id). Jika cadangan tidak ditentukan — tidak ada yang terjadi (diam, tanpa error ke pengguna).
Koneksi dan autentikasi
Endpoint: GET /ext/ws/{integrationID} melalui WebSocket (wss). integrationID adalah UUID integrasi bertipe "Penangan eksternal". Batas ukuran frame — 256 KiB.
Ada dua cara untuk melakukan autentikasi.
1. Header saat handshake (direkomendasikan). Kirim token di header Authorization:
Authorization: Bearer <token>
2. Auth-frame. Jika header Bearer tidak dikirim, kirim sebagai frame pertama dalam 10 detik setelah koneksi terbentuk:
{ "type": "auth", "token": "<token>" }
Jika auth-frame tidak diterima dalam 10 detik — koneksi ditutup dengan kode 4401. Token yang salah dengan cara apa pun juga menghasilkan close 4401.
Token dihasilkan saat pembuatan integrasi, ditampilkan sekali, dan disimpan hanya dalam bentuk hash (sha256), verifikasi menggunakan constant-time. Rotasi token:
POST /api/bots/{botID}/integrations/{integrationID}/rotate-token
Endpoint mengembalikan plaintext token baru; token lama segera berhenti berfungsi.
Peristiwa: bot → layanan
Setiap peristiwa adalah JSON-frame (EventEnvelope). Field type — salah satu dari: session.open, message, callback, session.cancel, session.expired.
Set lengkap field envelope:
{
"type": "session.open",
"session_id": "string",
"bot_id": "string",
"user": {
"tg_user_id": 123456789,
"first_name": "Ivan",
"username": "ivan",
"params": { "utm": "promo" }
},
"chat": { "id": 123456789, "type": "private" },
"update": { "...": "Telegram Update mentah (raw JSON)" },
"event": { "...": "peristiwa GetMyBot yang dinormalisasi (raw JSON)" }
}
session_id— pengidentifikasi sesi (dialog).bot_id— pengidentifikasi bot.user— data pengguna:tg_user_id(int64),first_namedanusernameopsional, sertaparams— parameter pengguna (termasuk dari tautan), map kunci→nilai.chat— obrolan:id(int64) dantype(misalnya"private").update— Telegram Update mentah (raw JSON). Ada untuksession.open,message,callback.event— peristiwa GetMyBot yang dinormalisasi (raw JSON).
Konten berdasarkan jenis berbeda-beda:
session.open— pemicu aktif, dialog baru dibuka. Adaupdate(Update mentah) danevent(peristiwa dinormalisasi lengkap),user.paramsterisi.message— dikirim saat layanan "menunggu" (expect=text/any) dan pengguna mengirim pesan. Konten sama sepertisession.open.callback— pengguna menekan tombol yang sebelumnya dikirim layanan.updatementah; diuserhanyatg_user_idyang terisi;eventhanya berisi callback_data asli yang ditentukan layanan di tombol:
{ "callback_data": "<nilai asli yang ditentukan layanan di tombol>" }
session.cancel/session.expired— envelope minimal:session_id,bot_id,user.tg_user_id,chat.id(untukcanceljugachat.type). Fieldupdate/eventtidak ada. Peristiwa ini hanya dikirimkan jika layanan online — tidak disimpan ke antrean offline.
Perintah: layanan → bot
Perintah adalah JSON-frame (CommandEnvelope). Field type — salah satu dari: execute, session.close, auth, ping, pong.
{
"type": "execute",
"session_id": "string",
"methods": [
{ "method": "sendMessage", "params": { "text": "Halo" } }
],
"expect": "none"
}
session_id— sesi yang terkait dengan perintah.token— hanya untuktype: "auth"(lihat Koneksi dan autentikasi).methods— array objek{ "method": "<nama metode Telegram Bot API>", "params": { ... } }. Nama metode persis seperti di Telegram Bot API (misalnyasendMessage),paramsadalah objek parameter metode tersebut.expect— apakah layanan menunggu respons pengguna:"none","text", atau"any"(nilai kosong diperlakukan sebagainone).session.close— menutup dialog secara eksplisit.
Perintah tidak langsung dikirim ke Telegram: GetMyBot memvalidasi dan memasukkannya ke outbox-nya, dari mana dikirim dengan token bot (beserta rate-limit, percobaan ulang, deduplikasi, dan log).
Cara execute diproses
Perintah execute dibuang seluruhnya jika: session_id kosong; sesi tidak ditemukan atau tidak berstatus open; sesi milik integrasi lain; sesi sudah kedaluwarsa.
Selanjutnya:
- Rate-limit: hingga 60
executeper menit per sesi (jendela geser). Melampaui batas — dibuang. - Hingga 30 metode dalam satu
execute; yang berlebih diabaikan secara diam-diam. - Setiap metode diperiksa terhadap whitelist (lihat Metode yang diizinkan) — metode yang tidak ada dalam daftar ditolak.
- Chat-guard: jika
paramsmengandungchat_idataufrom_chat_iddengan nilai yang tidak sama dengan0dan tidak sama denganchat_idsesi — seluruh metode ditolak (tidak boleh mengirim ke obrolan orang lain).chat_iddapat tidak ditentukan sama sekali: GetMyBot akan memaksa mengisi obrolan sesi. - Tombol inline dengan
callback_datasecara otomatis ditokenisasi (bentuk internalx:<token>); masa hidup tombol = hingga sesi berakhir. Tombolurl/webapp/switch_inlinetidak diubah.
Metode yang diizinkan
Layanan mengendalikan bot, sehingga set metode dibatasi oleh whitelist — hanya pengiriman dan operasi pesan untuk dialog saat ini. Metode di luar daftar diabaikan secara diam-diam (bukan error).
- pengiriman:
sendMessage,sendPhoto,sendDocument,sendVideo,sendAudio,sendMediaGroup,sendAnimation,sendVoice,sendLocation,sendChatAction; - edit dan hapus:
editMessageText,editMessageCaption,editMessageReplyMarkup,deleteMessage; - respons penekanan:
answerCallbackQuery; - teruskan dan salin:
forwardMessage,copyMessage; - sematkan:
pinChatMessage,unpinChatMessage.
Metode tingkat akun (setWebhook, getUpdates, logOut, close, setMyCommands, dll.) tidak diizinkan.
Menunggu dan siklus hidup sesi
Setelah setiap execute, nasib sesi bergantung pada expect dan apakah perintah mengandung tombol inline dengan callback_data:
expect=textatauany— pesan berikutnya dari pengguna akan dikirim ke layanan sebagai peristiwamessage.expect=noneDAN perintah TIDAK mengandung tombol inline dengan callback — sesi otomatis ditutup. Ini adalah pesan "terminal".expect=none, TETAPI ada tombol callback — sesi tetap terbuka: penekanan ditangkap selama token tombol masih hidup (hingga TTL).
Penting: expect tidak mempengaruhi penerimaan penekanan tombol. Penekanan tombol yang ditokenisasi ditangkap terlepas dari nilai expect — selama sesi dan token tombol masih hidup. expect hanya mengontrol apakah layanan menunggu respons teks/sembarang.
Batas dialog
Konteks dialog hidup tepat selama sesi terbuka:
- Respons teks saat menunggu aktif (
expect=text/any) — dikirim ke layanan sebagai peristiwamessage, bukan memicu reaksi bot biasa. - Penekanan tombol yang ditokenisasi — dikirim ke layanan sebagai peristiwa
callbackdengancallback_dataasli. GetMyBot selalu mengonfirmasi penekanan sendiri (answerCallbackQuery); kepemilikan penekanan diverifikasi (bot + pengguna + obrolan sesi). Tombol tidak menutup sesi. /cancel(juga/cancel@botdan "batal") saat sesi eksternal aktif — sesi ditutup,session.canceldikirim ke layanan, pengguna menerima "Dibatalkan.".- Satu dialog per pengguna. Membuka sesi baru menggeser sesi terbuka sebelumnya dari pengguna tersebut —
session.canceldikirimkan ke sesi lama itu. - Kedaluwarsa TTL. Berdasarkan timer, sesi ditandai
expireddan (jika layanan online)session.expireddikirim ke layanan.
Batas dan timeout
- Ukuran frame: 256 KiB.
- TTL sesi: default 3600 detik (dapat dikonfigurasi melalui parameter
session_ttl_secondsintegrasi). - Frekuensi perintah: 60
executeper menit per sesi (jendela geser). - Metode dalam satu
execute: hingga 30 (yang berlebih dibuang). - Sesi terbuka per integrasi: hingga 1000.
- Antrean offline: hingga 100 peristiwa per sesi.
- Heartbeat:
pingsetiap 30 detik; timeout tidak ada aktivitas — 60 detik. - Timeout auth-frame: 10 detik.
Keandalan (heartbeat, offline, antrean, fallback)
- Heartbeat. Server mengirim frame
{"type":"ping"}setiap 30 detik. Jika tidak ada aktivitas dari klien lebih dari 60 detik — koneksi ditutup. Klien dapat mengirimping/pongsendiri untuk menjaga aktivitas. Putusnya koneksi tidak mematikan dialog: sesi hidup di database hingga TTL atau koneksi ulang. - Koneksi ulang — di sisi layanan. Jika koneksi terputus, layanan Anda harus terhubung kembali. Sesi terbuka di database bertahan dari restart hingga TTL berakhir.
- Fallback saat offline pada awal. Jika layanan tidak terhubung saat reaksi aktif — reaksi cadangan dari pengaturan integrasi dijalankan (
on_unavailable_reaction_id). Jika tidak ditentukan — tidak ada yang terjadi (diam). - Antrean offline. Peristiwa
message/callbackyang tidak terkirim karena offline disimpan dalam antrean offline (hingga 100 peristiwa, FIFO, disimpan hingga sesi berakhir) dan dikirimkan saat koneksi ulang. Peristiwasession.open/session.cancel/session.expiredtidak disimpan dalam antrean. - Jaminan pengiriman — at-most-once. Saat antrean penuh atau kedaluwarsa, peristiwa dibuang. Rancang logika Anda sedemikian rupa sehingga melewatkan satu peristiwa tidak merusak skenario.
Indikator online
Untuk memeriksa apakah layanan mempertahankan koneksi aktif, gunakan endpoint:
GET /api/bots/{botID}/integrations/{integrationID}/status
Respons:
{ "online": true }
Indikator yang sama tersedia di kartu integrasi di panel. Padanan di MCP adalah tool get_integration_status.
Keamanan
GetMyBot mengeksekusi perintah layanan pihak ketiga dengan tokennya sendiri, sehingga perlindungan sangat ketat.
- Tidak ada permukaan SSRF. GetMyBot berperan sebagai server WebSocket dan tidak membuat koneksi keluar ke layanan — layanan yang terhubung ke GetMyBot. Tidak ada URL yang dikontrol dari luar yang dijangkau platform.
- Token disimpan dalam hash (sha256), verifikasi constant-time, rotasi didukung.
- Isolasi tenant. Perintah terikat ketat pada integrasinya sendiri; sesi terikat pada bot + pengguna + obrolan. Peristiwa bot lain tidak masuk ke soket Anda.
- Whitelist metode +
chat_idpaksa mencegah bot digunakan sebagai pengirim pesan ke obrolan sembarang. - Token bot tidak dikirim keluar — layanan tidak pernah berkomunikasi langsung dengan Telegram.
- Penyamaran rahasia. Token integrasi dan nilai sensitif disembunyikan dalam log.
Contoh
Penangan minimal: saat dialog dibuka, kirim pesan dan tutup sesi (expect: "none"). Ganti dengan id integrasi dan token Anda. Alamat harus mengandung <integrationID> dalam jalur.
Node.js
import WebSocket from "ws";
const URL = `wss://api.mybot.app/ext/ws/${process.env.MYBOT_INTEGRATION_ID}`;
const ws = new WebSocket(URL, {
headers: { Authorization: `Bearer ${process.env.MYBOT_TOKEN}` },
});
ws.on("message", (raw) => {
const ev = JSON.parse(raw);
if (ev.type === "session.open") {
ws.send(JSON.stringify({
type: "execute",
session_id: ev.session_id,
expect: "none",
methods: [{ method: "sendMessage", params: { text: "Halo dari penangan eksternal!" } }],
}));
}
});
Python
import json, os, asyncio, websockets
async def main():
url = f"wss://api.mybot.app/ext/ws/{os.environ['MYBOT_INTEGRATION_ID']}"
headers = {"Authorization": f"Bearer {os.environ['MYBOT_TOKEN']}"}
async with websockets.connect(url, additional_headers=headers) as ws:
async for raw in ws:
ev = json.loads(raw)
if ev["type"] == "session.open":
await ws.send(json.dumps({
"type": "execute",
"session_id": ev["session_id"],
"expect": "none",
"methods": [
{"method": "sendMessage", "params": {"text": "Halo dari penangan eksternal!"}}
],
}))
asyncio.run(main())
Tombol inline dan penanganan callback
Untuk menangkap penekanan, kirim tombol dengan callback_data (GetMyBot menokenisasinya secara otomatis). Dengan expect: "none" dan tombol, sesi tetap terbuka selama token tombol masih hidup — penekanan akan datang sebagai peristiwa callback, di mana event.callback_data sama dengan nilai aslinya. Mengonfirmasi penekanan (answerCallbackQuery) tidak diperlukan — GetMyBot melakukannya sendiri.
if (ev.type === "session.open") {
ws.send(JSON.stringify({
type: "execute",
session_id: ev.session_id,
expect: "none",
methods: [{
method: "sendMessage",
params: {
text: "Tekan tombol",
reply_markup: { inline_keyboard: [[{ text: "Mulai", callback_data: "go" }]] },
},
}],
}));
} else if (ev.type === "callback" && ev.event.callback_data === "go") {
ws.send(JSON.stringify({
type: "execute",
session_id: ev.session_id,
expect: "none",
methods: [{ method: "sendMessage", params: { text: "Tombol ditekan!" } }],
}));
}
Langkah selanjutnya
- Integrasi — ikhtisar umum layanan yang dapat dihubungkan.
- Reaksi — cara menyusun pemicu, kondisi, dan tindakan.
- Permintaan web dan webhook — cara lebih sederhana untuk memanggil URL eksternal tanpa dialog.