diff --git a/packages/voice/.eslintignore b/packages/voice/.eslintignore
index 8be4eac61..cd4efd8e5 100644
--- a/packages/voice/.eslintignore
+++ b/packages/voice/.eslintignore
@@ -1,2 +1 @@
*.d.ts
-examples
diff --git a/packages/voice/README.md b/packages/voice/README.md
index 301d9a1f3..6e0121187 100644
--- a/packages/voice/README.md
+++ b/packages/voice/README.md
@@ -64,6 +64,10 @@ try installing another.
- [`FFmpeg`](https://ffmpeg.org/) (installed and added to environment)
- `ffmpeg-static`: ^4.2.7 (npm install)
+## Examples
+
+The [voice-examples][voice-examples] repository contains examples on how to use this package. Feel free to check them out if you need a nudge in the right direction.
+
## Links
- [Website][website] ([source][website-source])
@@ -99,3 +103,4 @@ nudge in the right direction, please don't hesitate to join our official [discor
[npm]: https://www.npmjs.com/package/@discordjs/voice
[related-libs]: https://discord.com/developers/docs/topics/community-resources#libraries
[contributing]: https://github.com/discordjs/discord.js/blob/main/.github/CONTRIBUTING.md
+[voice-examples]: https://github.com/discordjs/voice-examples
diff --git a/packages/voice/examples/README.md b/packages/voice/examples/README.md
deleted file mode 100644
index 81617f38e..000000000
--- a/packages/voice/examples/README.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# Examples
-
-| Example | Description |
-| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| [Basic](./basic) | A simple "Hello World" TypeScript example that plays an mp3 file. Notably, it works with discord.js v12 and so it also contains an example of creating an adapter |
-| [Radio Bot](./radio-bot) | A fun JavaScript example of what you can create using @discordjs/voice. A radio bot that plays output from your speakers in a Discord voice channel |
-| [Recorder](./recorder) | An example of using voice receive to create a bot that can record audio from users |
diff --git a/packages/voice/examples/UNLICENSE b/packages/voice/examples/UNLICENSE
deleted file mode 100644
index 68a49daad..000000000
--- a/packages/voice/examples/UNLICENSE
+++ /dev/null
@@ -1,24 +0,0 @@
-This is free and unencumbered software released into the public domain.
-
-Anyone is free to copy, modify, publish, use, compile, sell, or
-distribute this software, either in source code form or as a compiled
-binary, for any purpose, commercial or non-commercial, and by any
-means.
-
-In jurisdictions that recognize copyright laws, the author or authors
-of this software dedicate any and all copyright interest in the
-software to the public domain. We make this dedication for the benefit
-of the public at large and to the detriment of our heirs and
-successors. We intend this dedication to be an overt act of
-relinquishment in perpetuity of all present and future rights to this
-software under copyright law.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
-
-For more information, please refer to
diff --git a/packages/voice/examples/basic/README.md b/packages/voice/examples/basic/README.md
deleted file mode 100644
index 70dafdcdf..000000000
--- a/packages/voice/examples/basic/README.md
+++ /dev/null
@@ -1,91 +0,0 @@
-# Basic Example
-
-This example will demonstrate how to join a voice channel and play resources, with some best practice
-assistance on making sure you aren't waiting indefinitely for things to happen.
-
-To achieve this, the example sets some fairly arbitrary time constraints for things such as joining
-voice channels and audio becoming available.
-
-## Code snippet
-
-This code snippet doesn't include any comments for brevity. If you want to see the full source code,
-check the other files in this folder!
-
-```ts
-import { Client, VoiceChannel, Intents } from 'discord.js';
-import {
- joinVoiceChannel,
- createAudioPlayer,
- createAudioResource,
- entersState,
- StreamType,
- AudioPlayerStatus,
- VoiceConnectionStatus,
-} from '@discordjs/voice';
-import { createDiscordJSAdapter } from './adapter';
-
-const player = createAudioPlayer();
-
-function playSong() {
- const resource = createAudioResource('https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3', {
- inputType: StreamType.Arbitrary,
- });
-
- player.play(resource);
-
- return entersState(player, AudioPlayerStatus.Playing, 5e3);
-}
-
-async function connectToChannel(channel: VoiceChannel) {
- const connection = joinVoiceChannel({
- channelId: channel.id,
- guildId: channel.guild.id,
- adapterCreator: createDiscordJSAdapter(channel),
- });
-
- try {
- await entersState(connection, VoiceConnectionStatus.Ready, 30e3);
- return connection;
- } catch (error) {
- connection.destroy();
- throw error;
- }
-}
-
-const client = new Client({
- ws: { intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES, Intents.FLAGS.GUILD_VOICE_STATES] },
-});
-
-client.login('token here');
-
-client.on('ready', async () => {
- console.log('Discord.js client is ready!');
-
- try {
- await playSong();
- console.log('Song is ready to play!');
- } catch (error) {
- console.error(error);
- }
-});
-
-client.on('message', async (message) => {
- if (!message.guild) return;
-
- if (message.content === '-join') {
- const channel = message.member?.voice.channel;
-
- if (channel) {
- try {
- const connection = await connectToChannel(channel);
- connection.subscribe(player);
- message.reply('Playing now!');
- } catch (error) {
- console.error(error);
- }
- } else {
- message.reply('Join a voice channel then try again!');
- }
- }
-});
-```
diff --git a/packages/voice/examples/basic/adapter.ts b/packages/voice/examples/basic/adapter.ts
deleted file mode 100644
index 6e7b1bf0c..000000000
--- a/packages/voice/examples/basic/adapter.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-import { DiscordGatewayAdapterCreator, DiscordGatewayAdapterLibraryMethods } from '../../';
-import { Snowflake, Client, Guild, VoiceBasedChannel, Status, Events } from 'discord.js';
-import {
- GatewayDispatchEvents,
- GatewayVoiceServerUpdateDispatchData,
- GatewayVoiceStateUpdateDispatchData,
-} from 'discord-api-types/v9';
-
-const adapters = new Map();
-const trackedClients = new Set();
-
-/**
- * Tracks a Discord.js client, listening to VOICE_SERVER_UPDATE and VOICE_STATE_UPDATE events
- *
- * @param client - The Discord.js Client to track
- */
-function trackClient(client: Client) {
- if (trackedClients.has(client)) return;
- trackedClients.add(client);
- client.ws.on(GatewayDispatchEvents.VoiceServerUpdate, (payload: GatewayVoiceServerUpdateDispatchData) => {
- adapters.get(payload.guild_id)?.onVoiceServerUpdate(payload);
- });
- client.ws.on(GatewayDispatchEvents.VoiceStateUpdate, (payload: GatewayVoiceStateUpdateDispatchData) => {
- if (payload.guild_id && payload.session_id && payload.user_id === client.user?.id) {
- adapters.get(payload.guild_id)?.onVoiceStateUpdate(payload);
- }
- });
- client.on(Events.ShardDisconnect, (_, shardId) => {
- const guilds = trackedShards.get(shardId);
- if (guilds) {
- for (const guildID of guilds.values()) {
- adapters.get(guildID)?.destroy();
- }
- }
- trackedShards.delete(shardId);
- });
-}
-
-const trackedShards = new Map>();
-
-function trackGuild(guild: Guild) {
- let guilds = trackedShards.get(guild.shardId);
- if (!guilds) {
- guilds = new Set();
- trackedShards.set(guild.shardId, guilds);
- }
- guilds.add(guild.id);
-}
-
-/**
- * Creates an adapter for a Voice Channel.
- *
- * @param channel - The channel to create the adapter for
- */
-export function createDiscordJSAdapter(channel: VoiceBasedChannel): DiscordGatewayAdapterCreator {
- return (methods) => {
- adapters.set(channel.guild.id, methods);
- trackClient(channel.client);
- trackGuild(channel.guild);
- return {
- sendPayload(data) {
- if (channel.guild.shard.status === Status.Ready) {
- channel.guild.shard.send(data);
- return true;
- }
- return false;
- },
- destroy() {
- return adapters.delete(channel.guild.id);
- },
- };
- };
-}
diff --git a/packages/voice/examples/basic/basic-example.ts b/packages/voice/examples/basic/basic-example.ts
deleted file mode 100644
index 00222e956..000000000
--- a/packages/voice/examples/basic/basic-example.ts
+++ /dev/null
@@ -1,153 +0,0 @@
-import { Client, VoiceBasedChannel, VoiceChannel } from 'discord.js';
-import {
- joinVoiceChannel,
- createAudioPlayer,
- createAudioResource,
- entersState,
- StreamType,
- AudioPlayerStatus,
- VoiceConnectionStatus,
-} from '@discordjs/voice';
-import { createDiscordJSAdapter } from './adapter';
-import { GatewayIntentBits } from 'discord-api-types/v9';
-
-/**
- * In this example, we are creating a single audio player that plays to a number of voice channels.
- * The audio player will play a single track.
- */
-
-/**
- * Create the audio player. We will use this for all of our connections.
- */
-const player = createAudioPlayer();
-
-function playSong() {
- /**
- * Here we are creating an audio resource using a sample song freely available online
- * (see https://www.soundhelix.com/audio-examples)
- *
- * We specify an arbitrary inputType. This means that we aren't too sure what the format of
- * the input is, and that we'd like to have this converted into a format we can use. If we
- * were using an Ogg or WebM source, then we could change this value. However, for now we
- * will leave this as arbitrary.
- */
- const resource = createAudioResource('https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3', {
- inputType: StreamType.Arbitrary,
- });
-
- /**
- * We will now play this to the audio player. By default, the audio player will not play until
- * at least one voice connection is subscribed to it, so it is fine to attach our resource to the
- * audio player this early.
- */
- player.play(resource);
-
- /**
- * Here we are using a helper function. It will resolve if the player enters the Playing
- * state within 5 seconds, otherwise it will reject with an error.
- */
- return entersState(player, AudioPlayerStatus.Playing, 5e3);
-}
-
-async function connectToChannel(channel: VoiceBasedChannel) {
- /**
- * Here, we try to establish a connection to a voice channel. If we're already connected
- * to this voice channel, @discordjs/voice will just return the existing connection for us!
- */
- const connection = joinVoiceChannel({
- channelId: channel.id,
- guildId: channel.guild.id,
- adapterCreator: createDiscordJSAdapter(channel),
- });
-
- /**
- * If we're dealing with a connection that isn't yet Ready, we can set a reasonable
- * time limit before giving up. In this example, we give the voice connection 30 seconds
- * to enter the ready state before giving up.
- */
- try {
- /**
- * Allow ourselves 30 seconds to join the voice channel. If we do not join within then,
- * an error is thrown.
- */
- await entersState(connection, VoiceConnectionStatus.Ready, 30e3);
- /**
- * At this point, the voice connection is ready within 30 seconds! This means we can
- * start playing audio in the voice channel. We return the connection so it can be
- * used by the caller.
- */
- return connection;
- } catch (error) {
- /**
- * At this point, the voice connection has not entered the Ready state. We should make
- * sure to destroy it, and propagate the error by throwing it, so that the calling function
- * is aware that we failed to connect to the channel.
- */
- connection.destroy();
- throw error;
- }
-}
-
-/**
- * Main code
- * =========
- * Here we will implement the helper functions that we have defined above.
- */
-
-const client = new Client({
- intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildVoiceStates],
-});
-
-void client.login('token here');
-
-client.on('ready', async () => {
- console.log('Discord.js client is ready!');
-
- /**
- * Try to get our song ready to play for when the bot joins a voice channel
- */
- try {
- await playSong();
- console.log('Song is ready to play!');
- } catch (error) {
- /**
- * The song isn't ready to play for some reason :(
- */
- console.error(error);
- }
-});
-
-client.on('messageCreate', async (message) => {
- if (!message.guild) return;
-
- if (message.content === '-join') {
- const channel = message.member?.voice.channel;
-
- if (channel) {
- /**
- * The user is in a voice channel, try to connect.
- */
- try {
- const connection = await connectToChannel(channel);
-
- /**
- * We have successfully connected! Now we can subscribe our connection to
- * the player. This means that the player will play audio in the user's
- * voice channel.
- */
- connection.subscribe(player);
- await message.reply('Playing now!');
- } catch (error) {
- /**
- * Unable to connect to the voice channel within 30 seconds :(
- */
- console.error(error);
- }
- } else {
- /**
- * The user is not in a voice channel.
- */
- void message.reply('Join a voice channel then try again!');
- }
- }
-});
diff --git a/packages/voice/examples/radio-bot/.gitignore b/packages/voice/examples/radio-bot/.gitignore
deleted file mode 100644
index dba742256..000000000
--- a/packages/voice/examples/radio-bot/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-config.json
-package-lock.json
diff --git a/packages/voice/examples/radio-bot/README.md b/packages/voice/examples/radio-bot/README.md
deleted file mode 100644
index 69d0b090b..000000000
--- a/packages/voice/examples/radio-bot/README.md
+++ /dev/null
@@ -1,64 +0,0 @@
-# Discord Radio Bot 🎧
-
-A proof-of-concept radio bot that uses @discordjs/voice and discord.js. Streams audio from an audio output hardware device on your computer over a Discord voice channel.
-
-**Works on:**
-
-- Linux (via PulseAudio `pulse`)
-- Windows (via DirectShow `dshow`)
-
-## Usage
-
-```bash
-# Clone the main @discordjs/voice repo, then install dependencies and build
-$ npm install
-$ npm run build
-
-# Enter this example's directory, create a config file and start!
-$ cd examples/radio-bot
-$ npm install
-$ nano config.json
-$ npm start
-
-# Join a voice channel in Discord, then send "-join"
-```
-
-## Configuring on Windows via `dshow`
-
-Run `ffmpeg -list_devices true -f dshow -i dummy` and observe output containing something similar:
-
-```
-DirectShow audio devices
- "Stereo Mix (Realtek(R) Audio)"
- Alternative name "@device_cm_{ID1}\wave_{ID2}"
-```
-
-For example, playing the above device will mirror audio from the speaker output of your machine. Your `config.json` should then be considered like so:
-
-```json
-{
- "token": "discord_bot_token",
- "device": "Stereo Mix (Realtek(R) Audio)",
- "type": "dshow",
- "maxTransmissionGap": 5000
-}
-```
-
-## Configuring on Linux via `pulse`
-
-Run `pactl list short sources` and observe output containing something similar:
-
-```
-5 alsa_output.pci.3.analog-stereo.monitor module-alsa-card.c s16le 2ch 44100Hz IDLE
-```
-
-Then configure your `config.json` with the device you'd like to use:
-
-```json
-{
- "token": "discord_bot_token",
- "device": "alsa_output.pci.3.analog-stereo.monitor",
- "type": "pulse",
- "maxTransmissionGap": 5000
-}
-```
diff --git a/packages/voice/examples/radio-bot/config.example.json b/packages/voice/examples/radio-bot/config.example.json
deleted file mode 100644
index 6aa3fed98..000000000
--- a/packages/voice/examples/radio-bot/config.example.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "token": "discord_bot_token",
- "device": "audio_hw_device_id",
- "type": "pulse",
- "maxTransmissionGap": 5000
-}
diff --git a/packages/voice/examples/radio-bot/index.js b/packages/voice/examples/radio-bot/index.js
deleted file mode 100644
index 672dbb6a6..000000000
--- a/packages/voice/examples/radio-bot/index.js
+++ /dev/null
@@ -1,104 +0,0 @@
-require('module-alias/register');
-
-const { Client } = require('discord.js');
-const prism = require('prism-media');
-const config = require('./config.json');
-const {
- NoSubscriberBehavior,
- StreamType,
- createAudioPlayer,
- createAudioResource,
- entersState,
- AudioPlayerStatus,
- VoiceConnectionStatus,
- joinVoiceChannel,
-} = require('@discordjs/voice');
-
-const player = createAudioPlayer({
- behaviors: {
- noSubscriber: NoSubscriberBehavior.Play,
- maxMissedFrames: Math.round(config.maxTransmissionGap / 20),
- },
-});
-
-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();
- }
-});
-
-function attachRecorder() {
- player.play(
- createAudioResource(
- new prism.FFmpeg({
- args: [
- '-analyzeduration',
- '0',
- '-loglevel',
- '0',
- '-f',
- config.type,
- '-i',
- config.type === 'dshow' ? `audio=${config.device}` : config.device,
- '-acodec',
- 'libopus',
- '-f',
- 'opus',
- '-ar',
- '48000',
- '-ac',
- '2',
- ],
- }),
- {
- inputType: StreamType.OggOpus,
- },
- ),
- );
- console.log('Attached recorder - ready to go!');
-}
-
-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);
- return connection;
- } catch (error) {
- connection.destroy();
- throw error;
- }
-}
-
-const client = new Client({ intents: ['GUILDS', 'GUILD_MESSAGES', 'GUILD_VOICE_STATES'] });
-
-client.on('ready', async () => {
- console.log('discord.js client is ready!');
- attachRecorder();
-});
-
-client.on('messageCreate', async (message) => {
- if (!message.guild) return;
- 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!');
- }
- }
-});
-
-void client.login(config.token);
diff --git a/packages/voice/examples/radio-bot/package.json b/packages/voice/examples/radio-bot/package.json
deleted file mode 100644
index 1cafafad5..000000000
--- a/packages/voice/examples/radio-bot/package.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "name": "discord-radio-bot",
- "version": "1.0.0",
- "description": "A proof-of-concept radio bot for @discordjs/voice",
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1",
- "start": "node index.js"
- },
- "keywords": [
- "discord",
- "radio",
- "bot",
- "audio",
- "speakers",
- "hardware",
- "dj"
- ],
- "author": "Amish Shah ",
- "license": "MIT",
- "dependencies": {
- "@discordjs/voice": "file:../../",
- "discord.js": "^13.7.0",
- "libsodium-wrappers": "^0.7.9",
- "module-alias": "^2.2.2",
- "prism-media": "^1.3.1"
- },
- "_moduleAliases": {
- "@root": ".",
- "@discordjs/voice": "../../",
- "libsodium-wrappers": "./node_modules/libsodium-wrappers"
- }
-}
diff --git a/packages/voice/examples/recorder/.eslintrc.json b/packages/voice/examples/recorder/.eslintrc.json
deleted file mode 100644
index ab8cc9fa2..000000000
--- a/packages/voice/examples/recorder/.eslintrc.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "root": true,
- "extends": "../../.eslintrc.json",
- "parserOptions": {
- "project": "./tsconfig.eslint.json"
- }
-}
diff --git a/packages/voice/examples/recorder/.gitignore b/packages/voice/examples/recorder/.gitignore
deleted file mode 100644
index 789c0675a..000000000
--- a/packages/voice/examples/recorder/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-package-lock.json
-auth.json
-tsconfig.tsbuildinfo
-recordings/*.ogg
diff --git a/packages/voice/examples/recorder/README.md b/packages/voice/examples/recorder/README.md
deleted file mode 100644
index 397542d53..000000000
--- a/packages/voice/examples/recorder/README.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# 👂 Recorder Bot
-
-This example shows how you can use the voice receive functionality in @discordjs/voice to record users in voice channels
-and save the audio to local Ogg files.
-
-## Usage
-
-```sh-session
-# Clone the main repository, and then run:
-$ npm install
-$ npm run build
-
-# Open this example and install dependencies
-$ cd examples/recorder
-$ npm install
-
-# Set a bot token (see auth.example.json)
-$ cp auth.example.json auth.json
-$ nano auth.json
-
-# Start the bot!
-$ npm start
-```
diff --git a/packages/voice/examples/recorder/auth.example.json b/packages/voice/examples/recorder/auth.example.json
deleted file mode 100644
index 34e3fca00..000000000
--- a/packages/voice/examples/recorder/auth.example.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "token": "Your Discord bot token here"
-}
diff --git a/packages/voice/examples/recorder/package.json b/packages/voice/examples/recorder/package.json
deleted file mode 100644
index 47ce68f27..000000000
--- a/packages/voice/examples/recorder/package.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
- "name": "receiver-bot",
- "version": "0.0.1",
- "description": "An example receiver bot written using @discordjs/voice",
- "scripts": {
- "start": "npm run build && node -r tsconfig-paths/register dist/bot",
- "test": "echo \"Error: no test specified\" && exit 1",
- "lint": "eslint src --ext .ts",
- "lint:fix": "eslint src --ext .ts --fix",
- "prettier": "prettier --write .",
- "build": "tsc",
- "build:check": "tsc --noEmit --incremental false"
- },
- "author": "Amish Shah ",
- "license": "MIT",
- "dependencies": {
- "@discordjs/opus": "^0.8.0",
- "discord-api-types": "^0.33.3",
- "discord.js": "^13.8.0",
- "libsodium-wrappers": "^0.7.9",
- "node-crc": "^1.3.2",
- "prism-media": "^2.0.0-alpha.0"
- },
- "devDependencies": {
- "tsconfig-paths": "^3.10.1",
- "typescript": "^4.7.4"
- }
-}
diff --git a/packages/voice/examples/recorder/recordings/.gitkeep b/packages/voice/examples/recorder/recordings/.gitkeep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/packages/voice/examples/recorder/src/bot.ts b/packages/voice/examples/recorder/src/bot.ts
deleted file mode 100644
index 8e7555008..000000000
--- a/packages/voice/examples/recorder/src/bot.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { getVoiceConnection } from '@discordjs/voice';
-import { GatewayIntentBits } from 'discord-api-types/v9';
-import Discord, { Interaction, Constants } from 'discord.js';
-import { deploy } from './deploy';
-import { interactionHandlers } from './interactions';
-
-const { token } = require('../auth.json') as { token: string };
-
-const client = new Discord.Client({
- intents: [GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildMessages, GatewayIntentBits.Guilds],
-});
-
-const { Events } = Constants;
-
-client.on(Events.CLIENT_READY, () => console.log('Ready!'));
-
-client.on(Events.MESSAGE_CREATE, async (message) => {
- if (!message.guild) return;
- if (!client.application?.owner) await client.application?.fetch();
-
- if (message.content.toLowerCase() === '!deploy' && message.author.id === client.application?.owner?.id) {
- await deploy(message.guild);
- await message.reply('Deployed!');
- }
-});
-
-/**
- * The IDs of the users that can be recorded by the bot.
- */
-const recordable = new Set();
-
-client.on(Events.INTERACTION_CREATE, async (interaction: Interaction) => {
- if (!interaction.isCommand() || !interaction.guildId) return;
-
- const handler = interactionHandlers.get(interaction.commandName);
-
- try {
- if (handler) {
- await handler(interaction, recordable, client, getVoiceConnection(interaction.guildId));
- } else {
- await interaction.reply('Unknown command');
- }
- } catch (error) {
- console.warn(error);
- }
-});
-
-client.on(Events.ERROR, console.warn);
-
-void client.login(token);
diff --git a/packages/voice/examples/recorder/src/createListeningStream.ts b/packages/voice/examples/recorder/src/createListeningStream.ts
deleted file mode 100644
index 60e659b04..000000000
--- a/packages/voice/examples/recorder/src/createListeningStream.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { createWriteStream } from 'node:fs';
-import { pipeline } from 'node:stream';
-import { EndBehaviorType, VoiceReceiver } from '@discordjs/voice';
-import type { User } from 'discord.js';
-import * as prism from 'prism-media';
-
-function getDisplayName(userId: string, user?: User) {
- return user ? `${user.username}_${user.discriminator}` : userId;
-}
-
-export function createListeningStream(receiver: VoiceReceiver, userId: string, user?: User) {
- const opusStream = receiver.subscribe(userId, {
- end: {
- behavior: EndBehaviorType.AfterSilence,
- duration: 1000,
- },
- });
-
- const oggStream = new prism.opus.OggLogicalBitstream({
- opusHead: new prism.opus.OpusHead({
- channelCount: 2,
- sampleRate: 48000,
- }),
- pageSizeControl: {
- maxPackets: 10,
- },
- });
-
- const filename = `./recordings/${Date.now()}-${getDisplayName(userId, user)}.ogg`;
-
- const out = createWriteStream(filename);
-
- console.log(`👂 Started recording ${filename}`);
-
- pipeline(opusStream, oggStream, out, (err) => {
- if (err) {
- console.warn(`❌ Error recording file ${filename} - ${err.message}`);
- } else {
- console.log(`✅ Recorded ${filename}`);
- }
- });
-}
diff --git a/packages/voice/examples/recorder/src/deploy.ts b/packages/voice/examples/recorder/src/deploy.ts
deleted file mode 100644
index ce1166ae9..000000000
--- a/packages/voice/examples/recorder/src/deploy.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { ApplicationCommandOptionType } from 'discord-api-types/v9';
-import type { Guild } from 'discord.js';
-
-export const deploy = async (guild: Guild) => {
- await guild.commands.set([
- {
- name: 'join',
- description: 'Joins the voice channel that you are in',
- },
- {
- name: 'record',
- description: 'Enables recording for a user',
- options: [
- {
- name: 'speaker',
- type: ApplicationCommandOptionType.User,
- description: 'The user to record',
- required: true,
- },
- ],
- },
- {
- name: 'leave',
- description: 'Leave the voice channel',
- },
- ]);
-};
diff --git a/packages/voice/examples/recorder/src/interactions.ts b/packages/voice/examples/recorder/src/interactions.ts
deleted file mode 100644
index 55610ca2b..000000000
--- a/packages/voice/examples/recorder/src/interactions.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-import { entersState, joinVoiceChannel, VoiceConnection, VoiceConnectionStatus } from '@discordjs/voice';
-import { Client, CommandInteraction, GuildMember, Snowflake } from 'discord.js';
-import { createListeningStream } from './createListeningStream';
-
-async function join(
- interaction: CommandInteraction,
- recordable: Set,
- client: Client,
- connection?: VoiceConnection,
-) {
- await interaction.deferReply();
- if (!connection) {
- if (interaction.member instanceof GuildMember && interaction.member.voice.channel) {
- const channel = interaction.member.voice.channel;
- connection = joinVoiceChannel({
- channelId: channel.id,
- guildId: channel.guild.id,
- selfDeaf: false,
- selfMute: true,
- adapterCreator: channel.guild.voiceAdapterCreator,
- });
- } else {
- await interaction.followUp('Join a voice channel and then try that again!');
- return;
- }
- }
-
- try {
- await entersState(connection, VoiceConnectionStatus.Ready, 20e3);
- const receiver = connection.receiver;
-
- receiver.speaking.on('start', (userId) => {
- if (recordable.has(userId)) {
- createListeningStream(receiver, userId, client.users.cache.get(userId));
- }
- });
- } catch (error) {
- console.warn(error);
- await interaction.followUp('Failed to join voice channel within 20 seconds, please try again later!');
- }
-
- await interaction.followUp('Ready!');
-}
-
-async function record(
- interaction: CommandInteraction,
- recordable: Set,
- client: Client,
- connection?: VoiceConnection,
-) {
- if (connection) {
- const userId = interaction.options.get('speaker')!.value! as Snowflake;
- recordable.add(userId);
-
- const receiver = connection.receiver;
- if (connection.receiver.speaking.users.has(userId)) {
- createListeningStream(receiver, userId, client.users.cache.get(userId));
- }
-
- await interaction.reply({ ephemeral: true, content: 'Listening!' });
- } else {
- await interaction.reply({ ephemeral: true, content: 'Join a voice channel and then try that again!' });
- }
-}
-
-async function leave(
- interaction: CommandInteraction,
- recordable: Set,
- _client: Client,
- connection?: VoiceConnection,
-) {
- if (connection) {
- connection.destroy();
- recordable.clear();
- await interaction.reply({ ephemeral: true, content: 'Left the channel!' });
- } else {
- await interaction.reply({ ephemeral: true, content: 'Not playing in this server!' });
- }
-}
-
-export const interactionHandlers = new Map<
- string,
- (
- interaction: CommandInteraction,
- recordable: Set,
- client: Client,
- connection?: VoiceConnection,
- ) => Promise
->();
-interactionHandlers.set('join', join);
-interactionHandlers.set('record', record);
-interactionHandlers.set('leave', leave);
diff --git a/packages/voice/examples/recorder/tsconfig.eslint.json b/packages/voice/examples/recorder/tsconfig.eslint.json
deleted file mode 100644
index ea6be8e9a..000000000
--- a/packages/voice/examples/recorder/tsconfig.eslint.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "extends": "./tsconfig.json"
-}
diff --git a/packages/voice/examples/recorder/tsconfig.json b/packages/voice/examples/recorder/tsconfig.json
deleted file mode 100644
index cadce793d..000000000
--- a/packages/voice/examples/recorder/tsconfig.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "extends": "../../tsconfig.json",
- "compilerOptions": {
- "baseUrl": ".",
- "outDir": "dist",
- "skipLibCheck": true,
- "paths": {
- "@discordjs/voice": ["../../"],
- "libsodium-wrappers": ["./node_modules/libsodium-wrappers"]
- }
- },
- "include": ["src/*.ts"],
- "exclude": [""]
-}