Ses
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.
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.
- Gateway bağlantında
op 4gönderirsin (intents: 0yeterlidir; ses için ek bir intent gerekmez):
{
"op": 4,
"d": {
"guild_id": "21531879014400001",
"channel_id": "21531879014400099",
"self_mute": false
}
}
- İzinlerin tamamsa ve sınırlar aşılmadıysa aynı oturuma
VOICE_SERVER_UPDATEolayı gelir:
{
"guild_id": "21531879014400001",
"channel_id": "21531879014400099",
"endpoint": "wss://noluyo.net/rtc",
"token": "<10 dakikalık LiveKit token'ı>",
"bitrate": 96000
}
- Bir kural ya da sınır engellerse bağlantı kapanmaz;
VOICE_JOIN_ERRORolayı gelir:
{
"guild_id": "21531879014400001",
"channel_id": "21531879014400099",
"code": "channel_bot_limit",
"message": "This voice channel already has the maximum number of bots."
}
- Kanaldan ayrılmak için önce LiveKit odasından kop, ardından
channel_id: nullileop 4gönder:
{
"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örmeConnect(1048576) — ses kanalına bağlanmaSpeak(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:
Speakkaldı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.ConnectveyaViewChannelkaldı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:
npm install @livekit/rtc-node@1.2.1
Dosyayı cal.mjs adıyla kaydet:
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:
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:
| Anahtar | Sınır | Açıklama |
|---|---|---|
channel | 2 | Bir ses kanalında aynı anda bulunabilecek en çok bot |
guild | 4 | Bir sunucudaki tüm ses kanallarında aynı anda bulunabilecek en çok bot |
app | 50 | Bir uygulamanın tüm sunucularda aynı anda tutabileceği ses bağlantısı |
platform | 100 | Tüm botların ortak eşzamanlı ses bağlantısı tavanı (NOLUYO_BOT_VOICE_MAX) |
alone | 5 | Kanalda hiç insan kalmadığında botun çıkarılmadan önce beklediği dakika |
op4 | 5 / 20 | Bot ve sunucu başına 20 saniyede gönderilebilecek op 4 çerçevesi |
tracks | 10 / 60 | Bot ve oda başına dakikada yapılabilecek ses izi yayını; aşılırsa bot odadan çıkarılır |
bitrate | 96 | Botlara 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 / 5gibi) 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 yenidenop 4gö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 birVOICE_SERVER_UPDATEgönderilir.cal.mjsgibiVOICE_SERVER_UPDATEdinleyen bir bot eskiRoomnesnesini kapatıp gelen yeniendpointvetokenile 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:
| Kod | Anlamı | Ne yapmalı |
|---|---|---|
unknown_guild | Sunucu yok ya da bot bu sunucunun üyesi değil. | Sunucu kimliğini kontrol et, tekrar deneme. |
unknown_channel | Kanal bu sunucuda yok. | Kanal kimliğini kontrol et. |
invalid_channel_type | Kanal bir ses kanalı (type: 2) değil. | Metin kanalı ya da kategori yerine ses kanalı seç. |
missing_access | Botun bu kanalı görme (ViewChannel) izni yok. | Kanal izinlerini kontrol et. |
missing_permissions | Botun kanalda Connect izni yok ya da zaman aşımında. | Botun rolüne Connect ve Speak ver. |
channel_bot_limit | Bu ses kanalında zaten 2 bot var. | Başka bir ses kanalı seç ya da diğer bot çıkana dek bekle. |
guild_bot_limit | Bu sunucuda zaten 4 bot seste. | Sunucudaki diğer botlardan biri sesten çıkana dek bekle. |
app_voice_limit | Uygulamanın eşzamanlı 50 ses bağlantısı doldu. | Boşta kalan bağlantıları kapat. |
bot_voice_full | Platform genelindeki bot ses kapasitesi o an dolu. | Biraz bekleyip yeniden dene, sık döngüye girme. |
voice_unavailable | Ses 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_TOKENile ortam değişkeninden okunur. - Ses kanalına girmek HTTP isteği değildir; gateway üzerinden
op 4gönderilir veVOICE_SERVER_UPDATEbeklenir.POST /channels/{id}/voicekişi rotasıdır ve bot token'ını403 bot_forbiddenile 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. TrackPublishOptionsiçindesource: TrackSource.SOURCE_MICROPHONEvemaxBitrate: BigInt(bitrate)kullanılmalıdır; kamera veya ekran kaynağı reddedilir.
Sırada ne var
- JavaScript kütüphanesi ve Python kütüphanesi:
bot.voice.joinile tek satırda ses yayını - Gateway:
op 4çerçevesinin gittiği WebSocket bağlantısı - Hatalar: tüm REST ve
VOICE_JOIN_ERRORhata kodları