nuget.org ↗
Kurulumdotnet add package Signalbird.Sdk

Signalbird.Sdk - .NET / ASP.NET Core

Üç yüzey taşır: Telsiz (log), Gönderim (e-posta/SMS/push) ve Yönetim (proje, sohbet gelen kutusu, uygulama).

Kurulum

bash
dotnet add package Signalbird.Sdk

net8.0 ve netstandard2.1 hedefler. Tek dış bağımlılık DI uzantısı içindir (Microsoft.Extensions.Http) ve ASP.NET Core uygulamasında zaten yüklüdür; çekirdek istemciler onu kullanmaz - konsol uygulaması ya da Azure Function gereksiz paket çekmesin.

Kayıt (DI)

csharp
// Program.cs
builder.Services.AddSignalbird(options =>
{
    options.DomainKey = builder.Configuration["Signalbird:RadioKey"]!;   // sb_secret_live_…
    options.Source = "api-01";
});

builder.Services.AddSignalbirdMessaging(options =>
    options.DomainKey = builder.Configuration["Signalbird:DomainKey"]!);    // sb_…

builder.Services.AddSignalbirdManagement(options =>
    options.DomainKey = builder.Configuration["Signalbird:DomainKey"]!);

Üçü de IHttpClientFactory üzerinden kurulur. Her istemcinin kendi HttpClient'ını new'lemesi soket tükenmesinin klasik sebebidir ve .NET'te en sık yapılan hatadır - SDK bunu sizin için doğru yapar.

DI kullanmıyorsanız:

csharp
var radio = new SignalbirdClient(new SignalbirdOptions { DomainKey = "sb_secret_live_…" });

Telsiz - log ve olay

csharp
public class PaymentService(SignalbirdClient signalbird)
{
    public async Task ChargeAsync(Order order, CancellationToken ct)
    {
        try
        {
            await _gateway.ChargeAsync(order, ct);

            await signalbird.InfoAsync("odeme", $"Sipariş #{order.Id} başarılı", ct: ct);
        }
        catch (Exception error)
        {
            await signalbird.CriticalAsync("critical", error.Message,
                new Dictionary<string, object?>
                {
                    ["order"] = order.Id,
                    ["gateway"] = "iyzico",
                }, ct);

            throw;   // Log göndermek hatayı yutmaz
        }
    }
}

Beş seviye: DebugAsync, InfoAsync, WarnAsync, ErrorAsync, CriticalAsync. Seviye vermeden LogAsync çağırırsanız kanalın kendi varsayılanı geçerlidir.

Toplu gönderim

csharp
await signalbird.BatchAsync(new[]
{
    new LogEvent { Channel = "deploy", Message = "v2.4.0 yayında", Level = SignalbirdLevel.Info },
    new LogEvent { Channel = "critical", Message = "kuyruk doldu" },
});

En fazla 100 kayıt; kısmi başarı normaldir ve başarısız satırlar yeniden denenmez.


Gönderim - e-posta, SMS, push

csharp
var result = await messaging.SendEmailAsync(new
{
    to = "ahmet@example.com",
    @class = "transactional",     // `class` ayrılmış sözcük - @ ile kaçılır
    subject = "Siparişiniz yola çıktı",
    body = "<p>Kargo takip no: 1234</p>",
});

if (!result.Ok)
{
    logger.LogWarning("signalbird: {Code} - {Message}", result.Code, result.Message);
}

class alanı zorunludur ve varsayılanı yoktur: transactional (işlemsel) ya da commercial (ticari). Bu hukuki bir kapıdır, kararı SDK veremez.

csharp
await messaging.SendSmsAsync(new { to = "+905551112233", @class = "transactional", body = "Kodunuz: 123456" });
await messaging.PreviewSmsAsync("Merhaba, siparişiniz hazır.");   // kota harcamaz
await messaging.SendPushAsync(new { to = "contact:42", @class = "transactional", subject = "Sipariş", body = "Yola çıktı" });

Kişiler

csharp
await messaging.ListContactsAsync(new Dictionary<string, object?> { ["q"] = "ahmet" });
await messaging.CreateContactAsync(new { email = "ahmet@example.com", first_name = "Ahmet" });
await messaging.UpdateContactAsync(contactId, new { phone = "+905551112233" });
await messaging.DeleteContactAsync(contactId);

// 1000'lik parçalar, SIRAYLA
await messaging.BulkContactsAsync(rows, new Dictionary<string, object?>
{
    ["list_id"] = 42,
    ["consent_source"] = "web_form",
});

Kampanyalar

csharp
await messaging.CreateCampaignAsync(new
{
    name = "Ağustos bülteni",
    channel = "email",
    list_id = 42,
    subject = "Bu ay neler oldu?",
    body = "<h1>Merhaba</h1>",
});

// Sayfa sayfa gezer; tüm listeyi belleğe almaz
await foreach (var message in messaging.IterateCampaignMessagesAsync(batchId))
{
    Reconcile(message.GetProperty("external_ref").GetString());
}

Webhook doğrulama

csharp
// Program.cs - ham gövdeyi okuyabilmek için
app.Use(async (context, next) =>
{
    context.Request.EnableBuffering();
    await next();
});

// Controller
[HttpPost("/webhooks/signalbird")]
public async Task<IActionResult> Handle()
{
    Request.Body.Position = 0;
    using var reader = new StreamReader(Request.Body);
    var rawBody = await reader.ReadToEndAsync();

    // HAM gövde: modeli deserialize edip yeniden serialize etmek imzayı bozar
    if (!Webhook.Verify(rawBody, Request.Headers["X-Signalbird-Signature"], _secret))
    {
        return Unauthorized();
    }

    return Ok();
}

Yönetim - kendi projenizi kurun

csharp
// Ortam kurulumu (deploy adımı ya da yönetim komutu)
var created = await management.CreateModuleKeyAsync("logger", new Dictionary<string, object?> { ["title"] = "Ödeme hataları" });

// `secret` YALNIZ burada döner; sunucuda yalnız özeti saklanır
var secret = created.Data?.GetProperty("secret").GetString();
var projectId = created.Data?.GetProperty("project").GetProperty("id").GetInt32();

await management.CreateRadioChannelAsync(projectId!, new
{
    key = "odeme",
    name = "Ödeme",
    level = "critical",
    notify_push = true,
    quiet_from = 0,
    quiet_to = 7,
});

// Sohbet gelen kutusu botu
var inbox = await management.ListConversationsAsync(
    new Dictionary<string, object?> { ["status"] = "open" });

await management.ReplyAsync(conversationId, new { body = "Merhaba! Birazdan dönüyoruz." });
await management.SetConversationStatusAsync(conversationId, "pending");

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


Sonuç biçimi

csharp
public sealed class SbResult
{
    public bool Ok { get; }
    public int Status { get; }
    public JsonElement? Data { get; }
    public string? Code { get; }
    public string? Message { get; }

    public T? Into<T>();     // gövdeyi tipinize çözer
}
csharp
var result = await management.GetAppAsync(id);

var app = result.Into<AppRecord>();

ThrowOnError = true iken SignalbirdException fırlatılır (Code, Status, Body).

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.

csharp
// SignalbirdClient DI'dan
var hash = signalbird.IdentityHash(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: dotnet/<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 (Console.Error). 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
dotnet add package Signalbird.Sdk

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.
  • Retry yoktur. Yeniden deneme kararı sizindir; Polly kullanıyorsanız IHttpClientFactory kaydına kendi politikanızı ekleyin.
  • Alan adları API ile birebir (snake_case) - SDK yeniden adlandırmaz.
  • CancellationToken her metodun son parametresidir.