pip install signalbirdsignalbird - 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
pip install signalbirdPython 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ı |
# .env
SIGNALBIRD_DOMAIN_KEY=sb_secret_live_xxxxxxxxxxxxxxxxxxxxxxxx
SIGNALBIRD_SOURCE=api-01
SIGNALBIRD_DOMAIN_KEY=sb_xxxxxxxxxxxxxxxxxxxxxxxxTelsiz - log ve olay
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:
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
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
# 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
@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}")
raiseGönderim - e-posta, SMS, push
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)
classalanı hukuki bir kapıdır:transactional(işlemsel) ya dacommercial(ticari). Varsayılanı yoktur - kararı SDK veremez.
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
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
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
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()okuyupjson.dumpsile 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
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:
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ı şeythrow_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.
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ı):
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:
pip install -U signalbirdHata 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: Falsedö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.
Nonealanlar gövdeden atılır: "gönderilmedi" ile "null yapıldı" aynı şey değildir.