import { NoSubscriberBehavior, StreamType, createAudioPlayer, createAudioResource, entersState, AudioPlayerStatus, VoiceConnectionStatus, joinVoiceChannel, getVoiceConnection, } from '@discordjs/voice'; import { GatewayIntentBits } from 'discord-api-types/v10'; import { Client, Events, ActivityType } from 'discord.js'; import prism_media from 'prism-media'; const { FFmpeg } = prism_media; const device = "VoiceMeeter VAIO3 Output (VB-Audio VoiceMeeter VAIO3)", maxTransmissionGap = 500, type = "dshow"; const player = createAudioPlayer({ behaviors: { noSubscriber: NoSubscriberBehavior.Play, maxMissedFrames: Math.round(maxTransmissionGap / 20), }, }); function attachRecorder() { player.play( createAudioResource( new FFmpeg({ args: [ '-analyzeduration', '0', '-loglevel', '0', '-f', type, '-i', type === 'dshow' ? `audio=${device}` : device, '-acodec', 'libopus', '-f', 'opus', '-ar', '48000', '-ac', '2', ], }), { inputType: StreamType.OggOpus, }, ), ); console.log('Attached recorder - ready to go!'); } player.on('stateChange', (oldState, newState) => { if (oldState.status === AudioPlayerStatus.Idle && newState.status === AudioPlayerStatus.Playing) { console.log('Playing audio output on audio player'); } else if (newState.status === AudioPlayerStatus.Idle) { console.log('Playback has stopped. Attempting to restart.'); attachRecorder(); } }); /** * * @param {any} channel * @returns {any} */ export async function connectToChannel(channel) { const connection = joinVoiceChannel({ channelId: channel.id, guildId: channel.guild.id, adapterCreator: channel.guild.voiceAdapterCreator, }); try { await entersState(connection, VoiceConnectionStatus.Ready, 30_000); await connection.subscribe(player); return connection; } catch (error) { connection.destroy(); throw error; } } export async function getVoiceChannelFromID(client, channelID) { return client.channels.cache.get(channelID) } export async function checkIfConnectedToVC(guildId) { const connection = getVoiceConnection(guildId) console.log("Connection!", connection); return connection } export async function initDiscordBotClient(token, systemName, readyCallback) { const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.MessageContent], }); client.on(Events.ClientReady, () => { console.log('discord.js client is ready!'); attachRecorder(); client.user.setPresence({ activities: [{ name: `${systemName}`, type: ActivityType.Listening }], }); readyCallback(client); }); /* on event create // TODO - Implement methods for discord users to interact directly with the bots for realtime info (last talked ID/TG, etc.) client.on(Events.MessageCreate, async (message) => { if (!message.guild) return; console.log(`New Message:`, message.content); if (message.content === '-join') { const channel = message.member?.voice.channel; if (channel) { try { const connection = await connectToChannel(channel); connection.subscribe(player); await message.reply('Playing now!'); } catch (error) { console.error(error); } } else { await message.reply('Join a voice channel then try again!'); } } }); */ client.login(token); }