pypi.org ↗
Kurulumpip install signalbird

signalbird - Python

Django, FastAPI, Flask, Celery işçileri ve düz betikler için. Üç yüzey taşır: Telsiz (log), Gönderim (e-posta/SMS/push) ve Yönetim (proje, sohbet gelen kutusu, uygulama).

Kurulum

bash
pip install signalbird

Python 3.8+ gerekir. Hiçbir bağımlılığı yoktur - standart kütüphanenin urllib'i kullanılır. Bir SDK'nın requests sürümü dayatması, bağımlılık çözümlemesini kilitleyen en sık sebeptir.

Anahtarlar

Yüzey Anahtar Nereden
Telsiz sb_secret_live_… Panel → Telsiz → Projeler (bir kez gösterilir)
Gönderim + Yönetim sb_… Panel → Konsol → API Anahtarları
bash
# .env
SIGNALBIRD_DOMAIN_KEY=sb_secret_live_xxxxxxxxxxxxxxxxxxxxxxxx
SIGNALBIRD_SOURCE=api-01
SIGNALBIRD_DOMAIN_KEY=sb_xxxxxxxxxxxxxxxxxxxxxxxx

Telsiz - log ve olay

python
from signalbird import signalbird

# SIGNALBIRD_DOMAIN_KEY ortam değişkeninden okunur, tekildir
signalbird().critical("critical", "ödeme servisi yanıt vermiyor", {
    "service": "iyzico",
    "attempt": 3,
})

signalbird().info("info", "ahmet@x.com yeni hesap oluşturdu")

Kendi istemciniz:

python
from signalbird import SignalbirdClient

radio = SignalbirdClient(
    domain_key=settings.SIGNALBIRD_DOMAIN_KEY,
    source="api-01",
    timeout=5.0,
    throw_on_error=False,   # varsayılan: sessiz hata
)

radio.log("deploy", "v2.4.0 yayında", level="info")

Beş seviye vardır: debug, info, warn, error, critical. Seviye vermezseniz kanalın kendi varsayılanı geçerlidir.

Toplu gönderim

python
result = radio.batch([
    {"channel": "deploy", "message": "v2.4.0 yayında", "level": "info"},
    {"channel": "critical", "message": "kuyruk doldu"},
])

En fazla 100 kayıt. Kısmi başarı normaldir (kota tam ortada dolabilir); sonuç satır satır döner ve başarısız satırlar yeniden denenmez.

Django ile

python
# settings.py
SIGNALBIRD = SignalbirdClient(domain_key=env("SIGNALBIRD_DOMAIN_KEY"), source=env("APP_ENV"))

# middleware.py
class SignalbirdMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        return self.get_response(request)

    def process_exception(self, request, exception):
        # Sessiz hata: telsiz erişilemezse istek zaten 500 dönecek,
        # bir de SDK yüzünden ikinci bir hata üretmesin.
        settings.SIGNALBIRD.error("api", str(exception), {
            "path": request.path,
            "method": request.method,
        })

Celery ile

python
@app.task(bind=True)
def process_payment(self, order_id):
    try:
        charge(order_id)
        signalbird().info("odeme", f"Sipariş #{order_id} başarılı")
    except Exception as error:
        signalbird().critical("critical", f"Sipariş #{order_id}: {error}")
        raise

Gönderim - e-posta, SMS, push

python
from signalbird import SignalbirdMessaging

sb = SignalbirdMessaging(domain_key=os.environ["SIGNALBIRD_DOMAIN_KEY"])

result = sb.send_email({
    "to": "ahmet@example.com",
    "class": "transactional",       # ZORUNLU, varsayılanı yok
    "subject": "Siparişiniz yola çıktı",
    "body": "<p>Kargo takip no: 1234</p>",
})

if not result.ok:
    logger.warning("signalbird: %s - %s", result.code, result.message)

class alanı hukuki bir kapıdır: transactional (işlemsel) ya da commercial (ticari). Varsayılanı yoktur - kararı SDK veremez.

python
sb.send_sms({"to": "+905551112233", "class": "transactional", "body": "Kodunuz: 123456"})
sb.preview_sms("Merhaba, siparişiniz hazır.")     # parça hesabı, kota harcamaz
sb.send_push({"to": "contact:42", "class": "transactional", "subject": "Sipariş", "body": "Yola çıktı"})

Kişiler ve listeler

python
sb.list_contacts({"q": "ahmet", "page": 1})
sb.create_contact({"email": "ahmet@example.com", "first_name": "Ahmet"})
sb.update_contact(contact_id, {"phone": "+905551112233"})
sb.delete_contact(contact_id)

# 1000'lik parçalar hâlinde SIRAYLA yükler
sb.bulk_contacts({
    "contacts": rows,
    "list_id": 42,
    "consent_source": "web_form",
    "consent_text": "Bültene abone olmayı kabul ediyorum",
})

Toplu yükleme paralel değildir: aynı e-posta iki parçada da varsa yarış olmasın. Bir parça başarısız olursa o noktada durur ve o ana kadar biriken sayımlar döner - yarısı yüklenmiş bir listeyi "başarısız" diye atmak, hangi kişinin girdiğini bilmemek demektir.

Kampanyalar

python
sb.create_campaign({
    "name": "Ağustos bülteni",
    "channel": "email",
    "list_id": 42,
    "subject": "Bu ay neler oldu?",
    "body": "<h1>Merhaba</h1>",
    "scheduled_at": "2026-09-01T09:00:00Z",
})

sb.get_campaign(batch_id)
sb.cancel_campaign(batch_id)

# Sayfa sayfa gezer, tüm listeyi belleğe almaz
for message in sb.iterate_campaign_messages(batch_id):
    reconcile(message["external_ref"], message["status"])

Kampanyadan çıkan her ileti zorunlu commercial'dır.

Webhook doğrulama

python
from signalbird import verify_webhook

# Django - request.body HAM gövdedir
if not verify_webhook(request.body, request.headers.get("X-Signalbird-Signature"), SECRET):
    return HttpResponseForbidden()

# FastAPI
raw = await request.body()
if not verify_webhook(raw, request.headers.get("x-signalbird-signature"), SECRET):
    raise HTTPException(403)

Doğrulama ham gövde üzerinde yapılır. request.json() okuyup json.dumps ile geri çevirmek imzayı bozar - alan sırası ve boşluklar değişir. Python'da en sık düşülen tuzak budur.


Yönetim - kendi projenizi kurun

python
from signalbird import SignalbirdManagement

admin = SignalbirdManagement(domain_key=os.environ["SIGNALBIRD_DOMAIN_KEY"])

# Deploy betiğinde: yeni ortam için proje ve kanal aç
created = admin.create_radio_project({"name": "ödeme-servisi"})
secret = created.data["secret"]     # YALNIZ burada döner

admin.create_radio_channel(created.data["project"]["id"], {
    "key": "odeme",
    "name": "Ödeme",
    "level": "critical",
    "notify_push": True,
    "quiet_from": 0,
    "quiet_to": 7,
})

# Sohbet gelen kutusu botu
inbox = admin.list_conversations({"status": "open"})

for conversation in inbox.data["data"]:
    admin.reply(conversation["id"], {"body": "Merhaba! Birazdan dönüyoruz."})
    admin.set_conversation_status(conversation["id"], "pending")

Anahtarınızın radio:*, chat:*, apps:* kapsamlarını taşıması gerekir. Ayrıntı: Yönetim dokümanı.


Sonuç biçimi

Her metot aynı zarfı döner. Result bir dict'tir ve öznitelik erişimi de sunar:

python
result = sb.send_email({...})

result.ok         # bool
result.status     # int
result.data       # çözülmüş JSON gövde
result.code       # hata kodu (başarıda None)
result.message

result["data"]    # aynı şey

throw_on_error=True iken SignalbirdError fırlatılır (code, status, body taşır).

Ziyaretçi kimliği doğrulaması - identityHash

Sohbet widget'ı ve uygulama SDK'sı oturum açmış kullanıcıyı external_id ile tanıtır. Açık anahtar sayfada durduğu için Signalbird bu kimliği ancak yanında sizin sunucunuzda üretilmiş geçerli bir hash varsa güvenilir sayar (2.7.0). Hash olmadan ziyaretçi kişi kaydına bağlanmaz, cihazı o kullanıcıya push hedefi olarak bağlanmaz ve kanal ajanının araçları kimliği yalnız visitor.unverified altında görür.

python
from signalbird import signalbird

hash = signalbird().identity_hash(str(user.id))

Gizli anahtarı zaten tutan Telsiz istemcisinin üzerindedir; ayrı kurulum gerekmez. Algoritma (her dilde aynı çıktı):

text
identity_hash = hex( HMAC-SHA256( hex(SHA-256(sb_secret_live_…)), external_id ) )

Anahtar gizli anahtarın SHA-256 özetidir (sunucu anahtarı düz saklamaz). Kullanım: Widget - Kimlik doğrulaması, Uygulama SDK'sı.

Sürüm bildirimi

SDK her istekte sürümünü bildirir: X-Signalbird-Sdk: python/<sürüm>. Yeni bir sürüm yayınlandığında API yanıtında Signalbird-Sdk-Status: outdated (ya da artık desteklenmiyorsa unsupported) döner ve SDK süreç başına bir kez uyarı yazar (signalbird logger (logging)). Uyarı hata değildir: isteğin sonucu değişmez.

Panelde Domainler → [domain] → Anahtarlar her anahtarın hangi SDK sürümüyle kullanıldığını gösterir; eski sürümdeki anahtarlar için panelin tepesinde bir uyarı şeridi ve bildirim çıkar. Güncellemek için:

bash
pip install -U signalbird

Hata kodları

Kod Anlamı
NO_KEY / WRONG_KEY_TYPE Kurulum anında yakalanır
INVALID_KEY Anahtar yok, yanlış ya da proje pasif
API_KEY_SCOPE Anahtarda gereken kapsam yok
MODULE_DISABLED Paketinizde bu modül yok
LIMIT_REACHED Aylık limitiniz doldu
VALIDATION_ERROR 422
NETWORK_ERROR / TIMEOUT Ağ (status 0)

Davranış kuralları

  • Sessiz hata varsayılandır. Signalbird erişilemezse ok: False döner, istisna fırlatılmaz - log göndermek uygulamanızın asıl işi değildir.
  • Retry yoktur. Aynı iletiyi iki kez göndermek hiç göndermemekten pahalıdır; yeniden deneme kararı sizindir.
  • Zaman aşımı Telsiz'de 5 sn, Gönderim ve Yönetim'de 15 sn.
  • None alanlar gövdeden atılır: "gönderilmedi" ile "null yapıldı" aynı şey değildir.