Harici İşleyici

Harici işleyici, kodunuz ile Telegram arasında bir köprüdür. Yapıcının mantığı yetmediğinde, belirli bir tepkiyi kendi sunucunuza devredersiniz: sunucu olayları alır ve Telegram Bot API formatında komutlarla yanıt verir; taşıma, teslim ve tüm Telegram işlemleri GetMyBot tarafından üstlenilir.

Bu nedir

GetMyBot'taki tepkiler görsel olarak oluşturulur: tetikleyici → koşullar → eylemler. Karmaşık veya standart dışı mantığı — kendi veritabanınızı, hesaplamaları, harici sistemlere yapılan çağrıları, makine öğrenmesini, dallanan senaryoları — yapıcıda ifade etmek güçtür. Harici işleyici bu tavanı kaldırır: kendi backend'inizi bağlar ve tüm tepkilerin işlenmesini ona devredersiniz.

Harici servis, GetMyBot'tan olayları alır (tetikleyici çalıştı, kullanıcı metin yanıtladı, düğmeye tıkladı) ve karşılığında bota ne göndereceğini söyler. GetMyBot, komutları kendi bot token'ı ile uygular — rate-limit, yeniden denemeler, deduplication ve diyalog günlüğü bunun üzerinde çalışır.

Çalışma modeli

  • GetMyBot bir WebSocket sunucusudur. Servisiniz kendisi bağlanır ve genel bir adrese veya webhook uç noktasına ihtiyaç duymaz. GetMyBot, servisinize giden bağlantı yapmaz; bu nedenle SSRF yüzeyi yoktur.
  • Kimlik doğrulama — entegrasyon token'ı ile. Servis, bağlantıda token'ı sunar; GetMyBot bunu hash ile doğrular ve bağlantıyı açık tutar.
  • Olaylar ve komutlar — Telegram Bot API formatında. Servis bir olay alır ve yanıt olarak "metot + parametreler" biçiminde çağrı dizisi gönderir; aynen normal bir botla çalışırken olduğu gibi.
  • Komutlar GetMyBot tarafından kendi token'ıyla uygulanır. Servis asla doğrudan Telegram ile iletişim kurmaz: komutlar doğrulamadan geçer ve GetMyBot'un dahili outbox'ına girer; oradan rate-limit, yeniden denemeler, deduplication ve günlükle birlikte bot token'ı aracılığıyla gönderilir.

Tek bir bağlantı, bir entegrasyonun tüm diyaloglarına hizmet eder — farklı kullanıcıların olayları çoğullanır ve session_id alanıyla ayırt edilir.

Entegrasyon kurulumu

  1. Sol menüde Entegrasyonlar bölümünü açın ve Entegrasyon Ekle düğmesine tıklayın.
  2. Harici İşleyici servisini seçin ve bir ad belirleyin.
  3. Oluşturulduktan sonra kart şunları gösterir:
    • WebSocket adresiwss://api.mybot.app/ext/ws/<integrationID> biçiminde; <integrationID>, bu entegrasyonun UUID'sidir. Servisiniz bu adrese bağlanır. Yolda id olmadan bağlantı kurulamaz.
    • Bağlantı token'ı — oluşturulurken yalnızca bir kez gösterilir. Kopyalayın ve güvenli bir yerde saklayın; token tekrar görüntülenemez (yalnızca hash olarak saklanır).
    • Token'ı yenile — düğme yeni bir token üretir ve eskisini hemen geçersiz kılar. Yenilemeden sonra token'ı servisinizde güncelleyin.
    • Çevrimiçi göstergesi — servisinizin şu anda aktif bir bağlantı tutup tutmadığını gösterir.

Protokolü etkileyen ek entegrasyon parametreleri:

  • session_ttl_seconds — saniye cinsinden oturum ömrü (varsayılan 3600).
  • on_unavailable_reaction_id — tetikleme anında servis çevrimdışıysa başlatılan yedek tepki (bkz. Güvenilirlik).

Tepkiye bağlama

Harici işleyici ayrı bir tetikleyici olarak değil, bir eylem olarak bağlanır. Tepki editörüne Harici İşleyiciye Aktar eylemini ekleyin ve ilgili entegrasyonu seçin.

Diyalogu herhangi bir mevcut GetMyBot tetikleyicisi açabilir — komut, metin, düğme tıklaması, bağlantıdan gelen parametre, gelen web isteği, zamanlama. Böyle bir tetikleyici çalıştığında ve eyleme ulaştığında, GetMyBot bir proxy oturumu açar ve servise session.open olayı gönderir.

Bu sırada servis bağlı değilse, GetMyBot entegrasyon ayarından yedek tepkiyi (on_unavailable_reaction_id) başlatır. Yedek tanımlanmamışsa — hiçbir şey olmaz (kullanıcıya hata gösterilmeden sessizce).

Bağlantı ve yetkilendirme

Uç nokta: WebSocket (wss) üzerinden GET /ext/ws/{integrationID}. integrationID — "Harici İşleyici" türündeki entegrasyonun UUID'si. Kare boyutu sınırı — 256 KiB.

İki yöntemden biriyle yetkilendirilebilir.

1. El sıkışmada başlık (önerilen). Token'ı Authorization başlığında iletin:

Authorization: Bearer <token>

2. Auth karesiyle. Bearer başlığı iletilmemişse, bağlantı kurulduktan sonra 10 saniye içinde ilk kare olarak şunu gönderin:

{ "type": "auth", "token": "<token>" }

10 saniye içinde auth karesi gelmezse — bağlantı 4401 koduyla kapatılır. Her iki yöntemde de yanlış token — yine 4401 kodu ile kapatma.

Token, entegrasyon oluşturulurken üretilir, bir kez gösterilir ve yalnızca hash (sha256) olarak saklanır; doğrulama sabit süreli yapılır. Token rotasyonu:

POST /api/bots/{botID}/integrations/{integrationID}/rotate-token

Uç nokta yeni düz metin token döndürür; eski token hemen çalışmayı durdurur.

Olaylar: bot → servis

Her olay bir JSON karesidir (EventEnvelope). type alanı şunlardan biridir: session.open, message, callback, session.cancel, session.expired.

Zarfın tam alan kümesi:

{
  "type": "session.open",
  "session_id": "string",
  "bot_id": "string",
  "user": {
    "tg_user_id": 123456789,
    "first_name": "Ivan",
    "username": "ivan",
    "params": { "utm": "promo" }
  },
  "chat": { "id": 123456789, "type": "private" },
  "update": { "...": "ham Telegram Update (raw JSON)" },
  "event": { "...": "normalleştirilmiş GetMyBot olayı (raw JSON)" }
}
  • session_id — oturum (diyalog) tanımlayıcısı.
  • bot_id — bot tanımlayıcısı.
  • user — kullanıcı verileri: tg_user_id (int64), isteğe bağlı first_name, username ve params — kullanıcı parametreleri (bağlantıdan gelenler dahil), anahtar→değer map'i.
  • chat — sohbet: id (int64) ve type (örneğin "private").
  • update — ham Telegram Update (raw JSON). session.open, message, callback için mevcuttur.
  • event — normalleştirilmiş GetMyBot olayı (raw JSON).

Türlere göre içerik farklılık gösterir:

  • session.open — tetikleyici çalıştı, yeni diyalog açıldı. update (ham Update) ve event (tam normalleştirilmiş olay) mevcuttur, user.params doldurulmuştur.
  • message — servis "bekleme tuttuğunda" (expect = text/any) ve kullanıcı mesaj gönderdiğinde gelir. İçerik session.open ile aynıdır.
  • callback — kullanıcı, daha önce servisin gönderdiği bir düğmeye tıkladı. update ham; user'da yalnızca tg_user_id doldurulmuştur; event yalnızca servisin düğmede belirlediği orijinal callback_data'yı içerir:
{ "callback_data": "<servisin düğmede belirlediği orijinal değer>" }
  • session.cancel / session.expired — minimum zarf: session_id, bot_id, user.tg_user_id, chat.id (cancel için ayrıca chat.type). update/event alanları yoktur. Bu olaylar yalnızca servis çevrimiçiyse teslim edilir — çevrimdışı kuyruğuna konulmaz.

Komutlar: servis → bot

Komut bir JSON karesidir (CommandEnvelope). type alanı şunlardan biridir: execute, session.close, auth, ping, pong.

{
  "type": "execute",
  "session_id": "string",
  "methods": [
    { "method": "sendMessage", "params": { "text": "Merhaba" } }
  ],
  "expect": "none"
}
  • session_id — komutun ait olduğu oturum.
  • token — yalnızca type: "auth" için (bkz. Bağlantı ve yetkilendirme).
  • methods{ "method": "<Telegram Bot API metot adı>", "params": { ... } } nesnelerinden oluşan dizi. Metot adı, Telegram Bot API'deki ile birebir aynıdır (örneğin sendMessage); params ise bu metodun parametre nesnesidir.
  • expect — servisin kullanıcı yanıtı bekleyip beklemediği: "none", "text" veya "any" (boş değer none olarak yorumlanır).
  • session.close — diyalogu açıkça kapat.

Komutlar doğrudan Telegram'a gitmez: GetMyBot bunları doğrulayarak kendi outbox'ına koyar; oradan rate-limit, yeniden denemeler, deduplication ve günlükle birlikte bot token'ı aracılığıyla gönderilir.

execute nasıl işlenir

Şu durumlarda execute komutu tamamen düşürülür: session_id boşsa; oturum bulunamazsa veya open durumunda değilse; oturum başka bir entegrasyona aitse; oturum süresi dolmuşsa.

Ardından:

  • Rate-limit: oturum başına dakikada 60 execute (kayan pencere). Limitin üzerinde — düşürme.
  • Tek bir execute'ta en fazla 30 metot; fazlası sessizce atılır.
  • Her metot için whitelist kontrolü yapılır (bkz. İzin verilen metodlar) — listede olmayan metot reddedilir.
  • Chat-guard: params'ta chat_id veya from_chat_id değeri 0'dan farklı ve oturumun chat_id'siyle eşleşmiyorsa — metodun tamamı reddedilir (başka bir sohbete mesaj gönderilemez). chat_id hiç belirtilmeyebilir: GetMyBot oturum sohbetini zorunlu olarak atar.
  • callback_data içeren inline düğmeler otomatik olarak token'a dönüştürülür (dahili format x:<token>); düğme ömrü = oturum TTL'sine kadar. url/webapp/switch_inline düğmelerine dokunulmaz.

İzin verilen metodlar

Servis, botu kendi token'ıyla yönetir; bu nedenle metot kümesi whitelist ile sınırlıdır — yalnızca mevcut diyalog mesajlarının gönderilmesi ve yönetimi. Listede olmayan metodlar sessizce yok sayılır (bu bir hata değildir).

  • gönderme: sendMessage, sendPhoto, sendDocument, sendVideo, sendAudio, sendMediaGroup, sendAnimation, sendVoice, sendLocation, sendChatAction;
  • düzenleme ve silme: editMessageText, editMessageCaption, editMessageReplyMarkup, deleteMessage;
  • tıklamaya yanıt: answerCallbackQuery;
  • iletme ve kopyalama: forwardMessage, copyMessage;
  • sabitleme: pinChatMessage, unpinChatMessage.

Hesap düzeyindeki metodlar (setWebhook, getUpdates, logOut, close, setMyCommands vb.) izin verilmez.

Bekleme ve oturum yaşam döngüsü

Her execute sonrasında oturumun durumu expect değerine ve komutta callback_data içeren inline düğme olup olmadığına bağlıdır:

  • expect = text veya any — kullanıcının bir sonraki mesajı message olayıyla servise iletilir.
  • expect = none VE komutta callback'li inline düğme YOK — oturum otomatik olarak kapanır. Bu "terminal" mesajdır.
  • expect = none, ANCAK callback düğmeleri VAR — oturum açık kalır: tıklamalar, düğme token'ları (TTL'ye kadar) yaşarken yakalanır.

Önemli: expect, düğme tıklamalarının alımını etkilemez. Token'lı düğmelere tıklamalar, expect değerinden bağımsız olarak — oturum ve düğme token'ı yaşadığı sürece — yakalanır. expect yalnızca servisin metin/isteğe bağlı yanıt bekleyip beklemediğini kontrol eder.

Diyalog sınırları

Diyalog bağlamı, oturum açık olduğu sürece yaşar:

  • Aktif bekleme sırasında metin yanıtı (expect = text/any) — botun normal tepkilerini başlatmak yerine message olayıyla servise gönderilir.
  • Token'lı düğmeye tıklama — orijinal callback_data ile callback olayı olarak servise gönderilir. GetMyBot her zaman tıklamayı kendisi onaylar (answerCallbackQuery); tıklamanın aidiyeti (bot + kullanıcı + oturum sohbeti) kontrol edilir. Düğme, oturumu kapatmaz.
  • /cancel (ayrıca /cancel@bot ve "iptal") — aktif harici oturumda — oturum kapanır, servise session.cancel gönderilir, kullanıcıya "İptal edildi." gönderilir.
  • Kullanıcı başına bir diyalog. Yeni oturum açılması, bu kullanıcının önceki açık oturumunu geçersiz kılar — ona session.cancel gönderilir.
  • TTL süresi dolumu. Zamanlayıcıyla oturum expired olarak işaretlenir ve (servis çevrimiçiyse) servise session.expired gönderilir.

Limitler ve zaman aşımları

  • Kare boyutu: 256 KiB.
  • Oturum TTL'si: varsayılan 3600 saniye (entegrasyonun session_ttl_seconds parametresiyle ayarlanabilir).
  • Komut sıklığı: oturum başına dakikada 60 execute (kayan pencere).
  • Bir execute'taki metot sayısı: en fazla 30 (fazlası atılır).
  • Entegrasyon başına açık oturum: en fazla 1000.
  • Çevrimdışı kuyruğu: oturum başına en fazla 100 olay.
  • Heartbeat: her 30 saniyede bir ping; aktivite eksikliği zaman aşımı — 60 saniye.
  • Auth karesi zaman aşımı: 10 saniye.

Güvenilirlik (heartbeat, çevrimdışı, kuyruk, fallback)

  • Heartbeat. Sunucu her 30 saniyede bir {"type":"ping"} karesi gönderir. İstemciden 60 saniyeden fazla aktivite yoksa — bağlantı kapatılır. İstemci, aktifliği korumak için kendi ping/pong'larını gönderebilir. Bağlantı kesilmesi diyalogları öldürmez: oturumlar TTL'ye veya yeniden bağlanmaya kadar veritabanında yaşar.
  • Yeniden bağlanma — servis tarafında. Bağlantı koptuğunda servisiniz yeniden bağlanmalıdır. Veritabanındaki açık oturumlar TTL dolana kadar yeniden başlatmaları atlatır.
  • Başlangıçta çevrimdışı fallback. Tepki tetiklendiğinde servis bağlı değilse — entegrasyon ayarından yedek tepki başlatılır (on_unavailable_reaction_id). Tanımlanmamışsa — hiçbir şey olmaz (sessizce).
  • Çevrimdışı kuyruğu. Çevrimdışı nedeniyle teslim edilemeyen message/callback olayları çevrimdışı kuyruğuna eklenir (en fazla 100 olay, FIFO, oturum süresine kadar saklanır) ve yeniden bağlandığında teslim edilir. session.open/session.cancel/session.expired olayları kuyruğa konulmaz.
  • Teslim garantisi — en fazla bir kez (at-most-once). Kuyruk dolması veya süresinin dolması durumunda olaylar atılır. Tek bir olayın atlanmasının senaryoyu bozmaması için mantığınızı buna göre tasarlayın.

Çevrimiçi göstergesi

Servisin aktif bağlantı tutup tutmadığı şu uç nokta ile kontrol edilebilir:

GET /api/bots/{botID}/integrations/{integrationID}/status

Yanıt:

{ "online": true }

Aynı gösterge, yönetim panelindeki entegrasyon kartında da mevcuttur. MCP'deki karşılığı — get_integration_status aracı.

Güvenlik

GetMyBot, harici bir servisin komutlarını kendi token'ıyla yürütür; bu nedenle koruma katıdır.

  • SSRF yüzeyi yoktur. GetMyBot bir WebSocket sunucusu olarak hareket eder ve servise giden bağlantı yapmaz — servistir bağlanan taraf. Platformun dışarıdan kontrol edilebilir bir URL'ye gittiği durum söz konusu değildir.
  • Token hash olarak saklanır (sha256), doğrulama sabit süreli yapılır, rotasyon desteklenir.
  • Kiracı izolasyonu. Komut kendi entegrasyonuna sıkı sıkıya bağlıdır; oturum — bota + kullanıcıya + sohbete. Diğer botların olayları sokete gelmez.
  • Metot whitelist'i + zorunlu chat_id, botu rastgele sohbetlere mesaj göndericiye dönüştürmez.
  • Bot token'ı dışarıya iletilmez — servis hiçbir zaman doğrudan Telegram ile iletişim kurmaz.
  • Gizli değerlerin maskelenmesi. Entegrasyon token'ı ve hassas değerler günlük ve loglarda gizlenir.

Örnekler

Minimum işleyici: diyalog açıldığında mesaj gönderir ve oturumu kapatır (expect: "none"). Entegrasyon id'sini ve token'ı yerine koyun. Adres, yolda zorunlu olarak <integrationID> içermelidir.

Node.js

import WebSocket from "ws";

const URL = `wss://api.mybot.app/ext/ws/${process.env.MYBOT_INTEGRATION_ID}`;
const ws = new WebSocket(URL, {
  headers: { Authorization: `Bearer ${process.env.MYBOT_TOKEN}` },
});

ws.on("message", (raw) => {
  const ev = JSON.parse(raw);
  if (ev.type === "session.open") {
    ws.send(JSON.stringify({
      type: "execute",
      session_id: ev.session_id,
      expect: "none",
      methods: [{ method: "sendMessage", params: { text: "Harici işleyiciden merhaba!" } }],
    }));
  }
});

Python

import json, os, asyncio, websockets

async def main():
    url = f"wss://api.mybot.app/ext/ws/{os.environ['MYBOT_INTEGRATION_ID']}"
    headers = {"Authorization": f"Bearer {os.environ['MYBOT_TOKEN']}"}
    async with websockets.connect(url, additional_headers=headers) as ws:
        async for raw in ws:
            ev = json.loads(raw)
            if ev["type"] == "session.open":
                await ws.send(json.dumps({
                    "type": "execute",
                    "session_id": ev["session_id"],
                    "expect": "none",
                    "methods": [
                        {"method": "sendMessage", "params": {"text": "Harici işleyiciden merhaba!"}}
                    ],
                }))

asyncio.run(main())

Inline düğme ve callback işleme

Tıklamayı yakalamak için callback_data içeren bir düğme gönderin (GetMyBot otomatik olarak token'a dönüştürür). expect: "none" ile düğmeli oturum, düğme token'ı yaşadığı sürece açık kalır — tıklama, event.callback_data'sının servisin düğmede belirlediği orijinal değere eşit olduğu callback olayıyla gelir. Tıklamayı onaylamaya (answerCallbackQuery) gerek yoktur — GetMyBot bunu kendisi yapar.

if (ev.type === "session.open") {
  ws.send(JSON.stringify({
    type: "execute",
    session_id: ev.session_id,
    expect: "none",
    methods: [{
      method: "sendMessage",
      params: {
        text: "Düğmeye tıklayın",
        reply_markup: { inline_keyboard: [[{ text: "Haydi", callback_data: "go" }]] },
      },
    }],
  }));
} else if (ev.type === "callback" && ev.event.callback_data === "go") {
  ws.send(JSON.stringify({
    type: "execute",
    session_id: ev.session_id,
    expect: "none",
    methods: [{ method: "sendMessage", params: { text: "Düğmeye basıldı!" } }],
  }));
}

Sırada ne var