- Работает на любых библиотеках такие как discord.js, seyfert, eris...
- Нет поддержки плеера, но готовая реализация есть тут
- Реализация Voice Gateway Version 8
(WebSocket + UDP + SRTP + Opus + Sodium)+ End-to-End Encryption (DAVE Protocol) - Полная реализация SRTP:
aead_aes256_gcm,xchacha20_poly1305(через библиотеки) - Адаптивная система отправки пакетов, без полноценного WebRTP ничего толкового не сделать!
- Работает с готовыми Ogg/Opus фреймами!
- Требуется FFmpeg, он отвечает за аудио!
- Работает даже при сильном event loop lag
- Есть свой парсер Ogg/Opus для получения чистого opus!
- Есть возможность переиспользовать аудио без конвертации
- Есть поддержка длинных видео, Live видео пока сыровато.
Warning
Это компонент из другого проекта UnTitles, данный модуль предоставляется как есть!
Tip
Вам потребуется AudioPlayer!!! Без него отправка аудио фреймов будет затруднительна
Рекомендуется отправлять аудио фреймы через 20 ms от прошло фрейма
npm install discord-voice-clientimport{BufferedAudioResource,PipeAudioResource,VoiceConnection}from"discord-voice-client";import{TaskCycle}from"snpk-cycle";// Проще из-за нативной поддержки adaptersconstadapter=message.guild.voiceAdapterCreator;constconfig={self_deaf: true,self_mute: false,guild_id: guild.id,channel_id: message.voice.channel.id};// Голосовое подключение готовоconstvoice=newVoiceConnection(config,adapter);// Debugvoice.websocket.on("debug",console.log);voice.websocket.on("warn",console.log);// Буферезированный аудио потокconstbufferedAudio=newBufferedAudioResource("urlOrPathFile",{seek: 10,filters: null});// Прямой аудио поток из ffmpeg с буфером в 14 секconstpipeAudio=newPipeAudioResource("urlOrPathFile",{seek: 10,filters: null});// Как отправлять циклично пакетыclassSender<TextendsVoiceConnection>extendsTaskCycle<T>{publicconstructor(){super({// Время до следующего прогона циклаduration: 20,drift: false,// Функция проверкиfilter: (item)=>item.ready,// Функция отправки аудио фреймаexecute: (connection)=>{connection.packet=audio.packet}});};};// Отправка пакетовvoice.packet=audio.packet;- Реализация voiceAdapterCreator
import{GatewayVoiceServerUpdateDispatchData,GatewayVoiceStateUpdateDispatchData}from"discord-api-types/v10";import{DiscordClient}from"../client";// Класс вашего клиента/** * @description Класс для взаимодействия с клиентским websocket'ом * @class VoiceManager */exportclassVoiceManager<ClientextendsDiscordClient>{/** * @description Коллекция адаптеров для общения голоса с клиентским websocket'ом * @readonly * @private */publicreadonlyadapters=newMap<string,DiscordGatewayAdapterLibraryMethods>();/** * @description Копия клиента * @private */privateclient: Client;/** * @description ID осколка * @private */privateshardID: number;/** * @description Создание класса * @param client - Класс клиента */publicconstructor(client: Client){this.client=client;};/** * @description Адаптер состояния голоса для этой гильдии, который можно использовать с `@discordjs/voice` для воспроизведения звука в голосовых и сценических каналах. * @public */publicvoiceAdapterCreator=(guildID: string): DiscordGatewayAdapterCreator=>{// Если нет ID осколкаif(!this.shardID)this.shardID=this.client.gateway.calculateShardId(guildID);returnmethods=>{this.adapters.set(guildID,methods);return{sendPayload: (data)=>{this.client.gateway.send(this.shardID,data);returntrue;},destroy: ()=>{this.adapters.delete(guildID);}};};};/** * @description Функция обновления данных о подключении к голосовому каналу * @param payload - Данные голосового состояния */publiconVoiceServer=(payload: GatewayVoiceServerUpdateDispatchData)=>{this.adapters.get(payload.guild_id)?.onVoiceServerUpdate(payload);};/** * @description Функция обновления данных о текущем голосовом состоянии * @param payload - Данные голосового состояния */publiconVoiceStateUpdate=(payload: GatewayVoiceStateUpdateDispatchData)=>{this.adapters.get(payload.guild_id)?.onVoiceStateUpdate(payload);};}/** * @description Шлюз Discord Адаптер, шлюза Discord. * @interface DiscordGatewayAdapterLibraryMethods */exportinterfaceDiscordGatewayAdapterLibraryMethods{/** * @description Call this when the adapter can no longer be used (e.g. due to a disconnect from the main gateway) */destroy(): void;/** * @description Call this when you receive a VOICE_SERVER_UPDATE payload that is relevant to the adapter. * @param data - The inner data of the VOICE_SERVER_UPDATE payload */onVoiceServerUpdate(data: GatewayVoiceServerUpdateDispatchData): void;/** * @description Call this when you receive a VOICE_STATE_UPDATE payload that is relevant to the adapter. * @param data - The inner data of the VOICE_STATE_UPDATE payload */onVoiceStateUpdate(data: GatewayVoiceStateUpdateDispatchData): void;}/** * @description Методы, предоставляемые разработчиком адаптера Discord Gateway для DiscordGatewayAdapter. * @interface DiscordGatewayAdapterImplementerMethods */exportinterfaceDiscordGatewayAdapterImplementerMethods{/** * @description Это будет вызвано voice, когда адаптер можно будет безопасно уничтожить, поскольку он больше не будет использоваться. */destroy(): void;/** * @description Реализуйте этот метод таким образом, чтобы данная полезная нагрузка отправлялась на основное соединение Discord gateway. * @param payload - Полезная нагрузка для отправки на основное соединение Discord gateway * @returns `false`, если полезная нагрузка определенно не была отправлена - в этом случае голосовое соединение отключается */sendPayload(payload: any): boolean;}/** * Функция, используемая для создания адаптеров. Она принимает параметр methods, содержащий функции, которые * могут быть вызваны разработчиком при получении новых данных по его шлюзовому соединению. В свою очередь, * разработчик вернет некоторые методы, которые может вызывать библиотека - например, для отправки сообщений на * шлюз или для подачи сигнала о том, что адаптер может быть удален. * @type DiscordGatewayAdapterCreator */exporttypeDiscordGatewayAdapterCreator=(methods: DiscordGatewayAdapterLibraryMethods)=>DiscordGatewayAdapterImplementerMethods;- Как использовать реализацию voiceAdapterCreator
/** * @description Реализация клиента discord * @class DiscordClient */exportclassDiscordClientextendsClient{/** * @description Класс для общения с websocket * @public */publicvoice=newVoiceManager(this);//...}// Создадим события для отправки данных в adapterimport{createEvent}from'seyfert';// VOICE_SERVER_UPDATEexportdefaultcreateEvent({data: {name: 'voiceServerUpdate'},asyncrun(packet,client){// Отправляем данные в adapterreturnclient.voice.onVoiceServer({ ...packet,guild_id: packet.guildId});}});// VOICE_STATE_UPDATEexportdefaultcreateEvent({data: {name: 'voiceStateUpdate'},asyncrun(state,client){constpayload=state[0];// Если нет данныхif(!payload)return;// Отправляем данные в adapterclient.voice.onVoiceStateUpdate({session_id: payload.sessionId,channel_id: payload.channelId,guild_id: payload.guildId,user_id: payload.userId,self_stream: payload.selfStream,self_video: payload.selfVideo,self_mute: payload.selfMute,self_deaf: payload.selfDeaf,request_to_speak_timestamp: payload.requestToSpeakTimestamp,deaf: payload.deaf,mute: payload.mute,suppress: payload.suppress,member: null});}});- Вот теперь можно отправлять пакеты под seyfert
import{BufferedAudioResource,PipeAudioResource,VoiceConnection,SyncCycle}from"discord-voice-client";import{TaskCycle}from"snpk-cycle";constadapter=client.voice.voiceAdapterCreator(guild.id);constconfig={self_deaf: true,self_mute: false,guild_id: ctx.guildId,channel_id: ctx.member.voice("cache").id};// Голосовое подключение готовоconstvoice=newVoiceConnection(config,adapter);// Буферезированный аудио потокconstbufferedAudio=newBufferedAudioResource("urlOrPathFile",{seek: 10,filters: null});// Прямой аудио поток из ffmpeg с буфером в 14 секconstpipeAudio=newPipeAudioResource("urlOrPathFile",{seek: 10,filters: null});// Как отправлять циклично пакетыclassSender<TextendsVoiceConnection>extendsTaskCycle<T>{publicconstructor(){super({// Время до следующего прогона циклаduration: 20,drift: false,// Функция проверкиfilter: (item)=>item.ready,// Функция отправки аудио фреймаexecute: (connection)=>{// Отправка пакетовconnection.packet=audio.packet}});};};