chore: monorepo setup (#7175)

This commit is contained in:
Noel
2022-01-07 17:18:25 +01:00
committed by GitHub
parent 780b7ed39f
commit 16390efe6e
504 changed files with 25459 additions and 22830 deletions

View File

@@ -0,0 +1,91 @@
# 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!');
}
}
});
```

View File

@@ -0,0 +1,69 @@
import { DiscordGatewayAdapterCreator, DiscordGatewayAdapterLibraryMethods } from '../../';
import { VoiceChannel, Snowflake, Client, Constants, Guild } from 'discord.js';
import { GatewayVoiceServerUpdateDispatchData, GatewayVoiceStateUpdateDispatchData } from 'discord-api-types/v9';
const adapters = new Map<Snowflake, DiscordGatewayAdapterLibraryMethods>();
const trackedClients = new Set<Client>();
/**
* 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(Constants.WSEvents.VOICE_SERVER_UPDATE, (payload: GatewayVoiceServerUpdateDispatchData) => {
adapters.get(payload.guild_id)?.onVoiceServerUpdate(payload);
});
client.ws.on(Constants.WSEvents.VOICE_STATE_UPDATE, (payload: GatewayVoiceStateUpdateDispatchData) => {
if (payload.guild_id && payload.session_id && payload.user_id === client.user?.id) {
adapters.get(payload.guild_id)?.onVoiceStateUpdate(payload);
}
});
client.on(Constants.Events.SHARD_DISCONNECT, (_, 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<number, Set<Snowflake>>();
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: VoiceChannel): DiscordGatewayAdapterCreator {
return (methods) => {
adapters.set(channel.guild.id, methods);
trackClient(channel.client);
trackGuild(channel.guild);
return {
sendPayload(data) {
if (channel.guild.shard.status === Constants.Status.READY) {
channel.guild.shard.send(data);
return true;
}
return false;
},
destroy() {
return adapters.delete(channel.guild.id);
},
};
};
}

View File

@@ -0,0 +1,152 @@
import { Client, VoiceChannel, Intents } from 'discord.js';
import {
joinVoiceChannel,
createAudioPlayer,
createAudioResource,
entersState,
StreamType,
AudioPlayerStatus,
VoiceConnectionStatus,
} from '@discordjs/voice';
import { createDiscordJSAdapter } from './adapter';
/**
* 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: VoiceChannel) {
/**
* 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({
ws: { intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES, Intents.FLAGS.GUILD_VOICE_STATES] },
});
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!');
}
}
});