İçeriğe geç
N'oluyo
Gateway

Ses

Son güncelleme:

Botun bir ses kanalına girip müzik, duyuru ya da ses efekti yayınlayabilir. Ses dosyası ya da akış senin makinende çözülür ve doğrudan medya sunucusuna gönderilir; N'oluyo'nun API sunucularından geçmez.

Önemli: Bir bot seste yalnızca yayın yapar, kimseyi duyamaz. Botun aldığı token'da canSubscribe: false yazılıdır ve medya sunucusu botun hiçbir ses, görüntü ya da veri izine abone olmasına izin vermez. Kodunda abone olmayı denesen bile tek bir ses karesi gelmez.

Akış

Ses bağlantısı iki adımda kurulur: önce gateway'den ses kanalına girmek istediğini söylersin, sonra gelen adres ve token ile LiveKit odasına bağlanırsın.

  1. Gateway bağlantında op 4 gönderirsin (intents: 0 yeterlidir; ses için ek bir intent gerekmez):

JSON
{
  "op": 4,
  "d": {
    "guild_id": "21531879014400001",
    "channel_id": "21531879014400099",
    "self_mute": false
  }
}
  1. İzinlerin tamamsa ve sınırlar aşılmadıysa aynı oturuma VOICE_SERVER_UPDATE olayı gelir:

JSON
{
  "guild_id": "21531879014400001",
  "channel_id": "21531879014400099",
  "endpoint": "wss://noluyo.net/rtc",
  "token": "<10 dakikalık LiveKit token'ı>",
  "bitrate": 96000
}
  1. Bir kural ya da sınır engellerse bağlantı kapanmaz; VOICE_JOIN_ERROR olayı gelir:

JSON
{
  "guild_id": "21531879014400001",
  "channel_id": "21531879014400099",
  "code": "channel_bot_limit",
  "message": "This voice channel already has the maximum number of bots."
}
  1. Kanaldan ayrılmak için önce LiveKit odasından kop, ardından channel_id: null ile op 4 gönder:

JSON
{
  "op": 4,
  "d": {
    "guild_id": "21531879014400001",
    "channel_id": null
  }
}

Bir bot her sunucuda aynı anda tek bir ses kanalında olabilir; farklı sunucularda ise aynı anda birden çok ses kanalında bulunabilir. Odaya girerken aldığın token 10 dakika geçerlidir; bağlandıktan sonra açık kalan odalarda LiveKit bağlantıyı kendisi yeniler, 10 dakika içinde hiç bağlanmazsan token geçersiz olur.

Gereken izinler

Ses kanalına girip yayın yapmak için botun o kanalda üç izne ihtiyacı vardır:

  • ViewChannel (1024) — kanalı görme

  • Connect (1048576) — ses kanalına bağlanma

  • Speak (2097152) — seste yayın yapma

Üçünün toplamı 3146752 eder; ekleme bağlantısında izin=3146752 kullanabilirsin.

Bot odaya girdikten sonra izinleri değişirse şunlar olur:

  • Speak kaldırılırsa (veya bot zaman aşımına alınırsa) bot odada kalır ama yayın izni anında kapatılır; sesi kesilir.

  • Connect veya ViewChannel kaldırılırsa bot odadan hemen çıkarılır.

Botlar yalnızca sunuculardaki ses kanallarına (type: 2) girebilir; DM aramalarına katılamaz, kamera ya da ekran paylaşımı açamaz ve yalnızca tek bir mikrofon ses izi yayınlayabilir.

Odaya bağlanmak ve dosya çalmak

Aşağıdaki örnek bir ses dosyasını ffmpeg ile 48 kHz stereo PCM'e çevirip odaya yayınlar ve dosya bitince kanaldan ayrılır. Yanına Gateway sayfasındaki gateway.mjs dosyasını koy.

Önce LiveKit'in Node paketini ve makinenin paket yöneticisinden ffmpeg aracını kur:

Bash
npm install @livekit/rtc-node@1.2.1

Dosyayı cal.mjs adıyla kaydet:

cal.mjs
import { spawn } from "node:child_process";
import { AudioFrame, AudioSource, LocalAudioTrack, Room, TrackPublishOptions, TrackSource, dispose } from "@livekit/rtc-node";
import { connect } from "./gateway.mjs";

const { GUILD_ID, VOICE_CHANNEL_ID, AUDIO_FILE } = process.env;
const RATE = 48000;
const CHANNELS = 2;
const SAMPLES = 960; // 20 ms
const FRAME_BYTES = SAMPLES * CHANNELS * 2;

const gateway = await connect({
  intents: 0, // Ses için intent gerekmez.
  onEvent(type, d) {
    if (type === "READY") gateway.send(4, { guild_id: GUILD_ID, channel_id: VOICE_CHANNEL_ID, self_mute: false });
    if (type === "VOICE_SERVER_UPDATE") play(d).catch(fail);
    if (type === "VOICE_JOIN_ERROR") fail(new Error(`${d.code}: ${d.message}`));
  },
});

function fail(err) {
  console.error(err.message);
  process.exit(1);
}

async function play({ endpoint, token, bitrate }) {
  const room = new Room();
  await room.connect(endpoint, token, { autoSubscribe: false, dynacast: false });

  const source = new AudioSource(RATE, CHANNELS);
  const track = LocalAudioTrack.createAudioTrack("muzik", source);
  const options = new TrackPublishOptions({
    source: TrackSource.SOURCE_MICROPHONE,
    audioEncoding: { maxBitrate: BigInt(bitrate) }, // Kanalın ses kalitesini aşma.
  });
  await room.localParticipant.publishTrack(track, options);

  const ffmpeg = spawn("ffmpeg", ["-v", "error", "-i", AUDIO_FILE, "-f", "s16le", "-ar", String(RATE), "-ac", String(CHANNELS), "-"], {
    stdio: ["ignore", "pipe", "inherit"],
  });
  let rest = Buffer.alloc(0);
  for await (const chunk of ffmpeg.stdout) {
    rest = Buffer.concat([rest, chunk]);
    while (rest.length >= FRAME_BYTES) {
      const pcm = rest.subarray(0, FRAME_BYTES);
      const samples = new Int16Array(pcm.buffer.slice(pcm.byteOffset, pcm.byteOffset + FRAME_BYTES));
      await source.captureFrame(new AudioFrame(samples, RATE, CHANNELS, SAMPLES));
      rest = rest.subarray(FRAME_BYTES);
    }
  }

  await source.waitForPlayout();
  await track.close();
  await room.disconnect();
  gateway.send(4, { guild_id: GUILD_ID, channel_id: null });
  await dispose();
  process.exit(0);
}

Çalıştırmak için:

Bash
export BOT_TOKEN='kopyaladigin_token'
export GUILD_ID='sunucu_id'
export VOICE_CHANNEL_ID='ses_kanali_id'
export AUDIO_FILE='./ornek.mp3'
node cal.mjs

Ses dosyası senin makinende okunur; N'oluyo'nun API sunucusuna yüklenmez ve bizde saklanmaz. Yayınladığın sesin kullanım ve telif haklarına sahip olman senin sorumluluğundadır.

Sınırlar

İnsanların sesli sohbetini korumak için bot ses bağlantıları aşağıdaki sınırlarla çalışır:

AnahtarSınırAçıklama
channel2Bir ses kanalında aynı anda bulunabilecek en çok bot
guild4Bir sunucudaki tüm ses kanallarında aynı anda bulunabilecek en çok bot
app50Bir uygulamanın tüm sunucularda aynı anda tutabileceği ses bağlantısı
platform100Tüm botların ortak eşzamanlı ses bağlantısı tavanı (NOLUYO_BOT_VOICE_MAX)
alone5Kanalda hiç insan kalmadığında botun çıkarılmadan önce beklediği dakika
op45 / 20Bot ve sunucu başına 20 saniyede gönderilebilecek op 4 çerçevesi
tracks10 / 60Bot ve oda başına dakikada yapılabilecek ses izi yayını; aşılırsa bot odadan çıkarılır
bitrate96Botlara verilen en yüksek ses bit hızı (kbps); kanal daha düşükse kanalın değeri gelir

op 4 hız sınırını aşarsan bağlantı kapanmaz, RATE_LIMITED (opcode: 4) olayı gelir; retry_after saniye bekleyip yeniden gönder. Kanalda tek başına 5 dakika kalan bot kendiliğinden sesten çıkarılır, böylece boş odalar ortak kapasiteyi doldurmaz.

Bot odada ne görür, kişi ne görür

Botun gördükleri: Bot ses alamaz ve kaydedemez. Ancak LiveKit odasında katılımcı olduğu için o odada o an kimin bağlı olduğunu (kullanıcı kimliği ve görünen ad), susturulmuş olup olmadığını ve kimin ne zaman konuştuğunu (ses içeriği olmadan yalnızca aktif konuşmacı bilgisini) görür. Ekleme ekranı Connect izni isteyen her botta sunucu yöneticisine bunu açıkça söyler.

Kişilerin gördükleri:

  • Web, masaüstü (oyun içi arayüz dahil) ve Android'de bot ses listesinde ve arama ekranında BOT etiketiyle görünür; etiketin üstüne gelindiğinde ya da bilgi penceresinde botun yalnızca yayın yaptığı ve kimseyi duyamadığı yazar.

  • Bot, ses kanalının kişi sayacına (1 / 5 gibi) dahil edilmez ve dolu bir kanala insan girmesini engellemez.

  • İsteyen her kullanıcı botun ses seviyesini kendi cihazında kısıp açabilir ya da botu yalnızca kendisi için susturabilir.

  • Botun satırında kulaklık kapalı (self_deaf: true) durumu her zaman açıktır; bu bir hata değil, botun kimseyi duyamadığının teknik karşılığıdır.

Yönetici ne yapabilir

Sunucuda yetkisi olan yöneticiler seste duran bir bot üzerinde üç işlem yapabilir:

  • Sunucuda sustur (MuteMembers): Botun yayın izni hemen kapatılır, sesi kesilir. Susturma kaldırılınca yeniden yayın yapabilir. Sunucuda sağırlaştır seçeneği botlar için gizlidir çünkü bot zaten hiçbir zaman duyamaz.

  • Bağlantıyı kes (MoveMembers): Bot LiveKit odasından çıkarılır (Disconnected) ve ses satırı silinir. Botun kendi kendine hemen geri girmemeli; yalnızca bir kullanıcı komutu istediğinde yeniden op 4 göndermelidir.

  • Başka ses kanalına taşı (MoveMembers): Bot eski odadan çıkarılır ve botun açık gateway oturumuna yeni kanal için yeni bir VOICE_SERVER_UPDATE gönderilir. cal.mjs gibi VOICE_SERVER_UPDATE dinleyen bir bot eski Room nesnesini kapatıp gelen yeni endpoint ve token ile yeni odaya bağlanarak yayını sürdürebilir.

Botun token'ı yenilenirse, uygulaması durdurulursa ya da bot sunucudan çıkarılırsa açık tüm ses bağlantıları anında kesilir.

Hata kodları (VOICE_JOIN_ERROR)

op 4 isteği reddedildiğinde VOICE_JOIN_ERROR olayındaki code alanı nedenini söyler:

KodAnlamıNe yapmalı
unknown_guildSunucu yok ya da bot bu sunucunun üyesi değil.Sunucu kimliğini kontrol et, tekrar deneme.
unknown_channelKanal bu sunucuda yok.Kanal kimliğini kontrol et.
invalid_channel_typeKanal bir ses kanalı (type: 2) değil.Metin kanalı ya da kategori yerine ses kanalı seç.
missing_accessBotun bu kanalı görme (ViewChannel) izni yok.Kanal izinlerini kontrol et.
missing_permissionsBotun kanalda Connect izni yok ya da zaman aşımında.Botun rolüne Connect ve Speak ver.
channel_bot_limitBu ses kanalında zaten 2 bot var.Başka bir ses kanalı seç ya da diğer bot çıkana dek bekle.
guild_bot_limitBu sunucuda zaten 4 bot seste.Sunucudaki diğer botlardan biri sesten çıkana dek bekle.
app_voice_limitUygulamanın eşzamanlı 50 ses bağlantısı doldu.Boşta kalan bağlantıları kapat.
bot_voice_fullPlatform genelindeki bot ses kapasitesi o an dolu.Biraz bekleyip yeniden dene, sık döngüye girme.
voice_unavailableSes hizmeti o an kullanılamıyor ya da geçici bir hata oluştu.Birkaç saniye bekleyip yeniden dene.

Bu sayfayı yapay zekaya verirsen

Bir yapay zeka aracından N'oluyo için ses veya müzik botu yazmasını isterken bu sayfayı ve Gateway sayfasını birlikte ver. Şu kuralları özellikle hatırlat:

  • Bot token'ı koda yazılmaz, process.env.BOT_TOKEN ile ortam değişkeninden okunur.

  • Ses kanalına girmek HTTP isteği değildir; gateway üzerinden op 4 gönderilir ve VOICE_SERVER_UPDATE beklenir. POST /channels/{id}/voice kişi rotasıdır ve bot token'ını 403 bot_forbidden ile reddeder.

  • Bot seste kimseyi duyamaz: setSubscribed(true) işe yaramaz, ses tanıma veya kayıt botu istendiğinde "N'oluyo'da botlar seste kimseyi duyamaz, bu özellik yapılamaz" denmelidir.

  • TrackPublishOptions içinde source: TrackSource.SOURCE_MICROPHONE ve maxBitrate: BigInt(bitrate) kullanılmalıdır; kamera veya ekran kaynağı reddedilir.

Sırada ne var