Dokumentasi pengembang
Bahasa Indonesia

Pengembangan bot

Bot suara

Saat seseorang menelepon bot dari aplikasi, model suara AI yang menjawab. Cara menyalakan, instruksi dan suara, notifikasi panggilan, tool, server MCP, kunci sendiri, transkrip, mode stream, hingga Apple Watch dan Simpan percakapan โ€” panduan untuk pemilik bot.

Bot suara adalah bot yang bisa ditelepon dari aplikasi. Yang menjawab telepon bukan kode milik pemilik bot, melainkan model suara AI milik Pabal; lewat Bot API, pemilik bot menentukan instruksi, salam pembuka, suara, bahasa, dan batas yang harus diikuti model itu. Setiap kali panggilan dimulai dan berakhir, pemilik bot menerima update voice_call, dan selama panggilan ia bisa meminta model memanggil kode bot (tool) untuk mencari pesanan atau memeriksa reservasi. Ada juga mode stream, yaitu server Anda sendiri yang langsung menerima audionya sebagai ganti model AI. Jika ini bot pertama Anda, mulailah dari Membuat bot.

Status saat ini

Bot suara harus dinyalakan di tiap server, dan saat halaman ini terakhir diperbarui (2026-10-02) fitur ini mati di pabal.me โ€” sedang diuji di server pengembangan

Bot suara hanya bisa ditelepon di server yang memenuhi semua syarat di bawah. Metodenya tetap ada di server yang fitur suaranya mati, jadi pengaturan bisa dibaca. Namun selama suara belum menyala di server itu dan operator belum mengizinkan bot tersebut, tidak ada pengaturan yang disimpan kecuali yang mematikan sesuatu (mematikan suara, mematikan transkrip, keluar dari mode stream, menghapus kunci sendiri, menghapus server MCP): 403 VOICE_NOT_AVAILABLE. Untuk mengetahui apakah bot Anda bisa menerima telepon saat ini, lihat available dan unavailable_reason di getVoiceCallSettings.

FiturAgar menyala (semua syarat)pabal.me sekarang
Panggilan ke bot suaraServer punya gateway suara dan relay panggilan (TURN) ยท kebijakan privasi memuat bagian panggilan bot (bot-calls) ยท operator mengizinkan bot itu ยท sebelum peluncuran publik, hanya penguji yang ditunjuk operator yang bisa menelepon ยท aplikasi penelepon adalah build yang memuat layar konfirmasi (desktop pabal-bc2 โ€” selama layar konfirmasi hanya ada dalam bahasa Korea dan Inggris, pabal-bc1 juga diterima โ€”, Android 62151, iPhone build 5 ke atas, aplikasi jam tangan untuk Apple Watch build 1 ke atas) โ€” build seperti itu belum dirilisMati
Menelepon dari Apple WatchPanggilan bot suara + API jam tangan di server + pintu masuk audio jam tangan (pabal-voice-edge) + gateway yang menerima panggilan langsung (direct) ยท build aplikasi jam tangan minimal TELEGRAM_VOICE_MIN_BUILD_WATCHOS ยท belum untuk bot yang memakai tool, server MCP, kunci sendiri, mode stream, atau transkrip pemilik botMati โ€” sedang diuji di server pengembangan
Simpan percakapan (salinan penelepon)Panggilan bot suara + bagian bot-calls-keep-text di kebijakan + gateway yang mengumumkan keep_text (0.9.0) + build aplikasi yang memuat kotak centang ยท tinjauan hukum atas teks layar konfirmasiMati โ€” belum ditinjau secara hukum
Panggilan dari botBagian bot-calls-outbound dalam kebijakan privasi dan bagian voice-bot-owners-outbound dalam ketentuan layanan sudah dimuat (juga bagian voice-bot-owners, agar pemilik bisa menyetujui ketentuan versi 4) ยท sakelar umum operator, setelah tinjauan hukum ยท pemilik bot menyalakannya di @BotFather (ketentuan versi 4, tujuan) ยท tinjauan bot oleh operator ยท izin penerima panggilan (versi aplikasi dengan layar izin) ยท gateway dengan rekaman pengumuman panggilan keluar yang sudah disetujui ยท bahasa bot Korea atau InggrisMati โ€” menunggu tinjauan hukum
Bahasa bot selain bahasa Korea dan Inggris (7 bahasa)Panggilan ke bot suara + gateway yang bisa menangani panggilan dalam bahasa bot itu (sejak 0.7.0 mendukung kesembilan bahasa) ยท operator membaca instruksi dan salam pembuka bot itu lalu mengizinkannya (juga di server yang menerima semua bot)Mati โ€” belum dirilis
Layar konfirmasi dan pengumuman suara selain bahasa Korea dan Inggris (8 bahasa)Dimuat ke build aplikasi dan image gateway setelah tiap bahasa lolos telaah hukum dan pemeriksaan penutur asli. Sebelum itu, penelepon yang memakai aplikasi dalam bahasa tersebut pun melihat layar konfirmasi berbahasa Inggris dan mendengar pengumuman berbahasa InggrisDraf โ€” belum ditelaah
Ketentuan pemilik bot (versi 4)Ketentuan layanan memuat bagian pemilik bot (voice-bot-owners). Transkrip, kunci sendiri, mode stream, server MCP, dan panggilan dari bot semuanya mensyaratkan ketentuan ini lebih duluBelum bisa disetujui
ToolPanggilan ke bot suara + gateway yang mendukung toolMati
Server MCPKomponen MCP ยท alamat keluar khusus (syarat operasional โ€” tidak diperiksa oleh kode) ยท bagian bot-calls-mcp di kebijakan dan bagian pemilik bot di ketentuan ยท persetujuan ketentuan pemilik bot ยท build aplikasi yang menampilkan penerima MCP bot di layar konfirmasi (sebelum itu server bisa didaftarkan, tetapi tidak ada panggilan yang membawa tool MCP)Mati
Kunci model sendiriKunci induk server ยท dinyalakan operator setelah aplikasi merilis layar konfirmasi (versi 2) yang menampilkan penerima bot berkunci sendiri ยท ketentuan pemilik bot ยท gateway yang bisa memakai kunci milik pemilik (bawaannya hanya kunci Google)Mati โ€” kunci bahkan tidak diterima
TranskripOperator mengizinkannya di halaman admin setelah telaah hukum (bawaannya mati) ยท ketentuan pemilik bot ยท sakelar milik pemilik bot ยท gateway yang punya pengumuman suara transkrip ยท panggilan terkelolaMati
Mode streamKunci induk server ยท bagian bot-calls-stream di kebijakan dan bagian pemilik bot di ketentuan ยท gateway yang menerima panggilan mode stream ยท build aplikasi yang memuat layar konfirmasi mode stream (belum ada)Mati
  • Penelepon (orang yang menelepon) harus berusia 19 tahun ke atas; sebelum menelepon ia melihat layar konfirmasi "An AI answers this call" ("Panggilan ini dijawab oleh AI"), dan setelah tersambung ia lebih dulu mendengar pengumuman suara tentang AI (Yang dilihat penelepon). Aplikasi tanpa layar konfirmasi (termasuk aplikasi Telegram asli dan klien seperti Telethon) tidak menampilkan tombol panggil bot.
  • Audio panggilan terkelola dikirim ke Google โ€” Gemini Live API milik Vertex AI, di Iowa, Amerika Serikat (Google Cloud us-central1). Pabal tidak menyimpan audio. Google tidak menyimpan audio maupun teks setelah membuat jawaban, tetapi konten yang dicurigai sebagai penyalahgunaan oleh pemeriksaan otomatisnya bisa dicatat hingga 90 hari (sama dengan isi layar konfirmasi). Hanya mesin ini yang dipakai di produksi, dan Pabal tidak diam-diam mengalihkan ke penyedia lain (tidak ada peralihan otomatis).
  • Berbeda dengan panggilan antarmanusia, panggilan ke bot tidak dienkripsi ujung ke ujung: gateway suara Pabal mendekripsi audio lalu meneruskannya ke model AI. Layar konfirmasi penelepon memberitahukan hal ini.

Beginilah alur satu panggilan

Penelepon Pabal (server operator) Di luar Pabal Aplikasi Pabal Konfirmasi ยท 19+ Build yang diubah pabal-server MTProto ยท Bot API Penerimaan ยท batas Riwayat panggilan Program bot Bot API (HTTPS) Kode ยท server Anda coturn Hanya relay audio pabal-voice Pengumuman AI ยท instruksi Tidak menyimpan audio Google Vertex AI Gemini Live us-central1 (AS) โ‘  Telepon โ‘ก Link panggilan ยท pengaturan ยท tool โ‘ข voice_call ยท kueri tool Pengaturan ยท jawaban ยท endVoiceCall Audio (hanya relay) โ‘ฃ Audio โ†” jawaban AI
Panggilan terkelola: sinyal di atas, audio di bawah

Penjelasan diagram

  • Tiga area: aplikasi penelepon โ†’ server operator Pabal (server Pabal, gateway suara, server relay) โ†’ di luar Pabal (model AI Google, program pemilik bot). Kode pemilik bot tidak pernah melihat satu byte pun dari audionya.
  • โ‘  Telepon: aplikasi memberi tahu server persetujuan yang diberikan di layar konfirmasi, lalu menelepon. Server memeriksa penelepon (persetujuan โ€” ia menyatakan sendiri berusia 19 tahun ke atas ยท build aplikasi ยท penguji ยท batas) sekaligus bot (izin ยท pengaturan ยท batas), lalu menjawab atau mengakhiri panggilan dengan nada sibuk.
  • โ‘ก Link: server meneruskan panggilan ke gateway suara bersama instruksi, salam pembuka, suara, bahasa, dan tool milik pemilik bot. Gateway selalu menaruh aturan dasar Pabal (mengaku sebagai AI, dan lain-lain) lebih dulu, baru kemudian instruksinya.
  • โ‘ข Bot API: program pemilik bot menentukan pengaturan, menerima voice_call saat panggilan dimulai dan berakhir, serta menerima dan menjawab voice_call_tool_query saat model memanggil tool. Panah biru di โ‘ข adalah yang dikirim server ke bot, panah abu-abu di bawahnya adalah yang dikirim bot ke server.
  • โ‘ฃ Audio (baris bawah): aplikasi dan gateway tidak saling mengetahui alamat masing-masing dan hanya bertukar audio lewat server relay (coturn); gateway meneruskan audio ke model Google dan menerima jawabannya. Kotak merah menandai tempat audio keluar dari Pabal.
  • Dalam mode stream, server pemilik bot menggantikan posisi Google di โ‘ฃ (Mode stream).

Panggilan dari Apple Watch (sedang disiapkan)

Aplikasi jam tangan tidak memakai MTProto. Jam tangan masuk lewat API jam tangan di server (JSON lewat HTTPS), menerima daftar bot dan layar konfirmasi, lalu menelepon. Server menjalankan keputusan penerimaan yang sama seperti untuk panggilan dari aplikasi, lalu memberi jam tangan tiket sekali pakai (15 detik) yang hanya berlaku untuk panggilan itu, dan menyerahkan panggilan ke gateway. Jam tangan membawa tiket itu ke pintu masuk audio (WSS, pabal-voice-edge), dan pintu masuk menyambungkan audionya ke proses gateway yang menangani panggilan itu. Tidak ada pertukaran kunci (DH) maupun relay panggilan (coturn). Namun gateway tetap membuka audio untuk diteruskan ke model, jadi panggilannya tidak dienkripsi ujung ke ujung, dan audionya dikirim ke tempat yang sama. Pemilik bot menerima voice_call yang sama persis dengan panggilan dari aplikasi (hanya waktu started yang berbeda, lihat di bawah).

Menyalakan panggilan suara

Panggilan suara dinyalakan oleh manusia di @BotFather โ€” menyetujui aturan bot suara adalah janji pemilik bot, bukan janji token. Pengaturan lainnya bisa diatur di @BotFather maupun lewat Bot API.

  1. Minta operator mengizinkan bot Anda. Di server yang hanya menerima bot yang diizinkan (bawaannya), selama operator belum mengizinkan bot itu dengan ID bot (angka sebelum : di token) di tab Suara halaman admin, @BotFather tidak menampilkan tombol panggilan suara, dan mengubah pengaturan menghasilkan 403 VOICE_NOT_AVAILABLE. Sebelum peluncuran publik, minta juga agar akun yang akan Anda pakai untuk mencoba dijadikan penguji (ID pengguna Anda adalah message.from.id di getUpdates setelah Anda mengirim pesan ke bot).
  2. Tulis instruksinya. Ini teks yang menjelaskan apa yang dilakukan bot di telepon. Atur dengan setVoiceCallSettings di bawah, atau kirim lewat tombol Instruksi (์ง€์‹œ๋ฌธ) di layar panggilan suara @BotFather. Tanpa instruksi, fitur ini tidak bisa dinyalakan (kecuali mode stream).
  3. Di @BotFather: /mybots โ†’ bot Anda โ†’ Panggilan suara (์Œ์„ฑ ํ†ตํ™”) โ†’ Nyalakan (์ผœ๊ธฐ). Pertama kali, aturan bot suara akan muncul; tekan Setujui aturan dan nyalakan (๊ทœ์น™์— ๋™์˜ํ•˜๊ณ  ์ผœ๊ธฐ) untuk menyalakan. Selanjutnya BotFather hanya bertanya "Nyalakan?" (์ผค๊นŒ์š”?).
  4. Tunggu izin ulang. Di server yang hanya menerima bot yang diizinkan, izin berlaku untuk instruksi, salam pembuka, dan tool pada saat itu, jadi jika Anda mengubah instruksi setelah diizinkan, hasilnya unavailable_reason: "awaiting_review". Bot baru menerima telepon lagi setelah operator memeriksa dan mengizinkannya kembali. Hal yang sama berlaku setiap kali Anda mengubah salah satu dari ketiganya (atau alamat stream dalam mode stream). Jika bahasa bot bukan bahasa Korea atau Inggris, bot baru menerima telepon setelah operator membaca instruksi dan salam pembukanya lalu mengizinkannya, bahkan di server yang menerima semua bot (Suara dan bahasa).
  5. Pastikan available di getVoiceCallSettings bernilai true, lalu coba telepon lewat tombol panggil di profil bot dari build aplikasi yang memuat layar konfirmasi.
์ผœ๊ธฐ
BotFather์Œ์„ฑ ํ†ตํ™”๋ฅผ ์ผœ๊ธฐ ์ „์— ์Œ์„ฑ ๋ด‡ ๊ทœ์น™์„ ํ™•์ธํ•ด ์ฃผ์„ธ์š”. 1. ๋ด‡์€ ์–ธ์ œ๋‚˜ AI ๋ผ๊ณ  ๋ฐํ˜€์š”. ์‚ฌ๋žŒ์ธ ์ฒ™ํ•˜๊ฒŒ ํ•  ์ˆ˜ ์—†์–ด์š”. 2. ์‹ค์ œ ๊ธฐ๊ด€ยทํšŒ์‚ฌยท์‚ฌ๋žŒ์„ ์‚ฌ์นญํ•  ์ˆ˜ ์—†์–ด์š”. 3. ์นด๋“œ ๋ฒˆํ˜ธยท์ฃผ๋ฏผ๋“ฑ๋ก๋ฒˆํ˜ธยท๋น„๋ฐ€๋ฒˆํ˜ธยท์ธ์ฆ ๋ฒˆํ˜ธ๋ฅผ ๋ฌป๊ฒŒ ํ•  ์ˆ˜ ์—†์–ด์š”. 4. ํ†ตํ™” ์†Œ๋ฆฌ๋Š” ํŒŒ๋ฐœ์ด ์ €์žฅํ•˜์ง€ ์•Š๊ณ , ๋Œ€๋‹ต์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด Google(Vertex AI)๋กœ ๋ณด๋‚ด์ ธ์š”. 5. ๋ด‡๊ณผ์˜ ์Œ์„ฑ ํ†ตํ™”๋Š” ๋งŒ 19์„ธ ์ด์ƒ๋งŒ ํ•  ์ˆ˜ ์žˆ์–ด์š”. 6. ๊ทœ์น™์„ ์–ด๊ธฐ๋ฉด ์šด์˜์ž๊ฐ€ ์Œ์„ฑ ํ†ตํ™”๋‚˜ ๋ด‡์„ ๋ฉˆ์ถœ ์ˆ˜ ์žˆ์–ด์š”.
๊ทœ์น™์— ๋™์˜ํ•˜๊ณ  ์ผœ๊ธฐ
BotFather์Œ์„ฑ ํ†ตํ™”๋ฅผ ์ผฐ์–ด์š”. ์Œ์„ฑ ํ†ตํ™” ยท @order_helper_bot ์ƒํƒœ: ์ผœ์ง ๋ชฉ์†Œ๋ฆฌ: Kore (๋‹จ๋‹จํ•œ) ยท ์–ธ์–ด: ํ•œ๊ตญ์–ด โ€ฆ ์ „ํ™”๋ฅผ ๋ฐ›์œผ๋ฉด ๋จผ์ € "AI ์Œ์„ฑ ๋ด‡๊ณผ ์—ฐ๊ฒฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค" ์•ˆ๋‚ด๊ฐ€ ๋‚˜๊ฐ€๊ณ , ๊ทธ๋‹ค์Œ ๋ด‡์ด ์ธ์‚ฌํ•ด์š”.

Saat ini BotFather membalas dalam bahasa Korea. Isi aturannya: 1. bot selalu mengaku sebagai AI dan tidak boleh dibuat berpura-pura menjadi manusia; 2. bot tidak boleh meniru lembaga, perusahaan, atau orang yang nyata; 3. bot tidak boleh dibuat menanyakan nomor kartu, nomor identitas kependudukan, kata sandi, atau kode verifikasi; 4. Pabal tidak menyimpan audio panggilan, dan audio itu dikirim ke Google (Vertex AI) untuk membuat jawaban; 5. hanya orang berusia 19 tahun ke atas yang boleh menelepon bot; 6. jika aturan dilanggar, operator bisa menghentikan panggilan suara atau botnya. Setelah disetujui, BotFather memberi tahu bahwa panggilan suara sudah menyala, menampilkan statusnya (suara Kore, bahasa Korea, โ€ฆ), dan mengingatkan bahwa setiap panggilan diawali pengumuman "You're now talking to an AI voice bot." sebelum bot menyapa.

Di server yang hanya menerima bot yang diizinkan, statusnya bisa tampil seperti ini: "์ผœ์ง โ€” ๋ฐ”๋€ ์ง€์‹œ๋ฌธยท์ธ์‚ฌ๋ง์„ ์šด์˜์ž๊ฐ€ ํ™•์ธํ•  ๋•Œ๊นŒ์ง€ ์ „ํ™”๋ฅผ ๋ฐ›์ง€ ์•Š์•„์š”" ("Menyala โ€” tidak menerima telepon sampai operator memeriksa instruksi dan salam pembuka yang diubah") (langkah 4 di atas). Jika bahasa bot bukan bahasa Korea atau Inggris, di server mana pun statusnya tampil seperti ini sampai operator mengizinkannya: "์ผœ์ง โ€” ํ•œ๊ตญ์–ดยท์˜์–ด๊ฐ€ ์•„๋‹Œ ๋ด‡์€ ์šด์˜์ž๊ฐ€ ์ง€์‹œ๋ฌธยท์ธ์‚ฌ๋ง์„ ์ฝ๊ณ  ํ—ˆ์šฉํ•  ๋•Œ๊นŒ์ง€ ์ „ํ™”๋ฅผ ๋ฐ›์ง€ ์•Š์•„์š” (์ž๋™ ๊ทœ์น™ ๊ฒ€์‚ฌ๊ฐ€ ํ•œ๊ตญ์–ด์™€ ์˜์–ด๋งŒ ์ฝ์–ด์š”)" ("Menyala โ€” bot yang bahasanya bukan bahasa Korea atau Inggris tidak menerima telepon sampai operator membaca instruksi dan salam pembukanya lalu mengizinkannya (pemeriksaan aturan otomatis hanya membaca bahasa Korea dan Inggris)").

Layar panggilan suara punya tombol nyalakan/matikan, instruksi, salam pembuka, suara, bahasa, batas, pemakaian, transkrip, kunci sendiri, mode stream, dan (di server yang MCP-nya menyala) server MCP. Bahkan setelah suara dimatikan atau izinnya dicabut, mematikan dan menghapus selalu bisa dilakukan โ€” mematikan suara, menghapus kunci sendiri, mematikan mode stream, menghapus server MCP.

Nama bot juga diperiksa terhadap peniruan identitas, saat dinyalakan dan pada setiap panggilan. Bot yang memakai nama lembaga atau perusahaan yang nyata tidak bisa dinyalakan: VOICE_CONTENT_REFUSED: name: impersonation. Jika bot itu memang milik lembaga tersebut, hubungi layanan pelanggan server.

Pengaturan โ€” setVoiceCallSettings

Menentukan pengaturan suara bot. Ini perubahan sebagian: hanya yang Anda kirim yang berubah; server memeriksa semuanya lalu menulisnya sekaligus, atau menolak semuanya. Jika tidak ada yang berubah, tidak ada yang ditulis.

curl -s "https://pabal.me/bot$BOT_TOKEN/setVoiceCallSettings" -H 'Content-Type: application/json' -d '{
  "instructions": "Anda adalah petugas telepon toko Bantu Pesan. Jawab dengan singkat dan sopan. Toko buka pukul 10.00 sampai 20.00.",
  "greeting": "Bantu Pesan, selamat siang. Ada yang bisa kami bantu?",
  "voice": "kore",
  "language": "ko-KR",
  "max_call_duration": 180
}'
# {"ok":true,"result":true}
ParameterTipeDeskripsi
enabledBooleanNyalakan atau matikan. Untuk menyalakan, aturan harus sudah disetujui di @BotFather (jika belum, 403 VOICE_RULES_NOT_ACCEPTED), dan untuk mode terkelola harus ada instruksi. Mematikan selalu bisa โ€” panggilan yang sedang berlangsung tetap berjalan sampai selesai, hanya telepon baru yang tidak diterima
instructionsStringInstruksi yang diikuti model. Hingga 8.192 byte UTF-8. "" menghapusnya โ€” bot terkelola yang menyala tidak bisa mengosongkannya. Makin panjang, makin mahal biaya panggilan, dan karena penelepon bisa saja mengetahui isinya, jangan taruh kata sandi atau kunci di dalamnya
greetingStringKalimat pertama bot setelah pengumuman AI. Hingga 512 byte. Jika "", model menyapa singkat dengan sendirinya
voiceStringID suara (daftar), bawaan kore
languageStringBahasa yang diucapkan dan dipahami bot, salah satu dari 9 id (bawaan ko-KR, daftar). zh-CN disimpan sebagai cmn-CN, dan id yang hanya berbeda huruf besar-kecilnya (ja-jp) disimpan dalam bentuk yang ada di daftar. Jika bukan bahasa Korea atau Inggris, bot baru menerima telepon setelah operator membaca instruksi dan salam pembukanya lalu mengizinkannya. Pengumuman suara AI di awal tidak memakai bahasa ini, melainkan bahasa layar konfirmasi yang dilihat penelepon
max_call_durationIntegerDurasi maksimum satu panggilan (detik). Minimal 60, maksimal limits.max_call_duration (yang lebih kecil antara batas atas operator dan server, bawaan 600). Nilai bawaan bot 300
max_concurrent_callsIntegerJumlah panggilan yang diterima bersamaan. Minimal 1, maksimal limits.max_concurrent_calls (bawaan 2). Nilai bawaan bot 2
toolsArrayTool bot yang bisa dipanggil model selama panggilan (Tool). [] menghapus semuanya
transcriptBooleanSakelar transkrip (Transkrip)
mode, stream_url, stream_secretStringMode stream (Pengaturan mode stream)
model, api_keyTidak diterima โ€” VOICE_FIELD_NOT_AVAILABLE. Model ditentukan server, dan kunci hanya lewat setVoiceCallApiKey
  • Aturan konten: instruksi, salam pembuka, dan deskripsi tool diperiksa saat disimpan dan pada setiap panggilan. Menyuruh bot mengaku manusia (claims_human), menyembunyikan bahwa ia AI (hides_ai), menyuruhnya mengabaikan aturan Pabal (overrides_rules), meniru lembaga atau perusahaan yang nyata (impersonation), memperkenalkan diri sebagai lembaga resmi (official_claim), menanyakan nomor kartu, nomor identitas kependudukan, kata sandi, atau kode verifikasi (asks_secrets), teks yang tampak seperti kunci rahasia (looks_like_secret), dan karakter tak terlihat (hidden_text) ditolak dengan VOICE_CONTENT_REFUSED: <kolom>: <kode>. Kalimat penyangkalan seperti "Jangan pernah menanyakan kata sandi" tetap lolos.
  • Aturan dasar Pabal selalu didahulukan. Pada setiap panggilan, model membaca berurutan: aturan Pabal โ†’ instruksi pemilik bot โ†’ penutup aturan Pabal, dan instruksi ditempatkan di dalam blok yang ditandai sebagai "teks pemilik bot". Instruksi tidak bisa menggantikan atau mendahului aturan Pabal.
  • Bisa diubah hingga 30 kali per jam (termasuk lewat @BotFather, dan hanya perubahan yang benar-benar terjadi yang dihitung). Jika lebih: 429 Too Many Requests: retry after N beserta parameters.retry_after.
  • Jika dikirim sebagai formulir (-d name=value), isi tools sebagai string JSON.
  • Bot yang hanya masuk lewat MTProto mengirim JSON yang sama dengan bots.sendCustomRequest (Bot MTProto).

Melihat pengaturan saat ini โ€” getVoiceCallSettings

curl -s "https://pabal.me/bot$BOT_TOKEN/getVoiceCallSettings"
{
  "ok": true,
  "result": {
    "enabled": true,
    "available": true,
    "suspended": false,
    "rules_accepted": true,
    "instructions": "Anda adalah petugas telepon toko Bantu Pesan. โ€ฆ",
    "greeting": "Bantu Pesan, selamat siang. Ada yang bisa kami bantu?",
    "voice": "kore",
    "language": "ko-KR",
    "max_call_duration": 180,
    "max_concurrent_calls": 2,
    "limits": {"max_call_duration": 600, "max_concurrent_calls": 2, "bot_minutes_per_day": 60, "owner_minutes_per_day": 120},
    "voices": [{"id": "zephyr", "name": "Zephyr", "description": "Bright"}, โ€ฆ],
    "languages": ["ko-KR", "en-US", "ja-JP", "cmn-CN", "fr-FR", "es-US", "id-ID", "vi-VN", "ar-EG"],
    "testers_only": true,
    "mode": "managed",
    "has_stream_secret": false,
    "owner_terms_accepted": false,
    "stream_available": false,
    "updated_date": 1790380712,
    "tools": [],
    "tool_limits": {"max_tools": 16, "max_definition_bytes": 8192, "definition_bytes": 0, "mcp_tools": 0},
    "tool_delivery": "none",
    "transcript": false,
    "transcript_available": false,
    "transcript_delivery": "get_updates",
    "has_api_key": false,
    "own_keys_available": false,
    "mcp_available": false
  }
}
KolomArti
enabledApakah pemilik bot sudah menyalakannya
available, unavailable_reasonApakah bot bisa menerima telepon saat ini, siapa pun peneleponnya, dan jika tidak, alasannya (tabel di bawah)
rules_accepted, owner_terms_acceptedApakah aturan bot suara dan ketentuan pemilik bot yang berlaku (versi 4) sudah disetujui
limitsNilai maksimum yang bisa diatur di server ini dan batas harian (menit)
voices, languagesSuara dan bahasa yang bisa dipilih
testers_onlytrue jika hanya penguji yang ditunjuk operator yang bisa menelepon karena fitur ini belum dibuka untuk publik
tools, tool_limits, tool_delivery, tool_delivery_problemTool beserta batasnya, jalur yang saat ini akan ditempuh kueri tool (webhookยทget_updatesยทmtprotoยทnone), dan alasan kueri tidak bisa sampai (Menerima kueri tool)
mode, stream_url, stream_secret_fingerprint, has_stream_secret, stream_available, stream_ignoresMode stream โ€” rahasianya tidak pernah ditampilkan, hanya sidik jari 8 karakter
transcript, transcript_available, transcript_unavailable_reason, transcript_delivery, transcript_delivery_problemSakelar transkrip, dan apakah transkrip benar-benar akan dikirim
has_api_key, api_key, own_keys_availableKunci model sendiri โ€” kuncinya tidak pernah ditampilkan, hanya penyedia, sidik jari, tanggal diatur, dan apakah bisa dipakai
mcp_available, mcp_servers, mcp_unavailable_reasonServer MCP
outboundStatus panggilan dari bot (Panggilan dari bot โ€” pengaturan dan penggunaan)
updated_dateWaktu perubahan terakhir (detik Unix)
unavailable_reasonArti
voice_offSuara dimatikan di server ini, atau kebijakan privasi belum memuat bagian panggilan bot
kill_switchOperator menghentikan semua panggilan suara
bot_voice_offPemilik bot belum menyalakannya
not_allowedOperator tidak mengizinkan bot ini
awaiting_reviewOperator belum memeriksa instruksi, salam pembuka, atau tool yang diubah (dalam mode stream, alamat stream). Jika bahasa bot bukan bahasa Korea atau Inggris, operator harus membaca instruksi dan salam pembukanya lalu mengizinkannya, bahkan di server yang menerima semua bot
suspendedOperator menangguhkan fitur suara bot ini (atau akun pemiliknya)
name_refused, text_refusedNama bot, atau instruksi, salam pembuka, atau deskripsi tool yang tersimpan, tidak sesuai dengan aturan konten saat ini. text_refused juga muncul jika bahasa yang dipilih bot sudah dikeluarkan dari daftar sehingga tidak bisa dipakai lagi โ€” pilih bahasa lagi
no_relay, no_gatewayServer tidak punya relay panggilan atau gateway suara
no_gateway_with_tools, no_own_key_gatewayTidak ada gateway yang bisa memakai tool atau kunci sendiri bot ini
no_gateway_with_languageTidak ada gateway tersambung yang bisa menangani panggilan dalam bahasa bot ini (Suara dan bahasa)
own_key_unavailable, stream_unavailableKunci sendiri atau mode stream yang diatur tidak bisa dipakai saat ini, sehingga bot tidak menerima telepon (bot juga tidak menerimanya dengan kunci platform sebagai gantinya)

Suara dan bahasa

Pilih dari 30 suara siap pakai Gemini Live (bawaan kore). Tidak ada fitur mengunggah atau mengkloning suara. Di tombol Suara (๋ชฉ์†Œ๋ฆฌ) @BotFather, Anda memilih berdasarkan nama dan karakternya.

idKarakteridKarakteridKarakter
zephyrcerahpuckriangcharontegas dan jelas
koremantapfenrirbersemangatledamuda
orusmantapaoedesegarcallirrhoesantai
autonoecerahenceladusmendesahiapetusjernih
umbrielsantaialgiebalembutdespinalembut
erinomejernihalgenibserakrasalgethitegas dan jelas
laomedeiariangachernarhalusalnilammantap
schedartenanggacruxdewasapulcherrimalugas
achirdramahzubenelgenubinyamanvindemiatrixpenuh perhatian
sadachbiabertenagasadaltagerberwawasansulafathangat

Bahasa (language) adalah salah satu dari 9 bahasa di bawah. Inilah bahasa yang diucapkan dan dipahami bot, dan bot menjawab dalam bahasa ini. Di @BotFather, pilih lewat tombol Bahasa (์–ธ์–ด) di layar panggilan suara.

idBahasaYang perlu diketahui
ko-KRBahasa KoreaBawaan
en-USBahasa Inggrisโ€”
ja-JPBahasa Jepangโ€”
cmn-CNBahasa Tionghoa (Mandarin)Berada di luar daftar dukungan resmi Google, sehingga Google tidak menjamin kualitasnya (dalam pengujian, baik pemahaman maupun jawabannya tepat). Dikirim sebagai zh-CN pun diterima dan disimpan sebagai cmn-CN
fr-FRBahasa Prancisโ€”
es-USBahasa Spanyolโ€”
id-IDBahasa Indonesiaโ€”
vi-VNBahasa Vietnamโ€”
ar-EGBahasa Arab (standar)Bot berbicara dalam bahasa Arab standar modern. Kodenya ar-EG, satu-satunya kode bahasa Arab di model real-time Google, tetapi kode ini tidak menentukan dialek โ€” bot hanya berbicara dalam dialek tertentu jika instruksi memintanya
  • Bot yang bahasanya bukan bahasa Korea atau Inggris baru menerima telepon setelah operator membaca instruksi dan salam pembukanya lalu mengizinkannya โ€” bahkan di server yang menerima semua bot. Alasannya, pemeriksaan aturan otomatis saat pengaturan disimpan (berpura-pura menjadi manusia, peniruan identitas, dan sebagainya) hanya membaca bahasa Korea dan Inggris. Sebelum diizinkan, hasilnya unavailable_reason: "awaiting_review", dan jika instruksi, salam pembuka, atau tool diubah setelah diizinkan, bot kembali menunggu izin. Di layar pemilihan bahasa dan setelah bahasa dipilih, @BotFather memberi tahu: "ํ•œ๊ตญ์–ดยท์˜์–ด๊ฐ€ ์•„๋‹Œ ์–ธ์–ด๋Š” ์ž๋™ ๊ทœ์น™ ๊ฒ€์‚ฌ๊ฐ€ ์ฝ์ง€ ๋ชปํ•ด์„œ, ์šด์˜์ž๊ฐ€ ์ง€์‹œ๋ฌธยท์ธ์‚ฌ๋ง์„ ์ฝ๊ณ  ํ—ˆ์šฉํ•œ ๋’ค์— ์ „ํ™”๋ฅผ ๋ฐ›์•„์š”." ("Bahasa selain bahasa Korea dan Inggris tidak bisa dibaca oleh pemeriksaan aturan otomatis, jadi bot baru menerima telepon setelah operator membaca instruksi dan salam pembukanya lalu mengizinkannya"). Di tab Suara halaman admin, bot itu tampil sebagai "ํ•œ๊ตญ์–ดยท์˜์–ด๊ฐ€ ์•„๋‹Œ ๋ด‡ (์ž๋™ ๊ทœ์น™ ๊ฒ€์‚ฌ๊ฐ€ ์ฝ์ง€ ๋ชปํ•จ): ์ง€์‹œ๋ฌธยท์ธ์‚ฌ๋ง์„ ์ฝ์–ด ์ฃผ์„ธ์š”" ("Bot yang bahasanya bukan bahasa Korea atau Inggris (tidak terbaca oleh pemeriksaan aturan otomatis): bacalah instruksi dan salam pembukanya").
  • Harus ada gateway yang bisa menangani panggilan dalam bahasa bot. Saat tersambung ke server, gateway menyatakan bahasa bot yang bisa ia tangani (sejak 0.7.0 mendukung kesembilannya), dan gateway yang tidak menyatakan bahasa dianggap hanya menangani bahasa Korea dan Inggris. Jika tidak ada gateway yang bisa menangani panggilan dalam bahasa bot itu, hasilnya no_gateway_with_language dan tombol panggil disembunyikan. Panggilan tidak dilayani dalam bahasa lain sebagai gantinya.
  • Huruf besar dan kecil pada id tidak dibedakan (ja-jp pun diterima), dan id disimpan dalam bentuk yang ada di daftar. Nilai yang tidak ada di daftar ditolak dengan VOICE_LANGUAGE_INVALID: language is one of ko-KR, en-US, ja-JP, cmn-CN, fr-FR, es-US, id-ID, vi-VN, ar-EG, yang sekaligus menyebutkan id yang diterima.
  • Jika bahasa yang dipilih kemudian dikeluarkan dari daftar, bot itu tidak menerima telepon meskipun pengaturannya tetap tersimpan (text_refused). Bot tidak menerima telepon dalam bahasa lain sebagai gantinya, jadi pilih lagi lewat Bahasa (์–ธ์–ด) di @BotFather.
  • Bahasa Khmer belum bisa dipilih sebagai bahasa bot. Model memang memahaminya, tetapi jawaban bebasnya belum diperiksa oleh penutur asli bahasa Khmer.

Jika penelepon terus berbicara dalam bahasa lain, model menjawab dalam bahasa itu. Pengumuman suara AI tidak memakai language bot, melainkan bahasa layar konfirmasi yang dilihat penelepon (Yang dilihat penelepon).

Menerima notifikasi panggilan โ€” voice_call

Saat ada yang menelepon bot, update voice_call datang. Ketiga update suara (voice_call, voice_call_tool_query, voice_call_transcript) hanya datang jika diminta dengan menyebut namanya: masukkan ke allowed_updates milik getUpdates atau setWebhook. Dengan daftar kosong pun tidak datang (untuk jenis lain, daftar kosong berarti semuanya datang).

curl -s "https://pabal.me/bot$BOT_TOKEN/getUpdates" -H 'Content-Type: application/json' \
  -d '{"allowed_updates": ["message", "voice_call", "voice_call_tool_query"], "timeout": 30}'
{
  "update_id": 7,
  "voice_call": {
    "id": "4611686018427387905",
    "direction": "incoming",
    "event": "started",
    "from": {"id": 100002, "is_bot": false, "first_name": "Minji", "language_code": "ko"},
    "date": 1790380803,
    "max_duration": 180
  }
}
{
  "update_id": 9,
  "voice_call": {
    "id": "4611686018427387905",
    "direction": "incoming",
    "event": "ended",
    "from": {"id": 100002, "is_bot": false, "first_name": "Minji", "language_code": "ko"},
    "date": 1790380952,
    "start_date": 1790380803,
    "duration": 149,
    "max_duration": 180,
    "end_reason": "caller"
  }
}
  • direction: "incoming" untuk panggilan ke bot, "outgoing" untuk panggilan yang dibuat bot (update panggilan dari bot). Membaca kolom yang tidak ada sebagai panggilan ke bot, seperti sebelumnya, tetap benar.
  • allowed_updates adalah satu pengaturan per bot, dan yang tersimpan adalah nilai terakhir yang diberikan getUpdates atau setWebhook (jika tidak diberikan, nilai sebelumnya tetap berlaku). Karena itu, jika Anda memberi daftar seperti contoh di atas, jenis bawaan yang tidak Anda tulis di daftar (edited_message, callback_query) tidak akan datang lagi โ€” tulis semua jenis yang dipakai bot Anda. Notifikasi panggilan yang terjadi sebelum Anda memintanya tidak ditumpuk, jadi tidak akan datang meskipun Anda memintanya belakangan.
  • Dari penelepon (from), yang dikirim hanya ID, nama, dan bahasa aplikasinya. Nama pengguna dan nomor telepon tidak dikirim (itulah yang dijanjikan layar konfirmasi). Akun yang sudah dihapus muncul sebagai first_name: "Deleted Account".
  • Dengan webhook, update ini datang berurutan lewat POST seperti update lain. Antrean getUpdates menyimpan 1.000 update terakhir per bot di memori dan hilang saat server dimulai ulang.
  • Bot yang ditangguhkan atau dihapus tidak menerima apa pun.
eventKapanKolom tambahan
startedSaat aplikasi penelepon sudah mengonfirmasi panggilan (pertukaran kunci selesai) dan server memberi tahu gateway bahwa panggilan tersambung. Server tidak melihat audionya, jadi ini "tersambung" menurut pengetahuan server. Panggilan dari Apple Watch tidak punya pertukaran kunci, jadi ini adalah saat gateway memberi tahu bahwa audio jam tangan sudah tersambungmax_duration
endedSaat panggilan yang sempat tersambung berakhir. Jika menunggu transkrip, bisa datang paling lambat 10 detik kemudian. Di server yang menyalakan Simpan percakapan, semua panggilan yang bukan mode stream datang paling lambat 10 detik kemudian, apa pun pilihan penelepon โ€” agar pemilik bot tidak bisa mengetahui pilihan peneleponstart_date, duration (detik tersambung menurut jam server), max_duration, end_reason, (jika transkrip menyala) transcript
missedPanggilan yang berakhir sebelum tersambung (ditutup saat masih berdering, dan sebagainya)end_reason
refusedHanya jika penelepon lolos semua pemeriksaan tetapi panggilan tidak bisa diterima karena kondisi di pihak bot (panggilan bersamaan, menit harian, gateway) โ€” penolakan lain tidak diberitahukan kepada pemilik botrefused_reason: busy (panggilan bersamaan penuh) ยท budget (menit harian bot, pemilik, atau server habis) ยท unavailable (gateway yang bisa menerima sedang penuh)

end_reason: caller (penelepon menutup telepon) ยท bot (bot menutup, endVoiceCall) ยท max_length (durasi maksimum) ยท failed (sambungan gagal) ยท operator (operator atau sakelar darurat) ยท suspended (bot ditangguhkan atau dihapus).

Untuk setiap panggilan yang diteruskan ke gateway, catatan panggilan (durasi panggilan, atau "panggilan dibatalkan") tertinggal di obrolan antara penelepon dan bot (panggilan yang ditolak sebelum diterima tidak meninggalkan catatan). Salinan di sisi bot tidak datang sebagai update message โ€” catatan milik bot adalah voice_call. Jika penelepon memilih Simpan percakapan, teks percakapannya tertinggal hanya di obrolan penelepon, tepat di bawah catatan panggilan, dan tidak ada apa pun yang tertinggal di sisi bot. Di server yang menyalakan Simpan percakapan, 30 nomor pesan setelah catatan panggilan dipesan lebih dulu, jadi nomor pesan berikutnya setelah catatan panggilan setidaknya 31 lebih besar daripada nomor catatan itu (juga untuk panggilan yang tidak disimpan).

Selama panggilan

Menutup telepon โ€” endVoiceCall

curl -s "https://pabal.me/bot$BOT_TOKEN/endVoiceCall" -d voice_call_id=4611686018427387905
  • farewell (Boolean, bawaan true): jika panggilan sudah tersambung, model mengucapkan salam perpisahan singkat lalu menutup. Jika belum tertutup dalam 10 detik, server yang mengakhirinya. Jika false, langsung ditutup. Panggilan mode stream selalu langsung ditutup.
  • Untuk panggilan yang diakhiri dengan cara ini, voice_call.end_reason bernilai bot. Jika bukan panggilan aktif milik bot ini: 400 VOICE_CALL_NOT_FOUND.

Memberi catatan kepada model โ€” sendVoiceCallContext

Memberi tahu model hal yang perlu ia ketahui selama panggilan. Model membacanya sebagai teks pemilik bot, bukan ucapan penelepon, dan aturan Pabal tetap didahulukan.

curl -s "https://pabal.me/bot$BOT_TOKEN/sendVoiceCallContext" -d voice_call_id=4611686018427387905 \
  --data-urlencode "text=Penelepon ini baru saja membatalkan pesanan A-1042. Jika ditanya, sampaikan hal itu." -d respond=false
  • text: hingga 2.000 karakter (6.144 byte), diperiksa dengan aturan konten. Jika respond=true, model langsung bereaksi; jika false (bawaan), catatan dipakai pada giliran berikutnya.
  • Catatan juga dikirim ke model (ke Google untuk panggilan terkelola), dan model bisa menyampaikannya kepada penelepon โ€” isilah hanya hal yang boleh diketahui penelepon.
  • Sebelum tersambung: 400 VOICE_CALL_NOT_ACTIVE; panggilan mode stream atau gateway yang tidak menerima catatan: 400 VOICE_CONTEXT_NOT_SUPPORTED; lebih dari 10 per menit per panggilan: 429 (retry_after 60).

Pemakaian โ€” getVoiceCallUsage

curl -s "https://pabal.me/bot$BOT_TOKEN/getVoiceCallUsage?period=today"
{"ok": true, "result": {"period": "today", "from_date": 1790348400, "to_date": 1790434799,
 "calls": 3, "seconds": 412, "missed": 1,
 "refused": {"busy": 0, "budget": 0, "unavailable": 0},
 "platform_seconds": 412, "own_key_seconds": 0,
 "remaining_seconds_today": {"bot": 3188, "owner": 6788}}}
  • period: today (bawaan) atau month. Dengan month, Anda bisa memilih bulannya, misalnya month=2026-09 (bulan yang belum tiba menghasilkan VOICE_USAGE_PERIOD_INVALID).
  • Batas hari dan bulan mengikuti zona waktu server (bawaan Asia/Seoul). remaining_seconds_today adalah batas harian dikurangi panggilan yang sudah selesai dan waktu yang sedang dicadangkan oleh panggilan yang masih berlangsung.
  • Catatan pemakaian tiap panggilan (tanpa data penelepon) disimpan 180 hari setelah bulannya berakhir, dan total bulanan tetap ada selama bot masih ada.

Tool โ€” memanggil kode bot selama panggilan

Jika penelepon bertanya "Pesanan A-1042 saya sudah sampai mana?", model memanggil tool check_order milik bot, server meneruskan kueri itu ke bot, lalu model menyampaikan jawaban bot secara lisan. Tool didefinisikan oleh pemilik bot, dan kode bot yang menjawabnya.

Model ยท gateway Server Pabal Kode bot โ‘  check_order {"order_id": "A-1042"} Cek argumen, buang kunci lain No. kartu/NIK: berhenti di sini โ‘ก voice_call_tool_query ยท budget_ms Hening: "One moment, let me check." 5 dtk: "Just a moment longer, please." โ‘ข answerVoiceCallToolQuery(result) Jawaban dicek, rahasia ditolak Maks. 16.384 byte โ‘ฃ tool_result (hanya data) Model menyampaikan hasilnya Terlambat: "The tool did not answer in time."
Satu kueri tool: model โ†’ server โ†’ bot โ†’ server โ†’ model

Penjelasan diagram

  • Tiga lajur: kiri adalah model dan gateway suara, tengah server Pabal, kanan kode milik pemilik bot. Garis putus-putus vertikal adalah waktu. Model tidak berbicara langsung dengan bot; semua kueri dan jawaban lewat server.
  • Kotak abu-abu di โ‘  โ€” server memeriksa argumen lebih dulu: apakah sesuai dengan parameters tool, ukuran dan kedalamannya, kunci yang tidak dideklarasikan (dibuang sebelum diteruskan), serta nilai yang tampak seperti nomor kartu atau nomor identitas kependudukan (tidak dikirim ke bot, dan model mendengar penolakan).
  • Yang diterima bot di โ‘ก: ID kueri, ID panggilan, penelepon, nama tool, argumen (string JSON), dan budget_ms โ€” sisa milidetik sampai jawaban harus tiba di server. Selama menunggu, gateway mengisi keheningan (kotak kiri).
  • Jawaban di โ‘ข juga diperiksa: yang tampak seperti rahasia, kalimat yang mendorong pelanggaran aturan, serta nomor kartu atau nomor identitas kependudukan ditolak, dan model mendengar pesan error. Jawaban pertama bersifat final, jadi jawaban yang ditolak pun menutup kuerinya.
  • โ‘ฃ Garis biru utuh adalah kondisi normal, garis putus-putus merah adalah saat tenggat terlewat. Model menerima hasil tool hanya sebagai data, bukan sebagai perintah; jika gagal, model mendengar kalimat pendek dari Pabal lalu menjelaskan keadaannya kepada penelepon.

Mendefinisikan tool

curl -s "https://pabal.me/bot$BOT_TOKEN/setVoiceCallSettings" -H 'Content-Type: application/json' -d '{
  "instructions": "Anda adalah petugas telepon toko Bantu Pesan. Jawab dengan singkat dan sopan. Jika penelepon menyebut nomor pesanan, cari status pengirimannya dengan tool check_order lalu sampaikan.",
  "tools": [{
    "name": "check_order",
    "description": "Mencari status pengiriman berdasarkan nomor pesanan. Dipanggil saat penelepon menyebut nomor pesanan.",
    "parameters": {"type": "object",
                   "properties": {"order_id": {"type": "string", "description": "Nomor pesanan, misalnya A-1042"}},
                   "required": ["order_id"]},
    "timeout_ms": 5000
  }]
}'
ButirAturan
KunciHanya empat: name, description, parameters (opsional), timeout_ms (opsional)
Jumlah dan ukuranTotal 16 termasuk tool bot dan tool MCP yang disematkan, dan jumlah UTF-8 semua definisi (nameยทdescriptionยทparameters) 8.192 byte. Sama untuk bot berkunci sendiri
name[A-Za-z_][A-Za-z0-9_-]{0,63}. Tidak boleh berisi __ (dipakai untuk nama tool MCP), tidak boleh diawali pabal, dan tidak boleh sama dengan nama lain meski beda huruf besar-kecil
descriptionWajib, hingga 1.024 byte, diperiksa dengan aturan konten. Model memutuskan kapan memanggil tool berdasarkan deskripsi ini, jadi tulislah dengan spesifik
parametersDi tingkat teratas {"type": "object"}. Jika dihilangkan, berarti "tanpa argumen"
timeout_ms1.000โ€“15.000, bawaan 5.000. Waktu maksimum model menunggu jawaban

parameters hanya memakai sebagian dari JSON Schema yang diterima oleh kedua mesin.

  • Kata kunci hanya sepuluh: type, description, enum, properties, required, items, minimum, maximum, minItems, maxItems. type hanya satu (stringยทnumberยทintegerยทbooleanยทarrayยทobject).
  • propertiesยทrequired untuk object, itemsยทminItemsยทmaxItems untuk array (items wajib), minimumยทmaximum untuk angka, enum hanya untuk string (1โ€“32 nilai, masing-masing 64 byte).
  • Nama properti [A-Za-z_][A-Za-z0-9_]{0,63}, 24 properti per tool, object paling dalam 3 tingkat dan gabungan object dan array paling dalam 5 tingkat. Deskripsi parameter 512 byte.
  • Parameter yang menanyakan rahasia ditolak: jika namanya berisi kata seperti pinยทcvvยทpasswordยทotpยทssnยทrrnยทcardnumber, atau deskripsinya menyebut nomor kartu, kata sandi, kode verifikasi, atau nomor identitas kependudukan, hasilnya asks_secrets.
  • Error menyebutkan masalah pertama beserta jalurnya: VOICE_TOOL_INVALID: tools[0].parameters.properties.tags: items_missing, VOICE_TOOL_INVALID: tools[0].name: reserved, VOICE_CONTENT_REFUSED: tools[0].description: impersonation.
  • Tool hanya diatur lewat Bot API. Layar panggilan suara @BotFather hanya menampilkan nama tool.
  • Di server yang hanya menerima bot yang diizinkan, menambah atau mengubah tool membuat bot tidak menerima telepon sampai operator mengizinkannya lagi (awaiting_review, langkah 4 di Menyalakan).

Menerima kueri tool

{
  "update_id": 813,
  "voice_call_tool_query": {
    "id": "8190448532776044545",
    "voice_call_id": "4611686018427387905",
    "from": {"id": 100002, "is_bot": false, "first_name": "Minji", "language_code": "ko"},
    "name": "check_order",
    "arguments": "{\"order_id\":\"A-1042\"}",
    "budget_ms": 4700
  }
}
  • arguments adalah teks sebuah objek JSON (paling banyak 4.096 byte dan kedalaman 8, sudah diperiksa server). Meski begitu, itu adalah nilai yang dibuat model dari ucapan penelepon, jadi periksa ulang di bot. Mengetahui nomor pesanan saja tidak boleh cukup untuk mendengar pesanan orang lain โ€” beri tahu hanya data milik from.id (penelepon yang sudah diverifikasi Pabal).
  • budget_ms adalah milidetik sejak bot menerimanya sampai jawaban harus tiba di server. Nilainya adalah timeout_ms tool dikurangi jalur kembali ke model (200 ms) dan jalur sampai ke bot (100 ms), jadi biasanya sekitar 300 ms lebih kecil dari timeout_ms. Jam kedua pihak tidak dipakai.

Jalur kueri menuju bot ditentukan dengan urutan ini (getVoiceCallSettings.tool_delivery).

  1. Lewat webhook, jika ada. voice_call_tool_query harus ada di allowed_updates webhook; jika tidak ada, kueri tidak mencari jalur lain dan tidak sampai (tool_delivery_problem: "not_in_allowed_updates"). Kueri adalah POST yang dikirim terpisah, jadi tidak tertahan oleh antrean berurutan, dan tidak dikirim ulang.
  2. Jika tidak ada webhook tetapi bot memanggil getUpdates dalam 15 detik terakhir, kueri ditaruh di urutan paling depan getUpdates berikutnya. Kueri juga harus ada di allowed_updates. Paling banyak 8 kueri menunggu per bot.
  3. Selain itu, jika bot tersambung lewat MTProto, lewat updateBotWebhookJSONQuery (tidak bergantung pada allowed_updates).
  4. Jika tidak ada satu pun jalur itu, kueri tidak sampai (tool_delivery_problem: "not_listening"), dan model mendengar "The tool could not be reached." ("Tool tidak dapat dihubungi").

Menjawab โ€” answerVoiceCallToolQuery

ParameterDeskripsi
tool_query_idvoice_call_tool_query.id dari update
resultHasilnya. Teks, atau objek/array jika badan permintaannya JSON (teks yang terbaca sebagai objek/array JSON juga diperlakukan sebagai JSON). Hingga 16.384 byte UTF-8 (operator bisa menurunkannya)
errorPesan kegagalan milik bot sendiri, hingga 512 byte. Model menyampaikan kalimat ini kepada penelepon
  • Tepat satu dari result dan error (tidak ada keduanya: TOOL_RESULT_EMPTY; ada keduanya: TOOL_RESULT_INVALID). Mengembalikan True.
  • Jawaban tool dikirim ke model (ke Google untuk panggilan terkelola), dan model menyampaikan isinya kepada penelepon โ€” isilah hanya hal yang boleh diketahui penelepon.
  • Jawaban pertama bersifat final. Untuk kueri yang terlambat atau sudah berakhir: 400 TOOL_QUERY_EXPIRED: <alasan> โ€” answered (sudah dijawab), timeout, cancelled (penelepon menyela dan model menariknya), call_ended, unreachable. ID yang tidak dikenal atau yang sudah lewat 10 menit: TOOL_QUERY_ID_INVALID.
  • Jawaban juga diperiksa: karakter tak terlihat dibuang, sedangkan yang tampak seperti rahasia (looks_like_secret), kalimat yang mendorong pelanggaran aturan (overrides_rulesยทhides_aiยทclaims_humanยทasks_secrets), serta nomor kartu atau nomor identitas kependudukan (sensitive_data) menghasilkan TOOL_RESULT_REFUSED: <kode>. Data milik pemilik bot, seperti alamat, nomor telepon, atau URL toko, tetap lolos. Jika melebihi ukuran: TOOL_RESULT_TOO_LONGยทTOOL_ERROR_TOO_LONG.
  • Dengan webhook, Anda bisa menjawab lewat badan respons โ€” cukup taruh ini di respons 200 untuk POST kueri tool (metode lain tidak dijalankan). Sebelum menjawab, pastikan selalu memeriksa header X-Telegram-Bot-Api-Secret-Token. Jika data pelanggan dimasukkan ke respons tanpa pemeriksaan itu, siapa pun yang mengirim kueri palsu ke alamat tersebut akan mendapatkan jawabannya.
{"method": "answerVoiceCallToolQuery", "tool_query_id": "8190448532776044545",
 "result": {"order_id": "A-1042", "status": "Sedang dikirim", "eta": "Tiba besok siang"}}

Contoh โ€” python-telegram-bot

Ini bot yang menjawab tool check_order di atas. Bot menerima lewat getUpdates dan memproses kueri bersamaan dengan update lain (concurrent_updates) โ€” jika tertunda karena memproses update sebelumnya, budget_ms bisa habis. Pesanan dicari berdasarkan ID pengguna Pabal milik orang yang memesan, dan hanya diberitahukan jika cocok dengan from.id kueri.

# order_voice_bot.py โ€” pip install "python-telegram-bot==22.*"
# Jalankan: read -rs BOT_TOKEN && export BOT_TOKEN
#           CUSTOMER_ID=<ID pengguna Pabal milik pemesan> python3 order_voice_bot.py
# Untuk mencoba, isi CUSTOMER_ID dengan ID Anda sendiri โ€” yaitu message.from.id di getUpdates setelah Anda mengirim pesan ke bot
import json
import os
import re

from telegram import Update
from telegram.error import TelegramError
from telegram.ext import Application, ContextTypes, TypeHandler

SERVER = "https://pabal.me"
CUSTOMER = int(os.environ.get("CUSTOMER_ID", "0"))
ORDERS = {                                    # tabel kecil untuk contoh; aslinya sistem pesanan toko
    (CUSTOMER, "A-1042"): {"status": "Sedang dikirim", "eta": "Tiba besok siang"},
}


def check_order(arguments, caller_id):
    """{"result": โ€ฆ} atau {"error": โ€ฆ} untuk diteruskan apa adanya ke answerVoiceCallToolQuery"""
    order_id = str(json.loads(arguments).get("order_id", "")).strip().upper()
    if not re.fullmatch(r"[A-Z]-\d{4}", order_id):     # argumen dibuat oleh model โ€” periksa ulang
        return {"error": "Nomor pesanannya tidak bisa dipahami. Contoh: A-1042"}
    found = ORDERS.get((caller_id, order_id))          # hanya pesanan milik orang yang menelepon
    if found is None:                                  # pesanan orang lain dijawab sama seperti pesanan yang tidak ada
        return {"error": f"Pesanan {order_id} tidak ditemukan."}
    return {"result": json.dumps({"order_id": order_id, **found}, ensure_ascii=False)}


async def on_update(update: Update, context: ContextTypes.DEFAULT_TYPE):
    q = update.api_kwargs.get("voice_call_tool_query")  # jenis yang tidak dikenal PTB masuk ke api_kwargs
    if q is None:
        return
    if q["name"] == "check_order":
        answer = check_order(q["arguments"], q["from"]["id"])
    else:
        answer = {"error": "Tool tidak dikenal."}
    try:
        await context.bot.do_api_request("answerVoiceCallToolQuery",
                                         api_kwargs={"tool_query_id": q["id"], **answer})
    except TelegramError as e:                         # TOOL_QUERY_EXPIRED: terlambat atau panggilan sudah berakhir
        print("Jawaban tidak diterima:", e, flush=True)


app = (Application.builder().token(os.environ["BOT_TOKEN"])
       .base_url(f"{SERVER}/bot").base_file_url(f"{SERVER}/file/bot")
       .concurrent_updates(True).build())
app.add_handler(TypeHandler(Update, on_update))
app.run_polling(allowed_updates=["message", "voice_call", "voice_call_tool_query"], poll_interval=0, timeout=10)

Karena aiogram melewati jenis update yang tidak dikenalnya, Anda harus menangkap voice_call_tool_query dari update.model_extra lebih dulu di middleware luar dispatcher (dp.update.outer_middleware). Repositori Pabal memiliki contohnya (scripts/bots/voice_tools_aiogram.py); mintalah kepada operator server jika Anda membutuhkannya.

Yang didengar model dan penelepon

  • Berhasil: hasilnya menjadi respons fungsi bagi model (sebagai nilai JSON jika berupa JSON). Teks di dalam hasil dibaca hanya sebagai data, tidak pernah sebagai perintah.
  • Gagal: model mendengar kalimat pendek dari Pabal dalam bahasa panggilan, lalu menjelaskan keadaannya kepada penelepon โ€” tenggat terlewat: "The tool did not answer in time." ("Tool tidak menjawab tepat waktu"); tidak tersambung: "The tool could not be reached." ("Tool tidak dapat dihubungi"); argumen seperti nomor kartu: "Pabal does not pass on card numbers or national ID numbers." ("Pabal tidak meneruskan nomor kartu atau nomor identitas kependudukan"); dan error dari bot apa adanya. Untuk argumen yang salah, model mendapat petunjuk bagian mana yang salah sehingga bisa memanggil ulang.
  • Selama menunggu, gateway mengisi keheningan: setelah 1,5 detik hening, "One moment, let me check." ("Mohon tunggu sebentar, saya periksa dulu") sekali (langsung jika tool itu akhir-akhir ini lambat menjawab), lalu setelah 5 detik "Just a moment longer, please." ("Mohon tunggu sebentar lagi") sekali. Kedua kalimat ini diucapkan bukan dalam bahasa penelepon, melainkan dalam bahasa bot, dan jika belum ada rekaman dalam bahasa itu yang lolos telaah, keduanya diucapkan dalam bahasa Inggris (saat ini yang sudah lolos telaah baru bahasa Korea dan Inggris).
  • Jika penelepon memotong pembicaraan (menyela), gateway bisa menarik kueri yang masih terbuka, dan jawaban yang datang setelahnya menghasilkan TOOL_QUERY_EXPIRED: cancelled.

Batas tool

Kueri yang terbuka bersamaan: 2 per panggilan, 8 per bot. Jumlah pemanggilan: 30 per menit per panggilan, 120 per menit per bot. Jika lebih, kueri tidak dikirim ke bot dan model langsung mendengar too_many_openยทrate_limited.

Ketentuan pemilik bot

Transkrip, kunci model sendiri, mode stream, server MCP, dan panggilan dari bot hanya bisa dinyalakan setelah menyetujui ketentuan pemilik bot (versi 4). Persetujuan dilakukan oleh manusia di layar panggilan suara @BotFather (dari layar transkrip, kunci sendiri, mode stream, server MCP, atau panggilan dari bot mana pun) โ€” tidak bisa lewat token. Meskipun Anda sudah menyetujui versi sebelumnya, persetujuan akan diminta lagi. Sebelum bagian pemilik bot dimuat di ketentuan layanan server, tombol persetujuan tidak muncul (jika dipanggil sebelum itu: 403 VOICE_OWNER_TERMS_NOT_ACCEPTED).

BotFather๋ด‡ ์ฃผ์ธ ์•ฝ๊ด€์„ ํ™•์ธํ•ด ์ฃผ์„ธ์š”. ๋Œ€ํ™” ๊ธฐ๋ก, ์ž๊ธฐ ๋ชจ๋ธ ํ‚ค, ์›์Œ ๋ชจ๋“œ, MCP ์„œ๋ฒ„, ๋ด‡์ด ๊ฑฐ๋Š” ์ „ํ™”์— ํ•„์š”ํ•ด์š”. 1. ๋Œ€ํ™” ๊ธฐ๋ก์„ ์ผœ๋ฉด, ํ†ตํ™”๊ฐ€ ๋๋‚  ๋•Œ AI๊ฐ€ ์ ์€ ๋Œ€ํ™” ๊ธ€์„ ๋‚ด๊ฐ€ ๋ฐ›์•„์š”. ๋ฐ›์€ ๊ธ€์€ ๋‚ด๊ฐ€ ์ฒ˜๋ฆฌํ•˜๊ณ  ์ง€์ผœ์š”. 2. ๋Œ€ํ™” ๊ธฐ๋ก์ด ์ผœ์ง„ ๋ด‡์— ์ „ํ™”ํ•˜๋ฉด, ํ†ตํ™”๊ฐ€ ์—ฐ๊ฒฐ๋  ๋•Œ ์•ˆ๋‚ด ์Œ์„ฑ์ด ๋Œ€ํ™” ๊ธ€์ด ๋ด‡ ์ฃผ์ธ์—๊ฒŒ ๊ฐ„๋‹ค๊ณ  ๋จผ์ € ์•Œ๋ ค์š”. 3. ๋ฐ›์€ ๋Œ€ํ™” ๊ธ€์€ ํ•„์š”ํ•œ ๋งŒํผ๋งŒ ๋‘๊ณ , ๋ฐœ์‹ ์ž๊ฐ€ ์ง€์›Œ ๋‹ฌ๋ผ๊ณ  ํ•˜๋ฉด ์ง€์›Œ์š”. ๋ชจ๋ธ ํ•™์Šต์— ์“ฐ์ง€ ์•Š์•„์š”. 4. ์ž๊ธฐ ๋ชจ๋ธ ํ‚ค๋ฅผ ์“ฐ๋ฉด, ํ†ตํ™” ์†Œ๋ฆฌ์™€ ๊ธ€์ด ๋‚ด ํ‚ค์˜ ์ œ๊ณต์ž์—๊ฒŒ ๋‚ด ๊ณ„์•ฝ์œผ๋กœ ๊ฐ€์š”. ๊ทธ ์ฒ˜๋ฆฌ์˜ ์ฑ…์ž„์€ ๋‚˜์—๊ฒŒ ์žˆ๊ณ , ๋‚ด ๊ฐœ์ธ์ •๋ณด ์•ˆ๋‚ด์— ๊ทธ๋ ‡๊ฒŒ ์ ์–ด์š”. 5. ์›์Œ ๋ชจ๋“œ์—์„œ๋Š” ์ „ํ™” ์†Œ๋ฆฌ๊ฐ€ ํŒŒ๋ฐœ์˜ AI ๋ชจ๋ธ์ด ์•„๋‹ˆ๋ผ ๋‚ด ์„œ๋ฒ„๋กœ ๊ฐ€์š”. ๊ทธ ์†Œ๋ฆฌ๋ฅผ ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฑด ๋‚˜์˜ˆ์š”. ์ „ํ™”๋ฅผ ๊ฑด ์‚ฌ๋žŒ์˜ ์†Œ๋ฆฌ์™€ ๋งํ•œ ๋‚ด์šฉ์„ ๋ฒ•์ด ํ—ˆ๋ฝํ•˜๋Š” ๋ฒ”์œ„์—์„œ๋งŒ ์“ฐ๊ณ , ํ•„์š” ์—†์œผ๋ฉด ์ง€์›Œ์š”. 6. ๋‚ด ์„œ๋ฒ„๊ฐ€ ์–ด๋””์— ์žˆ๋Š”์ง€, ๋ฌด์—‡์„ ํ•˜๋Š”์ง€ ์ „ํ™”๋ฅผ ๊ฑด ์‚ฌ๋žŒ์ด ๋ฌผ์œผ๋ฉด ์•Œ๋ ค์š”. ๋ด‡ ์†Œ๊ฐœ์— ์ ์–ด ๋‘๋ฉด ์ข‹์•„์š”. 7. ๋ด‡์€ ์–ธ์ œ๋‚˜ AI ๋ผ๊ณ  ๋ฐํ˜€์š”. ๋Œ€๋‹ต์€ AI ๊ฐ€ ๋งŒ๋“ค์–ด์š”. ์‚ฌ๋žŒ์ด ์ง์ ‘ ๋Œ€๋‹ตํ•˜๊ฑฐ๋‚˜, ์‚ฌ๋žŒ์˜ ๋…น์Œ์„ AI ๋Œ€๋‹ต์ฒ˜๋Ÿผ ํ‹€์ง€ ์•Š์•„์š”. ์›์Œ ๋ชจ๋“œ์—์„œ๋Š” ํŒŒ๋ฐœ์ด ํ†ตํ™” ๋งจ ์•ž์— "AI ์Œ์„ฑ ๋ด‡์ž…๋‹ˆ๋‹ค. ํ†ตํ™” ์†Œ๋ฆฌ๋Š” ๋ด‡ ๊ฐœ๋ฐœ์ž์˜ ์„œ๋ฒ„๋กœ ๊ฐ‘๋‹ˆ๋‹ค." ์•ˆ๋‚ด๋ฅผ ํ‹€์–ด์š”. 8. ์นด๋“œ ๋ฒˆํ˜ธยท์ฃผ๋ฏผ๋“ฑ๋ก๋ฒˆํ˜ธยท๋น„๋ฐ€๋ฒˆํ˜ธยท์ธ์ฆ ๋ฒˆํ˜ธ๋ฅผ ๋ฌป์ง€ ์•Š์•„์š”. ์Œ์„ฑ ๋ด‡ ๊ทœ์น™๋„ ๊ทธ๋Œ€๋กœ ์ง€์ผœ์š”. 9. ํŒŒ๋ฐœ์€ ๋Œ€ํ™” ๊ธ€์„ ์ €์žฅํ•˜์ง€ ์•Š์•„์š”. ์ „ํ•ด์ง€๋ฉด, ๋Šฆ์–ด๋„ 24์‹œ๊ฐ„ ๋’ค์—๋Š” ์„œ๋ฒ„์—์„œ ์ง€์›Œ์ ธ์š”. ํ‚ค, ์›์Œ ๋ชจ๋“œ์™€ MCP ์„œ๋ฒ„์˜ ๋น„๋ฐ€์€ ์•”ํ˜ธํ™”ํ•ด ๋‘๊ณ  ์–ด๋””์—๋„ ๊ธฐ๋กํ•˜์ง€ ์•Š์•„์š”. 10. MCP ์„œ๋ฒ„๋ฅผ ๋“ฑ๋กํ•˜๋ฉด, ํ†ตํ™” ์ค‘ ๋ชจ๋ธ์ด ๊ทธ ์„œ๋ฒ„์˜ ๋„๊ตฌ๋ฅผ ๋ถ€๋ฅผ ๋•Œ ๋ฐœ์‹ ์ž๊ฐ€ ๋งํ•œ ๊ฐ’์ด ๊ทธ ์„œ๋ฒ„๋กœ ๊ฐ€์š”. ๊ทธ ์„œ๋ฒ„์™€ ๊ทธ ์„œ๋ฒ„๊ฐ€ ๋„˜๊ธฐ๋Š” ๊ณณ์˜ ์ฒ˜๋ฆฌ ์ฑ…์ž„์€ ๋‚˜์—๊ฒŒ ์žˆ๊ณ , ๋ฐ›๋Š” ๊ณณ์€ ์‚ฌ์‹ค๋Œ€๋กœ ์‹ ๊ณ ํ•ด์š”. 11. ์•ฝ๊ด€์„ ์–ด๊ธฐ๋ฉด ์šด์˜์ž๊ฐ€ ์Œ์„ฑ ํ†ตํ™”๋‚˜ ๋ด‡์„ ๋ฉˆ์ถœ ์ˆ˜ ์žˆ์–ด์š”. ๋ด‡์ด ๊ฑฐ๋Š” ์ „ํ™”๋ฅผ ์ผœ๋ ค๋ฉด ์•„๋ž˜๋„ ์ง€์ผœ์š”. 12. ๋ด‡์ด ๊ฑฐ๋Š” ์ „ํ™”๋Š” ์„ ์–ธํ•˜๊ณ  ์šด์˜์ž๊ฐ€ ํ—ˆ๋ฝํ•œ ๋ชฉ์ (์•Œ๋ฆผยท๊นจ์šฐ๊ธฐยท๋ฐฐ๋‹ฌยท์˜ˆ์•ฝ)์—๋งŒ ์จ์š”. ๊ด‘๊ณ ยทํŒ๋งค ๊ถŒ์œ ยทํ™๋ณด๋ฅผ ํ•˜์ง€ ์•Š์•„์š”. ๋ฐฐ๋‹ฌยท์˜ˆ์•ฝ ์•ˆ๋‚ด ์•ˆ์—์„œ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์˜ˆ์š”. ๊ด‘๊ณ ์„ฑ ์ •๋ณด๋ฅผ ๋ณด๋‚ด๋ฉด ์ •๋ณดํ†ต์‹ ๋ง๋ฒ• ์ œ50์กฐ๋ฅผ ์–ด๊ธด ๊ฒƒ์ด๊ณ  ๊ทธ ์ฑ…์ž„์€ ๋‚˜์—๊ฒŒ ์žˆ์–ด์š”. ํŒŒ๋ฐœ์€ ๊ทธ๋Ÿฐ ๋ฐœ์‹ ์„ ๊ณง๋ฐ”๋กœ ๋ฉˆ์ถ”๊ณ  ๋ฐœ์‹ ์„ ํ•ด์ง€ํ•ด์š”. ์ง„๋ฃŒยท์ฑ„์šฉยท๋Œ€์ถœ์ฒ˜๋Ÿผ ์‚ฌ๋žŒ์˜ ๊ถŒ๋ฆฌ๋‚˜ ์˜๋ฌด์— ๊ด€ํ•œ ํŒ๋‹จ์„ ์ด ์ „ํ™”๋กœ ์•Œ๋ฆฌ์ง€ ์•Š์•„์š”. 13. ์ „ํ™”๋ฅผ ๊ฑด ๋ด‡์€ ์ฒซ ๋ง์—์„œ ๋ด‡ ์ด๋ฆ„๊ณผ AI ๋ผ๋Š” ๊ฒƒ์„ ๋ฐํžˆ๊ณ , ์‚ฌ๋žŒยท๊ธฐ๊ด€ยทํŒŒ๋ฐœ ์ง์›์„ ์‚ฌ์นญํ•˜์ง€ ์•Š์•„์š”. 14. context ์— ๊ฑด๊ฐ•ยท๊ธˆ์œต(๊ธˆ์•กยท๊ณ„์ขŒ)ยท์ฃผ์†Œ ๊ฐ™์€ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ ๋„ฃ์ง€ ์•Š๊ณ , ๋น„๋ฐ€๋ฒˆํ˜ธยท์ธ์ฆ ๋ฒˆํ˜ธยท์นด๋“œ ๋ฒˆํ˜ธ๋ฅผ ๋ฌป๊ฑฐ๋‚˜ ๊ถŒํ•˜์ง€ ์•Š์•„์š”. 15. ๋งŒ 19์„ธ ๋ฏธ๋งŒ์ธ ์‚ฌ๋žŒ์—๊ฒŒ ์ „ํ™”ํ•˜๋ ค ํ•˜์ง€ ์•Š์•„์š”. 16. ์กฐ์šฉํ•œ ์‹œ๊ฐ„ยท์‰ฌ๋Š” ์ค‘ยทํ•˜๋ฃจ ํ•œ๋„๋ฅผ ํ”ผํ•˜๋ ค ํ•˜์ง€ ์•Š์•„์š”(์—ฌ๋Ÿฌ ๋ด‡ยท๊ณ„์ •์œผ๋กœ ๋‚˜๋ˆ  ๊ฑธ๊ธฐ ํฌํ•จ). ์‚ฌ๋žŒ์ด ๊ทธ๋งŒ ๋ฐ›๊ฒ ๋‹ค๊ณ  ํ•˜๋ฉด ๋‹ค์‹œ ๊ฑธ์ง€ ์•Š์•„์š”. 17. ๋‚ด ๊ฐœ์ธ์ •๋ณด ์ฒ˜๋ฆฌ๋ฐฉ์นจ์„ ๋‘๊ณ , ํ†ตํ™” ๋‚ด์šฉ์ด AI ์ œ๊ณต์ž์—๊ฒŒ ๊ฐ„๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๋ ค์š”. ๋ด‡์ด ๊ฑฐ๋Š” ์ „ํ™”๋Š” ์ข…๋‹จ ๊ฐ„ ์•”ํ˜ธํ™”๊ฐ€ ์•„๋‹ˆ์—์š”. 18. ์‚ฌ๋žŒ์—๊ฒŒ ๋ณด์ด๋Š” ํ—ˆ๋ฝ ์ฐฝ์— ๋ด‡ ์ฃผ์ธ์ธ ๋‚ด ์ด๋ฆ„์ด ๋‚˜์™€์š”. 19. ์–ด๊ธฐ๋ฉด ๊ทธ ๋ด‡์˜ ๋ฐœ์‹ ์ด ๋ฉˆ์ถ”๊ณ , ์Œ์„ฑ ๊ธฐ๋Šฅ ์ „์ฒด๊ฐ€ ๋ฉˆ์ถœ ์ˆ˜ ์žˆ์–ด์š”. 20. ๋ฐœ์‹ ์œผ๋กœ ๋ฐ›๋Š” ์‚ฌ๋žŒ ์ •๋ณด(ํŒŒ๋ฐœ idยทํ—ˆ๋ฝํ–ˆ๋Š”์ง€ยท๋ฐ›์•˜๋Š”์ง€ยทํ†ตํ™” ์‹œ๊ฐ๊ณผ ๊ธธ์ดยท๋ˆ„๊ฐ€ ๋Š์—ˆ๋Š”์ง€)๋Š” ์„ ์–ธํ•œ ๋ชฉ์ ์—๋งŒ ์“ฐ๊ณ , ๋‚จ์—๊ฒŒ ์ฃผ์ง€ ์•Š๊ณ , ํ—ˆ๋ฝ์ด ๊บผ์ง€๊ฑฐ๋‚˜ ๊ทธ ์‚ฌ๋žŒ์ด ์ง€์›Œ ๋‹ฌ๋ผ๊ณ  ํ•˜๋ฉด ์ง€์›Œ์š”. ๋ฐ›์€ ์‹œ๊ฐ์œผ๋กœ ์‚ฌ๋žŒ์˜ ์ผ๊ณผ๋‚˜ ์žˆ๋Š” ๊ณณ์„ ์ง์ž‘ํ•˜์ง€ ์•Š์•„์š”. 21. ํ—ˆ๋ฝ์„ ์„œ๋น„์Šค์˜ ์กฐ๊ฑด์œผ๋กœ ๊ฑธ๊ฑฐ๋‚˜ ์†์—ฌ์„œ ๋ฐ›์ง€ ์•Š์•„์š”(ํ—ˆ๋ฝ ๋งํฌ ๋ฉ”์‹œ์ง€ ํฌํ•จ). 22. ์ „ํ™”๋งˆ๋‹ค ์ ๋Š” ๋ชฉ์ ์€ ๊ทธ ์ „ํ™”์˜ ์‹ค์ œ ๋ชฉ์ ์ด์—์š”. ๊นจ์šฐ๊ธฐ ์ฐฝ์„ ๋‹ค๋ฅธ ์šฉ๋„๋กœ ์“ฐ์ง€ ์•Š์•„์š”(์„œ๋ฒ„๋Š” ๋ชฉ์ ์„ ๋ด‡์ด ์ ์€ ๋Œ€๋กœ ๋ฏฟ์–ด์š”). 23. ๋ด‡ ํ† ํฐ์„ ์ง€ํ‚ค๊ณ , ์ƒŒ ๊ฒƒ์„ ์•Œ๋ฉด ๊ณง๋ฐ”๋กœ @BotFather ์—์„œ ์žฌ๋ฐœ๊ธ‰ํ•ด์š”. ํ† ํฐ์ด ๊ณง ์‚ฌ๋žŒ์—๊ฒŒ ์ „ํ™”ํ•  ๊ถŒํ•œ์ด์—์š”. 24. ๋ด‡์ด ๊ฑด ์ „ํ™”์—๋Š” ๋Œ€ํ™” ๊ธฐ๋ก(1ํ•ญ)์ด ์˜ค์ง€ ์•Š์•„์š”. ๋ด‡์—๊ฒŒ ๊ฐ€๋Š” ๊ฒƒ์€ 20ํ•ญ์˜ ๊ฒƒ์ด์—์š”.
์•ฝ๊ด€์— ๋™์˜ํ•˜๊ธฐ

BotFather menampilkan ketentuan ini dalam bahasa Korea; tombol di bawahnya adalah Setujui ketentuan (์•ฝ๊ด€์— ๋™์˜ํ•˜๊ธฐ). Intinya, pemilik bot berjanji:

  1. Jika transkrip dinyalakan, Anda menerima teks percakapan yang ditulis AI saat panggilan berakhir; Anda yang memproses dan menjaganya.
  2. Saat seseorang menelepon bot yang transkripnya menyala, pengumuman suara lebih dulu memberi tahu, saat tersambung, bahwa teks percakapan dikirim ke pemilik bot.
  3. Anda menyimpan teks yang diterima hanya selama diperlukan, menghapusnya jika penelepon meminta, dan tidak memakainya untuk melatih model.
  4. Jika memakai kunci model sendiri, audio dan teks panggilan dikirim ke penyedia kunci Anda di bawah kontrak Anda; pemrosesan itu menjadi tanggung jawab Anda, dan pemberitahuan privasi Anda harus menyebutkannya.
  5. Dalam mode stream, audio panggilan dikirim ke server Anda, bukan ke model AI Pabal, dan Andalah yang menerima serta memprosesnya; suara dan ucapan penelepon hanya Anda pakai sejauh diizinkan hukum, dan Anda hapus jika tidak lagi diperlukan.
  6. Jika penelepon bertanya, Anda memberi tahu di mana server Anda berada dan apa yang dilakukannya (sebaiknya ditulis di deskripsi bot).
  7. Bot selalu mengaku sebagai AI dan jawabannya dibuat oleh AI: tidak ada manusia yang menjawab langsung, dan tidak ada rekaman suara manusia yang diputar seolah-olah jawaban AI. Dalam mode stream, Pabal memutar pengumuman "This is an AI voice bot. The call's audio goes to the bot developer's server." di awal panggilan.
  8. Anda tidak menanyakan nomor kartu, nomor identitas kependudukan, kata sandi, atau kode verifikasi, dan tetap mematuhi aturan bot suara.
  9. Pabal tidak menyimpan teks percakapan: setelah dikirim, teks dihapus dari server paling lambat 24 jam kemudian; kunci serta rahasia mode stream dan server MCP disimpan terenkripsi dan tidak dicatat di mana pun.
  10. Jika Anda mendaftarkan server MCP, nilai yang diucapkan penelepon dikirim ke server itu saat model memanggil tool-nya selama panggilan; Anda bertanggung jawab atas pemrosesan oleh server itu dan pihak yang menerima data darinya, dan Anda melaporkan penerimanya dengan jujur.
  11. Jika ketentuan ini dilanggar, operator bisa menghentikan panggilan suara atau botnya.

Butir 12โ€“24 adalah yang Anda patuhi saat menyalakan panggilan dari bot: memakainya hanya untuk tujuan yang dinyatakan dan diizinkan operator, tanpa iklan; menyebut nama bot dan bahwa ia AI pada kata pertamanya; tidak memasukkan informasi sensitif ke context; tidak pernah mencoba menelepon orang di bawah 19 tahun; tidak menghindari jam tenang, jeda, atau batas; memakai yang Anda ketahui tentang orang yang ditelepon hanya untuk tujuan itu dan menghapusnya saat izin dimatikan; menjaga token. Panggilan yang dibuat bot tidak membawa transkrip (butir 24). Teks ketentuan bisa berubah setelah tinjauan hukum.

Server MCP

Server MCP mati secara bawaan, dan juga mati di pabal.me

Fitur ini baru menyala jika operator memasang komponen MCP (pabal-mcp, penanda tangan, proxy keluar) serta memuat bagian bot-calls-mcp di kebijakan privasi dan bagian pemilik bot di ketentuan layanan (menyediakan alamat keluar khusus untuk permintaan MCP adalah syarat operasional, yang tidak diperiksa oleh kode). Bahkan setelah menyala, selama aplikasi belum menampilkan penerima MCP bot di layar konfirmasi, server bisa didaftarkan tetapi tidak ada panggilan yang membawa tool MCP (mcp_unavailable_reason: "sheet"). Di server yang MCP-nya mati, setVoiceCallMcpServer menghasilkan 403 VOICE_NOT_AVAILABLE: MCP tools are not available on this server, dan @BotFather tidak punya tombol Server MCP (MCP ์„œ๋ฒ„).

Jika Anda mendaftarkan server MCP yang Anda jalankan (atau yang Anda pakai berdasarkan kontrak), model bisa memanggil tool server itu selama panggilan. Yang memanggil bukan kode bot, melainkan Pabal โ€” klien MCP Pabal mengirim permintaan ke server itu lewat proxy keluar Pabal sambil menegakkan tenggat, ukuran, dan pemeriksaan. Karena itu nilai yang diucapkan penelepon dikirim ke server tersebut (butir 10 ketentuan pemilik bot).

Yang harus dimiliki server MCP

  • Streamable HTTP, POST (versi protokol 2024-11-05 atau lebih baru). https:// dengan sertifikat publik, tanpa pengalihan, dan hanya port yang diizinkan server (443 di pabal.me).
  • Server yang memakai kredensial harus menolak dengan jujur (401) permintaan tanpa kredensial dan tidak menerima kredensial yang salah. Satu kredensial hanya boleh dipakai satu bot.
  • Dokumen operator: operator server menaruh dokumen JSON (maksimal 32 KB, tanpa pengalihan) di https://<host>/.well-known/pabal-voice-mcp โ€” pihak yang menjalankan, negara, kontak, tujuan, jenis dan lokasi data, penerima dan subpemroses, serta masa simpan. Inilah informasi penerima yang tampil di layar konfirmasi penelepon; jika tidak ada, pemilik bot melaporkannya saat mendaftar (lihat di bawah). Server publik yang bisa dipanggil siapa saja tanpa kredensial (auth.type: "none") harus menulis "accepts": "any" di entri endpoint dokumen ini untuk menyatakan bahwa ia menerima panggilan dari Pabal.
  • Yang ditambahkan Pabal: _meta pada tools/call berisi sisa tenggat me.pabal/budget_ms (milidetik) dan batas ukuran hasil me.pabal/result_budget_bytes, dan bisa berisi asersi bertanda tangan Pabal (me.pabal/assertion, JWT). Penerbit asersi, sub (bot:<id bot>), dan kunci verifikasinya (JWKS) diberikan oleh getVoiceCallMcpBinding.
  • tools/list paling besar 2 MB dan 20 halaman. Tidak boleh ada langkah yang memerlukan manusia (misalnya login di peramban).
  • Setiap tool memberi klasifikasinya lewat _meta["me.pabal/voice"] โ€” data (noneยทcallerยทthird_party), content (systemยทthird_party_text), effect (noneยทwriteยทnotifyยทfinancial), max_ms (paling besar 15.000), max_result_bytes (paling besar 65.536), dan opsional p95_ms. Jika server tidak memberikannya, pemilik bot menambahkannya dengan tools saat mendaftar.
  • Spesifikasi 1.0 hanya memberi L1: hanya tool dengan data: "none" dan effect: "none" (tool yang hanya membaca informasi publik) yang disematkan pada panggilan. Tool yang menangani data penelepon (L2) atau menjalankan sesuatu (L3) hanya dicatat, tidak dipakai.
  • Saat pendaftaran, Pabal menjalankan uji kesesuaian (alamat, TLS, versi, 401, daftar tool, struktur tool, cakupan kredensial, langkah manusia). Jika gagal, pendaftaran ditolak dengan MCP_CONFORMANCE_FAILED: CT-xx.

Mendaftarkan server

Rahasia hanya dikirim di badan permintaan (jika di query string: 400). Agar rahasia tidak tertinggal di baris perintah, simpan di berkas lalu kirim berkas itu.

umask 077
cat > mcp-shop.json <<'EOF'
{"label": "shop",
 "url": "https://mcp.example.com/mcp",
 "auth": {"type": "bearer", "token": "(token dari server MCP)"}}
EOF
curl -s "https://pabal.me/bot$BOT_TOKEN/setVoiceCallMcpServer" -H 'Content-Type: application/json' -d @mcp-shop.json
  • label: [a-z][a-z0-9]{0,11}, tidak boleh diawali pabal. 2 server per bot. Nama tool yang dilihat model adalah label__namatool.
  • auth.type: none, bearer (token), headers (hingga 3 header), client_credentials (token_url, client_id, client_secret, atau private_key_jwt: true โ€” sebelumnya jalankan createVoiceCallMcpClientKey). authorization_code belum tersedia. none (server publik) baru bisa didaftarkan jika dokumen publik operator server itu menyatakan bahwa ia menerima panggilan dari Pabal (jika tidak, MCP_OPT_IN_REQUIRED).
  • Perubahan yang mengganti penerima (server baru, alamat atau host token yang lain, laporan baru, tingkat yang lebih tinggi) dijawab dengan 400 MCP_CHANGE_NEEDS_CONFIRMATION, dan baru berlaku jika pemilik bot menekan tombol Konfirmasi (ํ™•์ธ โ€” ์ด ๋ณ€๊ฒฝ์„ ์ ์šฉ) yang muncul di @BotFather dalam 5 menit. Tujuannya agar, meskipun token bocor, ucapan penelepon tidak diam-diam dikirim ke tempat lain. Rotasi yang hanya mengganti rahasia langsung berlaku.
  • Jika operator server tidak menaruh dokumen publik, pemilik bot melaporkan penerimanya dengan jujur lewat declaration (pihak yang menjalankan, negara, kontak, tujuan, jenis data, lokasi, subpemroses, masa simpan). Penelepon melihatnya di layar konfirmasi, dan jika berubah, penelepon ditanya lagi.
  • Pendaftaran dan pemeriksaan: 10 kali per jam per bot, 20 kali per pemilik. Metode lain: refreshVoiceCallMcpServer (periksa ulang), deleteVoiceCallMcpServer (selalu bisa), getVoiceCallMcpBinding (penerbit, sub, dan JWKS yang dipakai server Anda untuk mengenali panggilan dari Pabal), createVoiceCallMcpClientKey.
  • Status server dan tool yang terdaftar muncul di getVoiceCallSettings.mcp_servers (tanpa path, query, atau rahasia). Naskah lengkap spesifikasinya (Pabal Voice MCP Profile 1.0) ada di repositori Pabal, di docs/app/voice-bot-mcp-profile.html; mintalah kepada operator server jika Anda membutuhkannya.

Kunci model sendiri

Kunci sendiri belum bisa dipakai di server produksi mana pun

Panggilan yang diterima dengan kunci sendiri mengharuskan penelepon melihat layar konfirmasi (versi 2) yang menyatakan bahwa audionya dikirim ke penyedia kunci itu, dan belum ada aplikasi yang menampilkan layar itu. Karena itu, server yang bukan server uji bahkan tidak menerima kunci sampai operator menyalakannya dengan menyatakan aplikasinya sudah siap (403 VOICE_OWN_KEY_NOT_AVAILABLE). Server juga harus punya kunci induk.

Alihkan panggilan bot ke akun model milik pemilik bot sendiri. Biayanya dibebankan ke akun penyedia milik pemilik, dan panggilan berkunci sendiri tidak memakai batas harian platform untuk bot, pemilik, dan server (batas penelepon tetap berlaku). Ketentuan pemilik bot harus disetujui lebih dulu.

curl -s "https://pabal.me/bot$BOT_TOKEN/setVoiceCallApiKey" -d provider=google \
  --data-urlencode "api_key@vertex-sa.json"
  • provider: google untuk kunci JSON akun layanan Vertex AI (hanya kolom yang diperlukan yang disimpan), openai untuk kunci API yang diawali sk-. Nilai lain: VOICE_PROVIDER_INVALID. Kunci hanya dikirim di badan permintaan (jika di query: 400) โ€” jika dibaca dari berkas seperti di atas, kunci tidak tertinggal di baris perintah. Jangan menempelkannya di obrolan dengan @BotFather.
  • Sebelum menerima kunci, server memastikan kunci benar-benar bisa dibuka lewat sesi uji gateway (paling lama 12 detik). Jika lolos, kunci disimpan dan hasilnya True. Jika penyedia menolak: API_KEY_INVALID; jika login berhasil tetapi tidak punya izin API real-time: API_KEY_NO_REALTIME_ACCESS; jika tidak ada gateway untuk menguji atau gateway sibuk: 503 API_KEY_CHECK_UNAVAILABLE: โ€ฆ: the key was not kept.
  • Pengujian: 5 kali per jam per bot, 10 kali per pemilik (semua bot digabung). Hanya pengujian yang sampai ke gateway yang dihitung.
  • Kunci tidak pernah ditampilkan di mana pun โ€” di jawaban, error, log, maupun layar. getVoiceCallSettings.api_key hanya berisi penyedia, tanggal diatur, sidik jari (8 karakter), dan apakah bisa dipakai.
  • Jika kunci yang diatur tidak bisa dipakai (ketentuan berubah, server tidak bisa membuka kunci, dan sebagainya), bot tidak menerima telepon โ€” bot tidak diam-diam beralih ke kunci platform. Jika Anda menghapus kunci dengan deleteVoiceCallApiKey (selalu bisa), panggilan diterima dengan kunci platform, dan panggilan yang sedang berlangsung dengan kunci Anda berakhir.
  • Panggilan berkunci sendiri tidak punya batas harian Pabal โ€” yang berlaku hanya durasi satu panggilan, panggilan bersamaan, dan batas penelepon. Biayanya masuk ke akun Anda, jadi pasang peringatan anggaran di konsol penyedia.
  • Kunci OpenAI baru dipakai jika operator gateway menyalakannya secara terpisah (bawaan gateway hanya kunci Google). Jika tidak ada gateway yang menyalakannya, pengujian kunci OpenAI menghasilkan 503 โ€ฆ no_gateway. Jalur kunci OpenAI belum pernah diuji dengan OpenAI yang sebenarnya (hanya dengan server tiruan), dan panggilan yang diterima dengan kunci OpenAI memakai suara OpenAI yang ditetapkan gateway (bawaan marin), apa pun pengaturan suara bot. Kunci Google dipakai di wilayah Vertex milik gateway (us-central1).
  • Dalam mode stream, kunci hanya disimpan dan tidak dipakai.

Transkrip

Transkrip hanya datang di server yang operatornya mengizinkan

Operator harus mengizinkannya di halaman admin setelah menyelesaikan telaah hukum (bawaannya mati). Jika Anda menyalakan sakelarnya di server yang tidak mengizinkannya, hasilnya transcript_available: false dan transcript_unavailable_reason: "not_allowed", dan teks percakapan tidak datang.

Jika dinyalakan, pemilik bot menerima teks percakapan panggilan yang ditulis AI. Setujui ketentuan pemilik bot, lalu nyalakan sakelarnya.

Transkrip dan Simpan percakapan itu berbeda

Transkrip (bagian ini) dinyalakan dan diterima oleh pemilik bot, dan penelepon mendengar pengumuman suara saat tersambung. Simpan percakapan dipilih oleh orang yang menelepon lewat kotak centang di layar konfirmasi, dan setelah panggilan selesai teksnya tertinggal hanya di obrolan orang itu sendiri โ€” tidak dikirim ke bot maupun pemilik bot, dan tidak ada pengumuman suara. Satu panggilan bisa memiliki keduanya. Simpan percakapan tidak punya sakelar yang dinyalakan pemilik bot; fitur ini menyala jika kebijakan privasi server memuat bagian bot-calls-keep-text (sedang disiapkan โ€” Menggunakan aplikasi โ€” bot suara).

curl -s "https://pabal.me/bot$BOT_TOKEN/setVoiceCallSettings" -d transcript=true
  • Panggilan yang dibuat bot tidak punya transkrip (panggilan dari bot) โ€” meskipun sakelarnya menyala.
  • Saat seseorang menelepon bot yang transkripnya menyala, penelepon mendengar tepat setelah pengumuman AI: "What is said on this call is written down and sent to the bot's owner." ("Isi percakapan dalam panggilan ini dicatat dan dikirim ke pemilik bot"). Hanya ucapan setelah pengumuman selesai yang dicatat. Panggilan mode stream tidak punya transkrip.
  • transcript ikut di dalam voice_call panggilan yang sudah berakhir (event: "ended"). Hanya inilah versi lengkapnya, jadi masukkan voice_call ke allowed_updates. Jika ingin menerima giliran satu per satu selama panggilan, masukkan juga voice_call_transcript (hanya lewat HTTP Bot API, tidak lewat MTProto).
  • Giliran yang berisi nomor kartu atau nomor identitas kependudukan dikirim dengan semua angkanya diganti *. Nomor yang diucapkan terpisah dalam dua giliran atau dibacakan dengan kata-kata tidak terdeteksi, jadi tangani teks yang diterima dengan hati-hati juga.
  • 4.096 byte per giliran, 256 KiB dan 4.000 giliran per panggilan. Jika lebih, sisanya dibuang dan truncated: true.
  • Pabal tidak menyimpan teks percakapan: teks ada di memori selama panggilan, diserahkan sekali ke bot, dan yang tidak diambil dihapus setelah 24 jam. Teks hilang jika server dimulai ulang. Penyimpanan dan pemrosesan setelah diterima menjadi tanggung jawab pemilik bot.
"transcript": {
  "turns": [
    {"role": "bot", "text": "Bantu Pesan, selamat siang. Ada yang bisa kami bantu?", "offset_ms": 5200},
    {"role": "caller", "text": "Pesanan A-1042 saya sudah sampai mana?", "offset_ms": 9800}
  ],
  "truncated": false
}
{"update_id": 21, "voice_call_transcript": {"voice_call_id": "4611686018427387905", "seq": 2,
 "role": "caller", "text": "Pesanan A-1042 saya sudah sampai mana?", "is_final": true, "offset_ms": 9800, "date": 1790380813}}

role adalah caller (penelepon) atau bot (model), dan offset_ms adalah milidetik sejak audio panggilan dimulai.

Mode stream

Mode stream belum bisa dipakai

Agar bot mode stream bisa ditelepon, aplikasi penelepon harus menampilkan layar konfirmasi mode stream yang menyatakan "audio panggilan dikirim ke server pengembang bot", dan belum ada build aplikasi yang memuatnya. Server juga harus punya kunci induk, bagian bot-calls-stream di kebijakan privasi, bagian pemilik bot, dan gateway yang menerima mode stream. Jika tidak, mode=stream menghasilkan 403 VOICE_STREAM_NOT_AVAILABLE.

Dalam mode stream, audio panggilan tidak dikirim ke model AI Pabal, melainkan ke server milik pemilik bot. Server Anda mendengarkan audio mentahnya dan mengirim balik jawaban dalam bentuk audio โ€” AI apa yang dipakai, Anda yang menentukan (model real-time penyedia lain, gabungan pengenalan dan sintesis suara milik Anda). Pabal tetap memutar pengumuman suara AI di awal, dan tetap menegakkan konfirmasi penelepon, batas, durasi panggilan, serta batas panggilan bersamaan. Instruksi tidak diperlukan, sedangkan tool, kunci sendiri, dan transkrip hanya disimpan dan tidak dipakai (stream_ignores).

Penelepon Pabal ยท gateway Server wss pemilik โ‘  Konfirmasi stream ยท telepon โ‘ก Upgrade + tanda tangan ยท pabal.voice.v1 โ‘ข start {call_id, bot_id, format} โ‘ฃ ready (dalam 3 dtk) Pengumuman AI: selama itu, suara penelepon tidak dikirim ke server bot โ‘ค live Audio (relay) โ‘ฅ PCM16 24 kHz ยท potongan 20 ms โ‘ฆ clear ยท mark ยท hangup โ‘ง stop {reason} โ†’ tutup 1000
Mode stream: server pemilik bot menggantikan model

Penjelasan diagram

  • Tiga lajur: kiri penelepon, tengah Pabal (server dan gateway suara), kanan server pemilik bot. Server bot tidak berbicara dengan aplikasi maupun server Pabal, tetapi hanya dengan gateway lewat WebSocket.
  • โ‘กโ€“โ‘ฃ terjadi selama telepon berdering: gateway tersambung lewat TLS ke alamat publik yang sudah diperiksanya, melakukan upgrade dengan header tanda tangan, lalu mengirim start. Telepon hanya diangkat jika server bot mengirim ready dalam 3 detik; jika tidak, penelepon mendengar nada sibuk.
  • Kotak abu-abu adalah gerbang: sebelum pengumuman suara AI ("This is an AI voice bot. The call's audio goes to the bot developer's server.") selesai, suara penelepon tidak dikirim ke server bot, dan audio yang dikirim bot lebih dulu tidak diputar. Begitu selesai, โ‘ค live dikirim.
  • โ‘ฅ Audio di kedua arah berformat PCM16, 24 kHz, mono, dan dari sisi penelepon selalu dalam potongan 20 ms (960 byte). Panah biru adalah yang dikirim Pabal, panah abu-abu adalah yang dikirim server bot.
  • โ‘ฆ dan โ‘ง: bot bisa mengirim penyelaan (clear), pemberitahuan posisi putar (mark), dan menutup telepon (hangup); saat panggilan berakhir, Pabal mengirim stop beserta alasannya lalu menutup dengan 1000.

Pengaturan dan probe

Jalankan dulu server contoh di bawah di alamat publik (nama yang cocok dengan sertifikatnya). Probe langsung tersambung ke server itu saat Anda mengaturnya, dan kegagalan karena tidak bisa tersambung pun dihitung dalam batas 5 kali per jam.

Setelah menyetujui ketentuan pemilik bot (@BotFather โ†’ Panggilan suara (์Œ์„ฑ ํ†ตํ™”) โ†’ Mode stream (์›์Œ ๋ชจ๋“œ) โ†’ Setujui ketentuan pemilik bot (๋ด‡ ์ฃผ์ธ ์•ฝ๊ด€์— ๋™์˜)), beri tahu alamat server dan rahasianya. Rahasia berupa 32โ€“128 karakter ASCII yang dapat dicetak (tanpa spasi), dan alamat baru selalu dikirim bersama rahasia baru.

umask 077
openssl rand -hex 32 | tr -d '\n' > stream-secret.txt      # tidak boleh ada baris baru di akhir
curl -s "https://pabal.me/bot$BOT_TOKEN/setVoiceCallSettings" -d mode=stream \
  -d stream_url=wss://bot.example.com:8443/voice --data-urlencode "stream_secret@stream-secret.txt"
  1. Format alamat: hanya wss://, tanpa nama pengguna, #, atau spasi, hanya ASCII (karakter lain di-percent-encode), paling panjang 2.048 karakter. Host berupa nama yang mengandung titik atau IP publik (localhost tidak boleh), port 443 atau 1024โ€“65535. Jika tidak: 400 STREAM_URL_INVALID: โ€ฆ.
  2. Batas probe dihitung sejak permintaan: 5 kali per jam per bot, 10 kali per pemilik, dan bersamaan paling banyak 1 per bot dan 2 per pemilik. Jika lebih: 429. Probe yang tidak tersambung ke mana pun karena gateway sibuk tidak dihitung.
  3. Pemeriksaan alamat: jika nama itu mengarah ke satu saja alamat privat atau alamat yang dicadangkan, atau ke alamat Pabal sendiri: 400 STREAM_URL_INVALID: the host must resolve only to public addresses.
  4. Probe: gateway mencoba tersambung sekali ("probe": true di start). Jika ready datang, fitur menyala dan rahasia disimpan terenkripsi di bawah kunci induk server. Jika gagal: 400 STREAM_PROBE_FAILED: <kode> โ€” unreachable (nama, koneksi, waktu, atau TLS), handshake_failed (jawaban selain 101 โ€” biasanya tanda tangannya ditolak), redirect, no_subprotocol, no_ready, bad_ready, address_refused, busy, url, dan lain-lain โ€” kode lainnya diteruskan apa adanya dari gateway. Server menunggu paling lama 12 detik.
  5. Jika mode stream dinyalakan atau alamatnya diubah lewat Bot API (token), @BotFather mengirimkan pemberitahuan kepada pemilik bot bahwa alamat stream telah berganti menjadi โ€ฆ (์›์Œ ์ฃผ์†Œ๊ฐ€ โ€ฆ ๋กœ ๋ฐ”๋€Œ์—ˆ์–ด์š” (๋น„๋ฐ€ โ€ฆ)) beserta tombol Kembali ke mode terkelola (๊ด€๋ฆฌํ˜•์œผ๋กœ ๋Œ์•„๊ฐ€๊ธฐ); jika kembali ke mode terkelola, BotFather memberitahukannya beserta tombol Panggilan suara (์Œ์„ฑ ํ†ตํ™”). Tujuannya agar, jika token bocor, audio tidak diam-diam dikirim ke server lain.
  • Jika stream_secret dimasukkan ke query string, jawabannya 400 sebelum hal lainnya. Mode stream tidak bisa diatur lewat respons webhook.
  • Untuk kembali ke mode terkelola: mode=managed (alamat dan rahasia dihapus). Jika bot menyala dan instruksinya kosong, hasilnya VOICE_INSTRUCTIONS_REQUIRED, jadi kirimkan instruksinya sekaligus.
  • Di server yang hanya menerima bot yang diizinkan, bot baru menerima telepon setelah operator memeriksa alamat stream dan mengizinkannya kembali.
  • getVoiceCallSettings hanya menampilkan stream_url dan sidik jari rahasianya. Pada panggilan mode stream, sendVoiceCallContext tidak bisa dipakai (VOICE_CONTEXT_NOT_SUPPORTED), dan endVoiceCall langsung menutup tanpa salam perpisahan.

Spesifikasi

  • TLS 1.2 ke atas, dengan sertifikat publik yang cocok dengan nama di URL. Gateway tersambung ke IP yang sudah diperiksanya, tetapi SNI dan pemeriksaan sertifikat dilakukan dengan nama itu. Pengalihan (3xx) tidak diikuti.
  • Subprotokol pabal.voice.v1 wajib dipilih (Sec-WebSocket-Protocol). Tanpa kompresi. Satu pesan paling besar 65.536 byte, pesan teks paling besar 4.096 byte (UTF-8).
  • Gateway mengirim ping setiap 15 detik. Jika tidak ada pong dalam 15 detik, koneksi diputus (sebagian besar pustaka menjawabnya secara otomatis).
  • Pesan teks adalah event JSON, pesan biner adalah audio. Format audionya hanya satu: PCM16 little-endian, mono, 24.000 Hz. call_id dan bot_id adalah string desimal.
ArahPesanKapan ยท arti
Pabal โ†’ bot{"event":"start","protocol":1,"call_id":"โ€ฆ","bot_id":"โ€ฆ","probe":false,"caller":{"lang":"ko"},"format":{"encoding":"pcm_s16le","sample_rate":24000,"channels":1,"frame_ms":20},"max_seconds":300}Sekali, tepat setelah upgrade. Tidak ada nama, nomor, atau ID penelepon (hanya caller.lang โ€” informasi tentang orangnya datang lewat update voice_call)
Pabal โ†’ bot{"event":"live"}Pengumuman AI sudah selesai. Sejak saat ini audio mengalir
Pabal โ†’ botBinerSuara penelepon, selalu 960 byte (20 ms). Hanya setelah live
Pabal โ†’ bot{"event":"mark","name":"โ€ฆ"}Semua audio sebelum mark yang dikirim bot itu sudah diputar
Pabal โ†’ bot{"event":"cleared","played_ms":N,"dropped_ms":N,"marks":["โ€ฆ"]}Jawaban atas clear
Pabal โ†’ bot{"event":"buffer_full","dropped_ms":N}Audio bot menumpuk lebih dari 30 detik, dan kelebihannya dibuang
Pabal โ†’ bot{"event":"stop","reason":"โ€ฆ","played_ms":N,"duration_ms":N}Panggilan berakhir. Sebentar lagi ditutup dengan 1000
Bot โ†’ Pabal{"event":"ready"}Dalam 3 detik setelah start, paling pertama
Bot โ†’ PabalBinerAudio bot: jumlah byte genap, 2โ€“65.536 byte. Yang dikirim sebelum live dibuang
Bot โ†’ Pabal{"event":"clear"}Penyelaan: hentikan sekarang audio bot yang sedang mengantre
Bot โ†’ Pabal{"event":"mark","name":"[A-Za-z0-9_.:-]{1,64}"}"Beri tahu jika pemutaran sudah sampai di sini". Paling banyak 256 menunggu per panggilan
Bot โ†’ Pabal{"event":"hangup"}Mengakhiri panggilan. Audio yang mengantre diputar dulu paling lama 5 detik, lalu telepon ditutup
  • event yang tidak dikenal diabaikan. Teks yang bukan JSON, teks lebih dari 4 KiB, serta audio dengan jumlah byte ganjil atau lebih dari 64 KiB adalah pelanggaran spesifikasi dan mengakhiri panggilan (penelepon mendengar pengumuman permintaan maaf).
  • Batas atas pengiriman: event teks rata-rata 50 per detik (sesaat 100), audio rata-rata 4 kali kecepatan real-time (sesaat 60 detik). Jika soket bot tidak membaca dan suara penelepon menumpuk lebih dari 30 detik (1.500 potongan), panggilan juga diakhiri.

Tanda tangan

Gateway menambahkan header ini pada permintaan upgrade.

X-Pabal-Voice-Signature: t=<detik Unix>,nonce=<16 byte hex>,call=<call_id>,v1=<hex>
v1 = HMAC-SHA256(stream_secret, "pabal-voice-stream/1\n<t>\n<nonce>\n<call>\n<host>\n<path?query>")
  • host adalah host URL yang Anda berikan kepada Pabal (huruf kecil, tanpa port). Jangan menghitungnya dari header Host permintaan โ€” nilai itu ditulis oleh pengirim, sehingga tidak mengikat tanda tangan ke host mana pun. path?query adalah path dan query URL apa adanya (/ jika tidak ada). Pada probe, call adalah nomor probe.
  • Server bot wajib memeriksa: |sekarang โˆ’ t| โ‰ค 300 detik, setiap nonce hanya sekali, dan HMAC dibandingkan byte demi byte dengan fungsi waktu-konstan. Jika salah, berikan jawaban selain 101, misalnya 401.
  • Lalu periksa apakah start.call_id sama dengan call di tanda tangan. Jika berbeda, tutup dengan 1008 โ€” agar upgrade yang sudah ditandatangani tidak bisa dipakai ulang untuk panggilan lain. Server yang hanya melayani satu bot juga memeriksa apakah start.bot_id adalah bot miliknya.
  • Nilai uji: rahasia example-stream-secret-of-the-bot-developer-0001, t=1790000000, nonce 00112233445566778899aabbccddeeff, call 4611686018427387905, host bot.example.com, path /voice?bot=100042 โ†’ v1=807517ff96ac75223bde35b48c775f49a2eda2fb4832a02c0ea832b914d70d0c.

Audio ยท penyelaan ยท mark ยท mengakhiri

  • Buffer bot โ†’ penelepon: gateway mengeluarkan audio sesuai kecepatan putar, jadi Anda boleh mengirim semuanya sekaligus, tetapi jika menumpuk lebih dari 30 detik, kelebihannya dibuang dan buffer_full dikirim. Jeda 300 ms pada audio bot menandai berakhirnya satu "potongan" (satuan untuk mark dan clear).
  • clear: membuang semua audio bot yang belum keluar, dan hening dimulai dari 10 ms berikutnya (terukur kurang dari 50 ms). Di jawaban cleared, played_ms adalah total audio bot yang benar-benar diputar dalam panggilan ini sejauh ini, dropped_ms adalah jumlah yang baru dibuang, dan marks adalah nama-nama mark pada potongan yang dibuang โ€” dipakai untuk memberi tahu model "di mana pembicaraan terpotong".
  • mark: kembali dengan nama yang sama setelah audio sebelumnya diputar. Sebaiknya audio sebelum mark diakhiri pada kelipatan 480 byte (10 ms); potongan yang tersisa diisi hening.
  • Mengakhiri: bot mengirim hangup atau menutup dengan rapi menggunakan 1000 atau 1001. Penutupan lain atau putusnya TCP diperlakukan sama seperti pelanggaran spesifikasi (penelepon mendengar pengumuman permintaan maaf). Jika Pabal yang mengakhiri, stop.reason berupa kode pendek โ€” hangup (penelepon menutup), max_length, bot_ended, disconnect, drain, kill_switch, operator, link_lost, media_failed โ€ฆ Perlakukan nilai yang tidak dikenal sebagai "sudah berakhir".
  • start pada probe berisi "probe": true, caller kosong, dan max_seconds: 0. Setelah Anda mengirim ready, datang {"event":"stop","reason":"probe"} lalu koneksi ditutup. Jangan membuka sesuatu yang berbiaya, seperti sesi model, saat probe.

Contoh โ€” server stream paling kecil

Server yang memeriksa seluruh spesifikasi tanpa AI: saat live, ia memutar bunyi penanda, lalu mengembalikan suara penelepon apa adanya (gema). Server ini memeriksa tanda tangan, waktu, nonce, panggilan, dan bot. Pastikan dulu dengan server ini bahwa panggilan berjalan sampai selesai, lalu ganti bagian gema dengan AI Anda. Repositori Pabal juga berisi scripts/bots/stream_echo_bot.py (dengan demo penyelaan) dan scripts/bots/stream_openai_relay.mjs (meneruskan ke Realtime dengan kunci OpenAI Anda โ€” contoh yang belum pernah dijalankan); mintalah kepada operator server jika Anda membutuhkannya.

# stream_bot.py โ€” pip install "websockets>=13"
# Jalankan: STREAM_SECRET_FILE=stream-secret.txt TLS_CERT=fullchain.pem TLS_KEY=privkey.pem \
#           PUBLIC_HOST=bot.example.com BOT_ID=<ID bot โ€” angka sebelum : di token> python3 stream_bot.py
import asyncio
import hashlib
import hmac
import json
import math
import os
import ssl
import struct
import time

from websockets.asyncio.server import serve

SECRET = open(os.environ["STREAM_SECRET_FILE"], "rb").read().strip()
PUBLIC_HOST = os.environ["PUBLIC_HOST"].lower()   # host stream_url yang diberikan ke Pabal (bukan header Host)
BOT_ID = os.environ.get("BOT_ID", "")             # jika kosong, menerima panggilan dari bot mana pun
PATH = os.environ.get("STREAM_PATH", "/voice")
PORT = int(os.environ.get("PORT", "8443"))
CHIME = b"".join(struct.pack("<h", int(8000 * math.sin(2 * math.pi * 880 * i / 24000)))
                 for i in range(24000 * 6 // 10))  # 880 Hz selama 0,6 detik, PCM16 24 kHz
seen = {}                                         # nonce yang sudah dipakai


def signed_call(header, target):
    """Memeriksa X-Pabal-Voice-Signature dan mengembalikan call id yang ditandatangani. None jika salah."""
    try:
        parts = dict(p.split("=", 1) for p in header.split(","))
        t, nonce, call, v1 = int(parts["t"]), parts["nonce"], parts["call"], parts["v1"]
    except (KeyError, ValueError):
        return None
    now = time.time()
    if abs(now - t) > 300 or nonce in seen:
        return None
    text = f"pabal-voice-stream/1\n{t}\n{nonce}\n{call}\n{PUBLIC_HOST}\n{target}"
    expected = hmac.new(SECRET, text.encode("utf-8", "surrogateescape"), hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected.encode(), v1.encode("utf-8", "surrogateescape")):  # byte demi byte
        return None
    for old in [n for n, at in seen.items() if at < now - 600]:
        del seen[old]
    seen[nonce] = now
    return call


def check(connection, request):
    if request.path.split("?", 1)[0] != PATH:
        return connection.respond(404, "not here\n")
    call = signed_call(request.headers.get("X-Pabal-Voice-Signature", ""), request.path)
    if call is None:
        return connection.respond(401, "bad signature\n")
    connection.signed_call = call                 # start harus menyebut panggilan ini
    return None


async def handle(ws):
    if ws.subprotocol != "pabal.voice.v1":
        await ws.close(1002, "pabal.voice.v1 only")
        return
    start = json.loads(await asyncio.wait_for(ws.recv(), 5))
    if (start.get("event") != "start" or start.get("call_id") != ws.signed_call
            or (BOT_ID and start.get("bot_id") != BOT_ID)):
        await ws.close(1008, "not this call")
        return
    await ws.send(json.dumps({"event": "ready"}))    # dalam 3 detik, paling pertama
    live = False
    async for message in ws:
        if isinstance(message, bytes):
            if live:
                await ws.send(message)            # mengembalikan 20 ms suara penelepon apa adanya (gema)
            continue
        event = json.loads(message)
        if event.get("event") == "live" and not start.get("probe"):   # pengumuman AI sudah selesai
            live = True
            await ws.send(CHIME)
            await ws.send(json.dumps({"event": "mark", "name": "chime"}))
        elif event.get("event") == "stop":
            print("Selesai:", start["call_id"], event.get("reason"), flush=True)
            break


async def main():
    tls = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    tls.minimum_version = ssl.TLSVersion.TLSv1_2
    tls.load_cert_chain(os.environ["TLS_CERT"], os.environ["TLS_KEY"])
    async with serve(handle, "0.0.0.0", PORT, ssl=tls, subprotocols=["pabal.voice.v1"],
                     process_request=check, max_size=65536, compression=None):
        print(f"Mendengarkan: wss://{PUBLIC_HOST}:{PORT}{PATH}", flush=True)
        await asyncio.get_running_loop().create_future()


asyncio.run(main())

Yang dilihat penelepon

  • Tombol panggil: tombol telepon muncul di kepala obrolan dan di profil bot suara. Tombol ini hanya tampil jika server menilai bisa menerima panggilan dari orang itu di perangkat itu (sebelum peluncuran publik, hanya untuk penguji). Tidak ada panggilan video.
  • Layar konfirmasi (sebelum izin mikrofon): judul "An AI answers this call"; bot bukan manusia dan suaranya dibuat oleh AI; panggilan tidak dienkripsi ujung ke ujung; ke mana audio dikirim (Google, Gemini Live API milik Vertex AI) serta negara dan wilayahnya ("United States (Iowa, Google Cloud us-central1)"); apa yang dikirim (suara selama panggilan, teks percakapan dan jawaban AI, catatan yang dimasukkan bot selama panggilan, dan hasil fungsi bot); penyimpanan (Pabal tidak menyimpan audio dan hanya menyimpan catatan panggilan selama 180 hari; Google tidak menyimpan apa pun setelah membuat jawaban, tetapi konten yang dicurigai sebagai penyalahgunaan oleh pemeriksaan otomatisnya bisa dicatat hingga 90 hari); apa yang diterima pemilik bot (nama, nomor akun, bahasa, waktu dan durasi panggilan, nilai yang dipakai tool โ€” bukan nomor telepon dan bukan audio); usia 19 tahun ke atas. Tombolnya "I'm 19 or older ยท Call" dan Batal. Usia tidak diverifikasi identitasnya, melainkan dikonfirmasi sendiri oleh penelepon. Tombol telepon adalah tombol utama (berisi warna aksen) dan Batal adalah tombol sekunder. Di server yang menyalakan Simpan percakapan, kotak centang "Keep this call's conversation as text in my chat" (awalnya tidak dicentang) ditambahkan setelah penjelasan.
  • Seberapa sering: di desktop dan Android, saat pertama kali menelepon bot itu (dan ditanya lagi jika penerima atau versi layarnya berubah); di iPhone dan Apple Watch, setiap kali. Jam tangan selalu menampilkan layar lengkap, dan posisi awal sakelar Simpan percakapan adalah pilihan pada panggilan terakhir ke bot itu. Aplikasi mengirim persetujuan ke server pada setiap panggilan, dan server memakai satu persetujuan โ€” dari perangkat yang sama, untuk bot yang sama, dan berumur kurang dari 2 menit โ€” hanya sekali untuk panggilan itu.
  • Saat tersambung: pengumuman suara AI "You're now talking to an AI voice bot." (untuk bot mode stream: "This is an AI voice bot. The call's audio goes to the bot developer's server.") โ†’ (untuk bot bertranskrip) pengumuman transkrip โ†’ salam pembuka bot. Pemilik bot tidak bisa mematikan pengumuman ini, dan ucapan penelepon sebelum pengumuman selesai tidak dikirim ke model. Simpan percakapan tidak punya pengumuman suara โ€” penelepon sendiri yang memilihnya di layar.
  • Bahasa pengumuman: pengumuman suara tidak memakai language bot, melainkan bahasa layar konfirmasi yang dibaca penelepon. Semua pengumuman dalam satu panggilan memakai satu bahasa โ€” pengumuman AI, pengumuman transkrip, dan permintaan maaf saat sambungan terputus (untuk bot mode stream, pengumuman mode stream dan permintaan maaf). Jika salah satunya belum punya rekaman dalam bahasa itu yang lolos telaah, semuanya diucapkan dalam bahasa Inggris. Saat ini bahasa yang sudah lolos telaah baru bahasa Korea dan Inggris. Layar konfirmasi dan pengumuman dalam 8 bahasa lainnya (bahasa Jepang, Tionghoa, Prancis, Spanyol, Indonesia, Vietnam, Khmer, dan Arab) masih berupa draf yang menunggu telaah hukum dan pemeriksaan penutur asli, jadi penelepon yang memakai aplikasi dalam bahasa-bahasa itu pun untuk sementara melihat layar konfirmasi berbahasa Inggris dan mendengar pengumuman berbahasa Inggris. Rekaman bahasa Khmer dan versi bahasa Jepang dari "Just a moment longer, please." ("Mohon tunggu sebentar lagi") masih menunggu didengarkan dan dipilih oleh manusia. Sebelum pengumuman pertama dalam panggilan dan sebelum pengumuman transkrip selalu ada jeda hening minimal 300 ms โ€” agar jalur telepon tidak menelan kata pertama.
  • Layar panggilan: di bawah nama, "@{username} ยท AI ยท Bot" dan "AI-generated voice ยท Call audio is processed by Google". Tidak ada kunci emoji maupun keterangan enkripsi.
  • Jika sambungan bermasalah, penelepon mendengar "Sorry, we've lost the connection. Ending the call now." lalu telepon ditutup. Semua penolakan berakhir dengan nada sibuk.

Penjelasan untuk pengguna aplikasi ada di Menggunakan aplikasi โ€” Menelepon bot suara.

Panggilan dari bot

Mati di pabal.me โ€” menunggu tinjauan hukum

Server sudah memilikinya, tetapi tidak ada bot yang bisa menelepon siapa pun sebelum bagian bot-calls-outbound dalam kebijakan privasi dan bagian voice-bot-owners-outbound dalam ketentuan layanan lolos tinjauan hukum dan dimuat. Belum ada pula versi aplikasi dengan layar izin (Status saat ini). Berikut cara kerjanya di server tempat fitur ini menyala.

Bot suara juga bisa menelepon seseorang lebih dulu: untuk memastikan janji, membangunkan pada jam yang dipilih, atau mengabarkan pengiriman. Kode pemilik bot memintanya dengan startVoiceCall, server menilai permintaan itu lalu membunyikan perangkat orang tersebut; jika ia menjawab, pengumuman Pabal diputar lebih dulu, lalu model suara AI berbicara sesuai instruksi bot. Bagaimana panggilan berakhir datang dalam update voice_call.

  • Hanya kepada orang yang mengizinkan. Orang itu harus mengizinkan bot tersebut meneleponnya di aplikasi Pabal (Izin penerima panggilan) dan bisa mematikannya kapan saja.
  • Bukan untuk iklan. Ada empat tujuan โ€” pengingat, bangun, pengiriman, dan janji โ€” dan iklan, penawaran penjualan, serta promosi tidak diperbolehkan (ketentuan pemilik bot, butir 12โ€“24).
  • Panggilan dari bot tidak terenkripsi ujung ke ujung โ€” suara melewati server Pabal dan penyedia AI. Setelah orang itu menjawab, suara dikirim ke Google selama panggilan โ€” Gemini Live API dari Vertex AI, Iowa, Amerika Serikat (Google Cloud us-central1). Pabal tidak menyimpan suara.
  • Tidak ada teks percakapan yang dikirim ke pemilik bot. Panggilan yang dibuat bot tidak punya transkrip. Yang diterima pemilik bot adalah lima hal di Yang dilihat dan didengar penerima panggilan.

Alurnya

Kode bot Server Pabal Gateway ยท model Aplikasi orang itu โ‘  startVoiceCall Penilaian: bot ยท izin ยท batas jam tenang ยท dijeda โ‘ก 200 {voice_call_id} โ‘ข dial โ‘ฃ Dering: nama (AI) โ‘ค Dijawab โ‘ฅ Pengumuman Pabal โ‘ฆ Bot: nama ยท AI ยท tujuan Panggilan โ€” suara melewati gateway dan model AI โ‘ง voice_call: started ยท ended Tak dijawab ยท tak berdering: not_answered
Satu panggilan dari bot: permintaan โ†’ penilaian โ†’ dering โ†’ dijawab โ†’ pengumuman โ†’ kata pertama โ†’ akhir

Penjelasan diagram

  • Empat jalur: dari kiri, kode pemilik bot, server Pabal, gateway suara dengan model AI, dan aplikasi penerima panggilan. Garis titik-titik vertikal adalah waktu; garis biru utuh adalah panggilan yang berjalan, dan garis abu-abu adalah jawaban kepada bot.
  • Kotak abu-abu di โ‘  โ€” tiga kali penilaian: sebelum menjawab, sisi bot (sudah dinyalakan, tinjauan operator, ketentuan, batas) dan ada tidaknya izin; setelah 200, sisi orang itu (jam tenang, dijeda, batas harian dan jarak waktu orang itu, perangkat yang bisa dibunyikan); dan sekali lagi tepat sebelum berdering.
  • 200 di โ‘ก hanya berarti "diterima". Apakah berdering dan apakah dijawab hanya diketahui dari update di โ‘ง.
  • โ‘ฃโ€“โ‘ฆ, sisi orang itu: dering menampilkan "(AI)" setelah nama bot, dan saat dijawab, pengumuman Pabal (โ‘ฅ) diputar sebelum bot. Pada kata pertamanya (โ‘ฆ), bot menyebut namanya, bahwa ia AI, dan tujuan panggilan. Selama kotak hijau, suara melewati gateway dan model AI โ€” tidak terenkripsi ujung ke ujung.
  • Garis putus-putus merah adalah intinya: permintaan yang tidak pernah berdering karena sisi orang itu dan permintaan yang berdering tanpa dijawab berakhir sama, pada waktu yang sama, sebagai not_answered. Bot tidak bisa tahu apakah orang itu sedang tidur atau sedang dalam panggilan lain.

Menyalakan โ€” @BotFather

Panggilan dari bot dinyalakan hanya oleh pemilik bot di @BotFather, tidak pernah lewat token (mengirim kolom panggilan ke setVoiceCallSettings menghasilkan 400 VOICE_FIELD_NOT_AVAILABLE). Panggilan suara harus menyala lebih dulu. /mybots โ†’ bot โ†’ Panggilan suara (์Œ์„ฑ ํ†ตํ™”) โ†’ Panggilan dari bot (๋ด‡์ด ๊ฑฐ๋Š” ์ „ํ™”) โ†’ Nyalakan (์ผœ๊ธฐ).

์ผœ๊ธฐ
BotFather@order_helper_bot ์ด ์‚ฌ๋žŒ์—๊ฒŒ ์ „ํ™”๋ฅผ ๊ฑธ ์ˆ˜ ์žˆ๊ฒŒ ํ• ๊นŒ์š”? โ€ข ์ด ๋ด‡์—๊ฒŒ ์ „ํ™”๋ฅผ ํ—ˆ๋ฝํ•œ ์‚ฌ๋žŒ์—๊ฒŒ๋งŒ ๊ฑธ์–ด์š”. ํ—ˆ๋ฝ์€ ๊ทธ ์‚ฌ๋žŒ์ด ํŒŒ๋ฐœ ์•ฑ์—์„œ ํ•˜๊ณ , ์–ธ์ œ๋“  ๋Œ ์ˆ˜ ์žˆ์–ด์š”. โ€ข ๊ทธ ์‚ฌ๋žŒ์˜ ์กฐ์šฉํ•œ ์‹œ๊ฐ„๊ณผ ์‰ฌ๋Š” ์ค‘์—๋Š” ์šธ๋ฆฌ์ง€ ์•Š์•„์š”. โ€ข ํ•˜๋ฃจ ์ƒํ•œ(์ง€๊ธˆ 50๋ฒˆ)๊ณผ ์„œ๋ฒ„์˜ ํ•œ๋„๊ฐ€ ์žˆ์–ด์š”. โ€ข ์šด์˜์ž๊ฐ€ ์ด ๋ด‡์˜ ์ด๋ฆ„ยท์‚ฌ์ง„ยท์ง€์‹œ๋ฌธยท๋ชฉ์†Œ๋ฆฌยท๋ชฉ์ ์„ ๊ฒ€ํ† ํ•˜๊ณ  ํ—ˆ๋ฝํ•ด์•ผ ๊ฑธ๋ ค์š”. ๋ฌด์—‡์ด๋“  ๋ฐ”๋€Œ๋ฉด ๋‹ค์‹œ ๊ฒ€ํ† ํ•ด์š”. โ€ข ๋ด‡์ด ๊ฑฐ๋Š” ์ „ํ™”๋Š” ์ข…๋‹จ ๊ฐ„ ์•”ํ˜ธํ™”๊ฐ€ ์•„๋‹ˆ์—์š”. ํ†ตํ™” ์†Œ๋ฆฌ๋Š” ํŒŒ๋ฐœ ์„œ๋ฒ„์™€ AI ์ œ๊ณต์ž๋ฅผ ๊ฑฐ์ณ์š”.
์ผœ๊ธฐ
BotFather๋ด‡์ด ๊ฑฐ๋Š” ์ „ํ™”๋ฅผ ์ผฐ์–ด์š”. ์šด์˜์ž๊ฐ€ ์ด ๋ด‡์„ ๊ฒ€ํ† ํ•˜๊ณ  ํ—ˆ๋ฝํ•˜๋ฉด Bot API ์˜ startVoiceCall ๋กœ ๊ฑธ ์ˆ˜ ์žˆ์–ด์š”. ๋ด‡์ด ๊ฑฐ๋Š” ์ „ํ™” ยท @order_helper_bot ์ƒํƒœ: ์ผœ์ง ์šด์˜์ž ํ—ˆ๋ฝ: ๋Œ€๊ธฐ ๋ชฉ์ : ๋ฐฐ๋‹ฌ ํ•˜๋ฃจ ์ƒํ•œ: 50๋ฒˆ ์˜ค๋Š˜: ์š”์ฒญ 0 ยท ๋ฐ›์Œ 0 ยท ์•ˆ ๋ฐ›์Œ 0 ์ด ๋ด‡์—๊ฒŒ ์ „ํ™”๋ฅผ ํ—ˆ๋ฝํ•œ ์‚ฌ๋žŒ์—๊ฒŒ๋งŒ, ๊ทธ ์‚ฌ๋žŒ์˜ ์กฐ์šฉํ•œ ์‹œ๊ฐ„๊ณผ ์‰ฌ๋Š” ์ค‘์„ ํ”ผํ•ด ๊ฑธ์–ด์š”. ๋ด‡์ด ๊ฑฐ๋Š” ์ „ํ™”๋Š” ์ข…๋‹จ ๊ฐ„ ์•”ํ˜ธํ™”๊ฐ€ ์•„๋‹ˆ์—์š” โ€” ํ†ตํ™” ์†Œ๋ฆฌ๋Š” ํŒŒ๋ฐœ ์„œ๋ฒ„์™€ AI ์ œ๊ณต์ž๋ฅผ ๊ฑฐ์ณ์š”.

BotFather menjawab dalam bahasa Korea. Pesan pertama menanyakan apakah bot boleh menelepon orang: hanya orang yang mengizinkan, tidak pernah pada jam tenang mereka atau saat dijeda, dengan batas harian (sekarang 50) dan batas server, dan hanya setelah operator meninjau dan mengizinkan nama, foto, instruksi, suara, dan tujuan bot (dan meninjau lagi setiap kali ada yang berubah); panggilan dari bot tidak terenkripsi ujung ke ujung. Pesan kedua menampilkan layar bot setelah menyala: menyala, menunggu operator, tujuan pengiriman, batas 50, permintaan hari ini.

  • Ketentuan versi 4: jika Anda belum menyetujuinya, ketentuan pemilik bot muncul sebelum fitur menyala. Butir 12โ€“24 adalah janji untuk panggilan dari bot.
  • Tujuan: fitur hanya menyala dengan sedikitnya satu dari reminder (pengingat) ยท wake_up (bangun) ยท delivery (pengiriman) ยท appointment (janji). Setiap permintaan menyebut salah satunya; tujuan yang tidak dipilih bot menghasilkan 400. Mengubah tujuan akan meminta izin lagi kepada orang yang sudah mengizinkan, dan operator meninjau ulang.
  • Batas harian: jumlah permintaan yang boleh dikirim bot per hari โ€” 10, 20, atau 50 (bawaan 50).
  • Tinjauan operator: operator melihat nama, @nama pengguna, foto, instruksi, salam pembuka, tool, suara, bahasa, mode, tujuan, dan nama pemilik bot di halaman admin, lalu mengizinkannya. Jika salah satunya berubah, bot tidak bisa menelepon sampai diizinkan lagi (403 VOICE_OUTBOUND_OFF: review).
  • Token: mengubah pengaturan suara lewat token (kecuali mematikan suara) membekukan panggilan dari bot selama 24 jam (frozen). Jika Anda sendiri yang mengubahnya, cabut dengan Saya yang mengubahnya (๋‚ด๊ฐ€ ๋ฐ”๊ฟจ์–ด์š”, vdkยทvdK) di @BotFather. Menerbitkan ulang token juga membekukan 24 jam, mengakhiri panggilan yang sedang berjalan, dan menghapus webhook bot yang panggilannya menyala.
  • Hanya bot berbahasa Korea atau Inggris (ko-KRยทen-US) yang bisa menelepon. Bot dalam mode stream, dengan kunci sendiri, tool, atau server MCP tidak bisa (mode).

Izin penerima panggilan

Bot tidak bisa memperoleh izin sendiri. Orang itu membuka layar izin bot tersebut di aplikasi Pabal dan mengetuk "I am 19 or older ยท Allow" (Saya berusia 19 tahun ke atas ยท Izinkan). Layar itu menyebut bahwa AI yang menelepon, tujuan, nama pemilik bot, ke mana suara dikirim, dan jam tenang (Menggunakan aplikasi โ€” Mengizinkan bot menelepon Anda). Ada tiga cara membukanya.

  • Menu bot: "Allow callsโ€ฆ" (Izinkan panggilanโ€ฆ) di obrolan atau profil bot.
  • Tautan di obrolan 1:1: bot mengirim https://pabal.me/<nama pengguna bot>?callme=1 ke obrolan 1:1 dengan orang itu, sebagai tautan di teks atau tombol URL inline. Layar hanya terbuka untuk pesan yang dikirim bot itu di obrolan itu โ€” tautan yang diteruskan ke obrolan lain, pratinjau, atau web hanya membuka obrolan. Seseorang bisa membuka layar dari tautan sekali sehari (semua bot dihitung bersama), dan tidak sama sekali jika ia belum pernah menulis ke bot.
  • Daftar di pengaturan "Bots allowed to call me" (Bot yang boleh menelepon saya): melihat bot yang diizinkan, mematikan, dan mengizinkan lagi.
curl -s "https://pabal.me/bot$BOT_TOKEN/sendMessage" -d chat_id=$USER_ID \
  --data-urlencode "text=Mau kami telepon sebelum pesanan Anda tiba?" \
  --data-urlencode 'reply_markup={"inline_keyboard": [[{"text": "Izinkan panggilan", "url": "https://pabal.me/order_helper_bot?callme=1"}]]}'
  • Server yang di-host sendiri: aplikasi hanya mengenal tautan pabal.me. Untuk server lain, host-nya harus ditambahkan ke aturan tautan versi aplikasi agar tautan bisa membuka layar (menu dan daftar pengaturan tetap berfungsi).
  • Untuk saat ini: hanya akun dengan nomor +82, di perangkat yang zona waktunya Korea (Seoul) dan sudah masuk setidaknya 24 jam, yang bisa menyalakan izin (operator menentukan negaranya). Orang yang menjawab berusia di bawah 19 tahun tidak bisa melakukan panggilan dengan bot selama setahun.
  • Jika nama, pemilik, atau tujuan bot berubah, atau setahun setelah izin diberikan, izin ditanyakan lagi; sampai saat itu bot tidak bisa menelepon.

Menelepon โ€” startVoiceCall

curl -s "https://pabal.me/bot$BOT_TOKEN/startVoiceCall" -d user_id=$USER_ID -d purpose=delivery \
  --data-urlencode "context=Pesanan A-1042 tiba hari ini sekitar pukul 15.00. Tanyakan apakah ia bisa menerimanya saat itu." \
  -d max_duration=180 -d ring_timeout=30
{"ok": true, "result": {"voice_call_id": "8812345678901234"}}
ParameterArti
user_idId orang yang akan ditelepon, yang harus sudah mengizinkan bot ini
purposeSalah satu tujuan yang dipilih bot ini: reminder ยท wake_up ยท delivery ยท appointment
contextKeterangan panggilan ini, hanya untuk model: 1โ€“1.000 karakter, 3.072 byte. Diperiksa dengan aturan konten (peniruan identitas, menyembunyikan AI, meminta rahasia, iklan). Model memakainya setelah kata pertamanya (nama, AI, tujuan), ketika orang itu sudah menjawab
max_durationLama maksimum panggilan setelah dijawab (detik): 30 sampai 300 atau batas bot, mana yang lebih kecil. Bawaan 180
ring_timeoutLama berdering (detik), 30โ€“45. Bawaan 40
JawabanArtiYang dilakukan
200 {"voice_call_id": "โ€ฆ"}Diterima. Apakah berdering dan dijawab datang lewat updateโ€”
400 Bad Request: <apa>Parameter salah, tujuan yang tidak dipilih bot (purpose is not declared), bahasa bot bukan Korea atau InggrisPerbaiki
400 VOICE_CONTEXT_REFUSED: <kode>context melanggar aturan kontenPerbaiki
403 VOICE_OUTBOUND_OFF: <alasan>Sisi bot: owner_off (belum dinyalakan) ยท terms (ketentuan versi 4) ยท review (menunggu tinjauan operator) ยท suspended (penghentian otomatis) ยท frozen (dibekukan karena token) ยท owner_name (nama pemilik gagal pemeriksaan peniruan identitas) ยท mode, atau alasan sisi bot dari pengaturan suara (unavailable_reason): bot_voice_off ยท not_allowed ยท suspended ยท name_refused ยท text_refused ยท awaiting_review ยท stream_unavailable ยท own_key_unavailable ยท no_gateway ยท no_gateway_with_tools ยท no_gateway_with_language ยท no_own_key_gateway. Alasan sisi server voice_off ยท kill_switch ยท no_relay ยท outbound_off datang sebagai 503, bukan 403@BotFather ยท operator
403 USER_UNAVAILABLEOrang ini tidak bisa ditelepon: tidak ada izin, dimatikan atau perlu izin ulang, diblokir, bukan orang, di luar cakupan, di bawah 19 tahun, bukan penguji. Isinya selalu sama, apa pun alasannyaMinta izin
429 retry after NBatas di sisi bot atau pemilik: laju permintaan, permintaan harian bot atau pemilik, panggilan serentak bot, menit panggilan keluar pemilik, menit panggilan per hari bot atau pemilik (dipakai bersama panggilan ke bot)Setelah N detik
503 VOICE_UNAVAILABLESisi server: sakelar panggilan keluar mati, suara dimatikan di server (termasuk jika kebijakan privasi belum memuat bagian panggilan bot), sakelar darurat, tidak ada relay panggilan, batas dan jatah panggilan keluar server, tidak ada gateway yang bisa menelepon (atau semuanya penuh), permintaan tidak bisa dicatatNanti
{"ok": false, "error_code": 403, "description": "Forbidden: USER_UNAVAILABLE"}

Sisi orang itu dinilai setelah 200. Meski ada izin, pada jam tenang orang itu, saat dijeda, melewati batas harian dengan orang itu atau batas harian dan jarak waktunya sendiri, tanpa perangkat yang bisa dibunyikan, atau saat ia sedang dalam panggilan lain, tidak ada yang berdering dan not_answered datang sekitar ring_timeout + 12 detik setelah permintaan โ€” sama dengan dering yang tidak dijawab. Izin tidak mengubah sisi bot (403, 429, 503).

Pengaturan dan penggunaan

"outbound": {
  "enabled": true,
  "purposes": ["delivery"],
  "daily_cap": 50,
  "reviewed": true,
  "frozen_until": 0,
  "suspended": false,
  "available": true
}
"outbound": {
  "today": {"requests": 4, "answered": 2, "not_answered": 2, "minutes": 3},
  "limits": {"daily": 50, "owner_daily": 100, "owner_minutes": 30}
}
  • outbound di getVoiceCallSettings selalu ada: yang diatur di @BotFather (enabledยทpurposesยทdaily_cap), tinjauan operator (reviewed), kapan pembekuan token berakhir (frozen_until, 0 jika tidak dibekukan), penghentian otomatis (suspended), dan apakah bot bisa menelepon sekarang (available). unavailable_reason hanya ada jika available bernilai false (di sisi bot, nilainya sama dengan alasan 403 VOICE_OUTBOUND_OFF; di sisi server, voice_off ยท kill_switch ยท no_relay ยท outbound_off โ€” keempatnya berpasangan dengan 503 VOICE_UNAVAILABLE).
  • outbound di getVoiceCallUsage hanya ada untuk bot yang pernah menyalakan panggilan: permintaan, dijawab, tidak dijawab, dan menit hari ini, serta batas harian yang berlaku untuk bot ini. Jumlah dering tidak diberikan. callsยทsecondsยทmissedยทrefused tidak pernah menghitung panggilan yang dibuat bot. Namun panggilan yang dibuat bot memakai menit panggilan per hari bot dan pemiliknya yang sama dengan panggilan ke bot: detik panggilan yang dijawab ditagihkan sehingga bot dan owner di remaining_seconds_today berkurang โ€” selama berdering pun sudah berkurang sebesar yang dipesan panggilan itu, dan kembali jika tidak dijawab.
  • setVoiceCallSettings tidak bisa mengubahnya (400 VOICE_FIELD_NOT_AVAILABLE).

Update

Datang sebagai update voice_call yang sama dengan panggilan ke bot (tulis voice_call di allowed_updates). direction bernilai "outgoing", dan orang itu hanya muncul sebagai id di to (tanpa nama, nama pengguna, atau nomor telepon).

{
  "update_id": 83,
  "voice_call": {
    "id": "8812345678901234",
    "direction": "outgoing",
    "event": "not_answered",
    "to": {"id": 100005},
    "purpose": "delivery",
    "date": 1790000123
  }
}
{
  "update_id": 81,
  "voice_call": {
    "id": "8812345678901234",
    "direction": "outgoing",
    "event": "started",
    "to": {"id": 100005},
    "purpose": "delivery",
    "date": 1790000130,
    "max_duration": 180
  }
}
{
  "update_id": 82,
  "voice_call": {
    "id": "8812345678901234",
    "direction": "outgoing",
    "event": "ended",
    "to": {"id": 100005},
    "purpose": "delivery",
    "date": 1790000250,
    "start_date": 1790000130,
    "duration": 120,
    "max_duration": 180,
    "end_reason": "callee"
  }
}
{
  "update_id": 84,
  "voice_call": {
    "id": "8812345678901234",
    "direction": "outgoing",
    "event": "failed",
    "to": {"id": 100005},
    "purpose": "delivery",
    "date": 1790000110
  }
}
eventKapanKolom tambahan
startedOrang itu menjawab dan panggilan tersambung. Di Apple Watch, saat suara jam tangan tersambung (jika tidak pernah tersambung, ended 0 detik dengan failed)max_duration
endedPanggilan yang dijawab berakhir. Datang paling lambat 10 detik kemudian apa pun pengaturan server, karena server menunggu penggunaan dari gatewaystart_date, duration, max_duration, end_reason
not_answeredPermintaan yang tidak dijawab โ€” tanpa membedakan berdering tanpa dijawab, ditolak, atau tidak pernah berdering karena sisi orang itu. Sekitar ring_timeout + 12 detik setelah permintaanTidak ada
failedSambungan ke gateway yang memegang permintaan terputus sebelum dijawabTidak ada

end_reason: callee (orang itu menutup) ยท bot (bot menutup, atau tokennya diterbitkan ulang) ยท max_length (lama maksimum) ยท failed (sambungan gagal setelah dijawab) ยท operator (operator atau sakelar darurat) ยท suspended (bot atau pemilik ditangguhkan). Panggilan yang dibuat bot tidak punya missed, refused, atau ringing โ€” kecepatan menjawab akan menunjukkan apakah orang itu ada di dekat ponselnya โ€” dan tidak punya transcript (dimatikan secara desain).

Selama panggilan

  • endVoiceCall: sebelum bot diberi tahu hasilnya, menarik permintaan, baik saat berdering maupun belum (true, lalu not_answered). Mengakhiri panggilan yang sudah dijawab (end_reason bot). Menarik permintaan dalam 15 detik membuat pasangan itu dijeda satu jam.
  • sendVoiceCallContext: hanya setelah orang itu menjawab (sebelumnya, VOICE_CALL_NOT_FOUND). Tiga kali per panggilan, 1.000 karakter dan 3.072 byte, aturan konten yang sama dengan context. Jika gateway tidak menerima catatan, VOICE_CONTEXT_NOT_SUPPORTED.

Yang dilihat dan didengar penerima panggilan

  • Dering: "(AI)" setelah nama bot, dan "AI voice bot ยท Pabal never asks for codes" (bot suara AI ยท Pabal tidak pernah meminta kode).
  • Saat dijawab, sebuah pengumuman diputar sebelum bot: "This is a Pabal AI voice call. We never ask for passwords, codes or money transfers. To stop these calls, turn off call permission in the bot's chat." (Ini panggilan suara AI Pabal. Kami tidak pernah meminta kata sandi, kode, atau transfer uang. Untuk menghentikan panggilan ini, matikan izin panggilan di obrolan bot.) โ€” dalam bahasa layar izin yang dibaca orang itu (Korea atau Inggris); dari aplikasi berbahasa lain, dalam bahasa Inggris. Pemilik bot tidak bisa mematikannya.
  • Kata pertama bot: nama bot, bahwa ia AI, dan tujuan panggilan. context dipakai sesudahnya.
  • Cara mematikan: "Stop calls from this bot" (Hentikan panggilan dari bot ini) di menu, memblokir bot, dan melaporkannya selalu mematikan. Mengatakan dengan jelas saat panggilan sesuatu seperti "jangan telepon saya lagi" langsung mematikannya (sekadar "berhenti" tergantung penilaian model).
  • Dijeda: setelah penolakan, dering yang tidak dijawab, atau menutup dalam 10 detik setelah menjawab, bot tidak bisa menelepon orang itu selama 24 jam.
  • Catatan panggilan tak terjawab: jika sebuah perangkat benar-benar berdering, orang itu tidak memblokir bot, dan obrolan bot tidak punya pesan yang belum dibaca, tersimpan "Missed AI bot call" (panggilan bot AI tak terjawab โ€” sudah dibaca, tanpa notifikasi). Jika ada pesan yang belum dibaca, atau tidak berdering karena jam tenang atau jeda, tidak ada catatan. Setiap permintaan bot tersimpan tanpa kecuali di bukti laporan (30 hari).
  • Yang diterima pemilik bot (dalam urutan layar izin):
    1. id Pabal orang itu
    2. apakah izinnya menyala (terlihat dari jawaban atas permintaan panggilan)
    3. apakah orang itu menjawab
    4. kapan panggilan dimulai dan berapa lama
    5. siapa yang menutup
    Panggilan tak terjawab dan panggilan yang ditolak terlihat sama. Isi pembicaraan tidak dikirim.
  • Perangkat bersama: perangkat mana pun yang sedang masuk bisa menjawab, dan bot menyebut tujuan serta keterangan yang diberikan kepada siapa pun yang menjawab. Di komputer yang dipakai bersama, ingatkan untuk keluar.
  • Layar kunci: di Android 14 dan yang lebih baru, jika orang itu tidak mengizinkan notifikasi layar penuh, panggilan datang sebagai notifikasi, bukan layar panggilan di atas layar kunci. iPhone dan Apple Watch berdering seperti panggilan telepon.
  • Simpan percakapan menyimpannya hanya di obrolan orang itu sendiri, jika ia memilihnya di layar izin. Dengan Jawab di jam tangan yang dinyalakan di aplikasi Pabal untuk Apple Watch, Apple Watch juga berdering.
  • Panggilan dari bot tidak terenkripsi ujung ke ujung โ€” suara melewati server Pabal dan penyedia AI.

Batas ยท jam tenang ยท jeda ยท penghentian otomatis

BatasBawaanDi server produksi
Satu bot ke satu orang (per hari)3 panggilanOperator hanya bisa menurunkannya
Ke satu orang (per hari, semua bot)6 panggilanHanya bisa diturunkan
Jarak waktu antarpanggilan ke satu orang10 menitHanya bisa dinaikkan
Permintaan satu bot (per hari)50 panggilanPemilik memilih 10, 20, atau 50 di @BotFather
Satu pemilik bot (per hari, semua bot)100 panggilan ยท 30 menitJumlah panggilan hanya bisa diturunkan
Sampai bot boleh mengirim permintaan berikutnya10 detikHanya bisa dinaikkan
Menit panggilan keluar server30% dari menit harian serverHanya bisa diturunkan
Jam tenang21:00โ€“08:00Operator dan orang itu hanya bisa memperluasnya. Menurut zona waktu perangkat
Jendela bangun (khusus wake_up)paling lama 120 menitOrang itu memilihnya di dalam jam tenangnya
Dering (ring_timeout)30โ€“45 detik40 detik jika permintaan tidak menyebutkannya
Jeda โ€” penolakan, dering tak dijawab, menutup dalam 10 detik setelah menjawab24 jamHanya bisa dinaikkan
Jeda โ€” bot menarik permintaan dalam 15 detik1 jamHanya bisa dinaikkan
  • "Per hari" adalah satu hari menurut zona waktu server (TELEGRAM_VOICE_TIME_ZONE). Panggilan tidak pernah lebih dari 300 detik.
  • Panggilan dari bot juga memakai menit panggilan per hari yang sama dengan panggilan ke bot (bot 60 menit ยท pemilik 120 menit). Jika sudah habis, startVoiceCall mendapat 429, dan panggilan ke bot itu juga ditolak (refused, budget).
  • Jeda berakhir saat waktunya habis, saat orang itu menelepon bot, mengetuk "I can take calls now" (Saya bisa menerima panggilan sekarang), atau mengizinkannya lagi. Pesan atau tombol dari bot tidak mengakhirinya.
  • Penghentian otomatis hanya menghentikan panggilan bot kepada orang (403 VOICE_OUTBOUND_OFF: suspended) dan berlangsung sampai operator memeriksa dan mencabutnya: tingkat jawaban di bawah 20% dari 20 panggilan terakhir, 3 laporan dalam 7 hari dari orang yang pernah ia buat berdering, 30% atau lebih orangnya mematikan izin (dengan sedikitnya 10 orang), 2 panggilan terkena aturan selama panggilan, 5 penarikan dalam 15 detik, atau 2 orang atau kurang yang menjawab dengan 60 menit atau lebih panggilan yang dijawab.

Batas

BatasBawaanSiapa yang menentukan
Durasi maksimum satu panggilanBot 300 detik (rentang yang bisa diatur: 60 sampai batas atas operator dan server, bawaan 600)Pemilik bot, di dalam batas atas operator dan server. Tombol @BotFather: 1, 3, 5, dan 10 menit
Panggilan bersamaan satu bot2 (1 sampai batas atas operator dan server, bawaan 2)Pemilik bot, di dalam batas atas operator dan server
Menit panggilan per hari untuk satu bot60 menitOperator
Per hari untuk satu pemilik bot (semua bot digabung)120 menitOperator
Per hari untuk seluruh server300 menitOperator
Satu penelepon60 menit per hari, 20 panggilan per jamOperator
Panggilan terpendek yang diterima60 detik โ€” jika sisa waktu kurang dari ini, tidak diterimaOperator
Akun baruOrang yang mendaftar kurang dari 7 hari tidak bisa menelepon โ€” kecuali penguji, dan orang yang pernah dikirimi pesan oleh bot lebih dari sehari sebelumnyaOperator
Mengubah pengaturan30 kali per jam per botโ€”
Catatan selama panggilan10 per menit per panggilanโ€”
Tool16 buah dan 8 KB; kueri terbuka 2 per panggilan dan 8 per bot; per menit 30 per panggilan dan 120 per botโ€”
Pengujian kunci sendiri ยท probe stream5 kali per jam per bot, 10 kali per pemilikโ€”
Pendaftaran dan pemeriksaan MCP10 kali per jam per bot, 20 kali per pemilik, 2 server per botโ€”

Dari Apple Watch, bot yang memakai tool, server MCP, kunci sendiri, mode stream, atau transkrip pemilik bot tidak bisa ditelepon. Penolakan ini hanya sampai ke penelepon (403 WATCH_NOT_YET di aplikasi jam tangan), dan bot tidak menerima update refused.

Durasi satu panggilan adalah yang terkecil di antara sisa waktu hari ini untuk penelepon, bot, pemilik, dan server, serta batas atas panggilan. Saat panggilan diterima, durasi itu dicadangkan di keempat batas, dan setelah selesai hanya detik yang sebenarnya menurut jam server yang ditagihkan. Panggilan berkunci sendiri tidak memakai batas bot, pemilik, dan server (batas penelepon tetap berlaku).

Error

Error berbentuk sama seperti metode lain: {"ok": false, "error_code": 400, "description": "Bad Request: VOICE_โ€ฆ"}. 403 berbentuk Forbidden: <kode>: <keterangan> (kecuali USER_UNAVAILABLE, yang selalu persis Forbidden: USER_UNAVAILABLE tanpa keterangan), dan 429 disertai parameters.retry_after.

KodeArti
403 VOICE_RULES_NOT_ACCEPTEDAturan bot suara harus disetujui lebih dulu di @BotFather
403 VOICE_NOT_AVAILABLEBot ini tidak bisa memakai suara di server ini (suara mati, operator tidak mengizinkan) โ€” pengaturan tidak bisa diubah, hanya mematikan dan menghapus yang bisa
403 VOICE_SUSPENDEDOperator menangguhkan fitur suara bot ini. Hanya mematikan yang bisa
403 VOICE_OWNER_TERMS_NOT_ACCEPTEDKetentuan pemilik bot (versi 4) harus disetujui lebih dulu
403 VOICE_OWN_KEY_NOT_AVAILABLEServer ini tidak menerima kunci sendiri
403 VOICE_STREAM_NOT_AVAILABLEMode stream tidak bisa dipakai di server ini saat ini
403 VOICE_OUTBOUND_OFF: <alasan>Bot ini tidak bisa menelepon orang saat ini: owner_off ยท terms ยท review ยท suspended ยท frozen ยท owner_name ยท mode, atau alasan sisi bot dari pengaturan suara (Menelepon). voice_off ยท kill_switch ยท no_relay ยท outbound_off datang sebagai 503
403 USER_UNAVAILABLEOrang ini tidak bisa ditelepon โ€” isinya selalu sama apa pun alasannya, termasuk tidak adanya izin
503 VOICE_UNAVAILABLEServer tidak bisa menelepon orang saat ini (sakelar panggilan keluar, suara server mati, sakelar darurat, tidak ada relay panggilan, batas server, gateway, permintaan gagal dicatat) โ€” coba lagi nanti
Bad Request: purpose is not declared, Bad Request: the bot's voice language must be ko or en for outbound callsstartVoiceCall: tujuan yang tidak dipilih bot, bahasa bot selain Korea atau Inggris
VOICE_INSTRUCTIONS_REQUIRED, VOICE_INSTRUCTIONS_TOO_LONG, VOICE_GREETING_TOO_LONGInstruksi diperlukan ยท instruksi melebihi 8.192 byte ยท salam pembuka melebihi 512 byte
VOICE_INVALID, VOICE_LANGUAGE_INVALID, VOICE_LIMIT_INVALID, VOICE_MODE_INVALIDSuara atau bahasa tidak ada ยท batas di luar rentang ยท mode selain managed/stream
VOICE_CONTENT_REFUSED: <kolom>: <kode>, VOICE_CONTEXT_REFUSED: <kode>Aturan konten (Pengaturan, context pada startVoiceCall)
VOICE_TOOL_INVALID: <jalur>: <alasan>Definisi tool (Mendefinisikan tool)
VOICE_FIELD_NOT_AVAILABLE: โ€ฆKolom yang tidak diterima (model, api_key), kolom stream pada bot terkelola, atau kolom panggilan dari bot (outboundยทpurposesยทdaily_cap โ€” hanya di @BotFather)
VOICE_CALL_NOT_FOUND, VOICE_CALL_NOT_ACTIVE, VOICE_CONTEXT_NOT_SUPPORTED, VOICE_CONTEXT_TOO_LONGMetode selama panggilan (Selama panggilan)
VOICE_USAGE_PERIOD_INVALIDPeriode getVoiceCallUsage
TOOL_QUERY_ID_INVALID, TOOL_QUERY_EXPIRED: โ€ฆ, TOOL_RESULT_โ€ฆ, TOOL_ERROR_TOO_LONGJawaban tool (Menjawab)
API_KEY_INVALID, API_KEY_NO_REALTIME_ACCESS, 503 API_KEY_CHECK_UNAVAILABLE, VOICE_PROVIDER_INVALIDKunci sendiri (Kunci model sendiri)
STREAM_URL_INVALID, STREAM_SECRET_REQUIRED, STREAM_SECRET_INVALID, STREAM_PROBE_FAILED: โ€ฆMode stream (Pengaturan dan probe)
MCP_โ€ฆ, MCP_OPT_IN_REQUIREDServer MCP (Mendaftarkan server). 503 VOICE_NOT_AVAILABLE: mcp: โ€ฆ: coba lagi sebentar kemudian; 409 MCP_CHECK_IN_PROGRESS: coba lagi setelah pemeriksaan selesai
429 Too Many Requests: retry after NBatas per jam (pengaturan, pengujian kunci, probe, MCP), batas catatan, atau batas panggilan dari bot (batas)

Bot yang tersambung lewat MTProto

  • Bot yang hanya masuk lewat MTProto memanggil metode suara dengan bots.sendCustomRequest (JSON yang sama, dengan nama metodenya). Yang bisa: setVoiceCallSettings, getVoiceCallSettings, answerVoiceCallToolQuery, endVoiceCall, sendVoiceCallContext, getVoiceCallUsage, startVoiceCall, dan lima metode MCP. Selain itu: METHOD_INVALID. Jawaban startVoiceCall adalah {"voice_call_id": "โ€ฆ"}, dan penolakan datang sebagai error RPC (USER_UNAVAILABLE, VOICE_OUTBOUND_OFF: review, FLOOD_WAIT_n, VOICE_UNAVAILABLE).
  • Metode yang tidak bisa dipanggil dari jawaban webhook: setVoiceCallApiKey, startVoiceCall, serta metode pendaftaran dan kunci MCP. Jika diletakkan di isi jawaban atas POST webhook, metode itu tidak dijalankan โ€” agar tidak ada pesan dari seseorang yang langsung berubah menjadi panggilan telepon lewat jawaban webhook. Panggil dengan permintaan tersendiri.
  • Kunci sendiri hanya lewat HTTPS: setVoiceCallApiKey dan deleteVoiceCallApiKey tidak bisa dipanggil lewat sendCustomRequest. Untuk menghapus kunci, pakai HTTP Bot API atau @BotFather.
  • Untuk bot tanpa webhook yang tidak memanggil getUpdates dalam 15 detik terakhir, voice_call (termasuk untuk panggilan yang dibuat bot) didorong sekali lewat updateBotWebhookJSON (tidak bergantung pada allowed_updates, tidak dikirim ulang). Kueri tool datang lewat updateBotWebhookJSONQuery dan dijawab dengan bots.answerWebhookJSONQuery berisi {"result": โ€ฆ} atau {"error": "โ€ฆ"}. voice_call_transcript tidak datang lewat MTProto (transcript pada update akhir panggilan tetap datang).
  • Untuk error RPC, 429 menjadi FLOOD_WAIT_n, dan sisanya adalah kode huruf besar apa adanya.