aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.parlance.json0
-rw-r--r--AudioBackend/AudioControl.js20
-rw-r--r--AudioBackend/PlayAudio.js46
-rw-r--r--AudioBackend/QueueSystem.js8
-rw-r--r--AudioBackend/Shutdown.js11
-rw-r--r--AudioBackend/VoiceInitialization.js14
-rw-r--r--Commands/about.js23
-rw-r--r--Commands/join.js9
-rw-r--r--Commands/leave.js11
-rw-r--r--Commands/list.js11
-rw-r--r--Commands/next.js5
-rw-r--r--Commands/pause.js10
-rw-r--r--Commands/ping.js5
-rw-r--r--Commands/play.js14
-rw-r--r--Commands/previous.js4
-rw-r--r--Commands/reshuffle.js12
-rw-r--r--Commands/shutdown.js7
-rw-r--r--Commands/status.js38
-rw-r--r--Locales/en/translation.json78
-rw-r--r--README.md33
-rw-r--r--Utilities/Voting.js32
-rw-r--r--Utilities/i18n.js10
-rw-r--r--bot.js22
-rw-r--r--package.json14
-rw-r--r--yarn.lock320
25 files changed, 453 insertions, 304 deletions
diff --git a/.parlance.json b/.parlance.json
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/.parlance.json
diff --git a/AudioBackend/AudioControl.js b/AudioBackend/AudioControl.js
index 3a9c7a5..57d8ed7 100644
--- a/AudioBackend/AudioControl.js
+++ b/AudioBackend/AudioControl.js
@@ -22,21 +22,24 @@ import { readdirSync, readFileSync } from 'node:fs';
import { shufflePlaylist, orderPlaylist } from './QueueSystem.js';
import { playAudio, currentTrack, updatePlaylist } from './PlayAudio.js';
import { player } from './VoiceInitialization.js';
+import i18next from '../Utilities/i18n.js';
+const t = i18next.t;
const { shuffle, repeat } = JSON.parse(readFileSync('./config.json', 'utf-8'));
export const files = readdirSync('music');
export let playerState;
+export let playerStatus;
export let isAudioStatePaused;
let totalTrack = files.length;
async function repeatCheck(bot) {
if (repeat) {
- console.log('All beats in the playlist has finished, repeating beats...');
+ console.log(t('musicRepeatingFinished'));
totalTrack = files.length;
return (shuffle) ? await shufflePlaylist(bot) : await orderPlaylist(bot);
} else {
- console.log('All beats in the playlist has finished.');
+ console.log(t('musicPlaylistFinished'));
updatePlaylist('stop');
audioState(2);
}
@@ -53,9 +56,9 @@ export async function nextAudio(bot) {
export async function previousAudio(bot, interaction) {
if (currentTrack <= 0) {
- return await interaction.reply({ content: 'You are at the beginning of the playlist, cannot go further than this', ephemeral: true });
+ return await interaction.reply({ content: t('previousBeginningPlaylist'), ephemeral: true });
} else {
- await interaction.reply({ content: 'Playing previous music' });
+ await interaction.reply({ content: t('musicPrevious') });
player.stop();
updatePlaylist('back');
return await playAudio(bot);
@@ -73,17 +76,20 @@ export function toggleAudioState() {
export function audioState(state) {
switch (state) {
case 0:
- playerState = 'Playing';
+ playerState = t('playPlayerState');
+ playerStatus = 0;
isAudioStatePaused = false;
player.unpause();
break;
case 1:
- playerState = 'Paused';
+ playerState = t('pausePlayerState');
+ playerStatus = 1;
isAudioStatePaused = true;
player.pause();
break;
case 2:
- playerState = 'Stopped';
+ playerState = t('stopPlayerState');
+ playerStatus = 2;
totalTrack = files.length;
isAudioStatePaused = true;
player.stop();
diff --git a/AudioBackend/PlayAudio.js b/AudioBackend/PlayAudio.js
index 3d9f13a..5c45418 100644
--- a/AudioBackend/PlayAudio.js
+++ b/AudioBackend/PlayAudio.js
@@ -25,7 +25,10 @@ import { EmbedBuilder, AttachmentBuilder } from 'discord.js';
import { player } from './VoiceInitialization.js';
import { audioState, files } from './AudioControl.js';
import { integer } from '../Commands/play.js';
+import i18next from '../Utilities/i18n.js';
+
const { statusChannel, txtFile } = JSON.parse(readFileSync('./config.json', 'utf-8'));
+const t = i18next.t;
let fileData;
@@ -46,7 +49,7 @@ export async function playAudio(bot) {
const resource = createAudioResource('music/' + audio);
player.play(resource);
- console.log(`Now playing: ${audio}`);
+ console.log(t('nowPlayingFile', { audio }));
audioState(0);
@@ -60,7 +63,13 @@ export async function playAudio(bot) {
audioArtist = common.artist;
audioYear = common.year;
audioAlbum = common.album;
- if (common.picture) audioPicture = new AttachmentBuilder(common.picture[0].data, { name: 'albumArt.png', description: 'Album Art' });
+ if (common.picture) {
+ // Convert base64 image to a buffer
+ const imageBuffer = Buffer.from(common.picture[0].data, 'base64');
+
+ // Create a new attachment using the buffer
+ audioPicture = new AttachmentBuilder(imageBuffer, 'albumArt.png');
+ }
} else {
metadataEmpty = true;
}
@@ -72,7 +81,7 @@ export async function playAudio(bot) {
audio = audio.split('.').slice(0, -1).join('.');
if (txtFile) {
- fileData = `Now Playing: ${audio}`;
+ fileData = t('nowPlayingFile', { audio });
writeFile('./now-playing.txt', fileData, (err) => {
if (err) { console.log(err); }
});
@@ -81,31 +90,30 @@ export async function playAudio(bot) {
const statusEmbed = new EmbedBuilder();
if (metadataEmpty) {
- statusEmbed.setTitle('Now Playing');
+ statusEmbed.setTitle(t('nowPlaying'));
statusEmbed.addFields(
- { name: 'Title', value: `${audio}` },
- { name: 'Duration', value: `${duration}` }
+ { name: t('musicTitle'), value: `${audio}` },
+ { name: t('musicDuration'), value: `${duration}` }
);
statusEmbed.setColor('#0066ff');
} else {
- statusEmbed.setTitle('Now Playing');
+ statusEmbed.setTitle(t('nowPlaying'));
statusEmbed.addFields(
- { name: 'Title', value: `${audioTitle}`, inline: true },
- { name: 'Artist', value: `${audioArtist}`, inline: true },
- { name: 'Year', value: `${audioYear}` },
- { name: 'Duration', value: `${duration}` }
+ { name: t('musicTitle'), value: `${audioTitle}`, inline: true },
+ { name: t('musicArtist'), value: `${audioArtist}`, inline: true },
+ { name: t('musicYear'), value: `${audioYear}` },
+ { name: t('musicDuration'), value: `${duration}` }
);
- // console.log(audioPicture);
- if (audioPicture) {
- // statusEmbed.setThumbnail({ url: 'attachment://albumArt.png' });
- }
- statusEmbed.setFooter({ text: `Album: ${audioAlbum}\nFilename: ${audioFile}` });
+ // if (audioPicture) {
+ // statusEmbed.setThumbnail('attachment://albumArt.png');
+ // }
+ statusEmbed.setFooter({ text: t('playerFooter', { audioAlbum, audioFile }) });
statusEmbed.setColor('#0066ff');
}
const channel = bot.channels.cache.get(statusChannel);
- if (!channel) return console.error('The status channel does not exist! Skipping.');
- return await channel.send({ embeds: [statusEmbed], files: [audioPicture] });
+ if (!channel) return console.error(t('statusChannelError'));
+ return await channel.send({ embeds: [statusEmbed] });
}
export function updatePlaylist(option) {
@@ -126,7 +134,7 @@ export function updatePlaylist(option) {
audio = inputFiles[integer];
break;
case 'stop':
- audio = 'Not Playing';
+ audio = t('notPlaying');
break;
}
}
diff --git a/AudioBackend/QueueSystem.js b/AudioBackend/QueueSystem.js
index 0cd6ded..2bab8eb 100644
--- a/AudioBackend/QueueSystem.js
+++ b/AudioBackend/QueueSystem.js
@@ -20,6 +20,8 @@
***************************************************************************/
import { playAudio, updatePlaylist } from './PlayAudio.js';
import { files } from './AudioControl.js';
+import i18next from '../Utilities/i18n.js';
+const t = i18next.t;
function shuffleArray(array) {
// Durstenfeld Shuffle
@@ -29,16 +31,16 @@ function shuffleArray(array) {
}
}
export async function orderPlaylist(bot) {
- console.log('Playing beats by order...');
+ console.log(t('musicPlayOrder'));
updatePlaylist('reset');
console.log(files);
return await playAudio(bot);
}
export async function shufflePlaylist(bot) {
- console.log('Shuffling beats...');
+ console.log(t('musicShuffling'));
shuffleArray(files);
- console.log('Playing beats by shuffle...');
+ console.log(t('musicPlayShuffle'));
updatePlaylist('reset');
console.log(files);
return await playAudio(bot);
diff --git a/AudioBackend/Shutdown.js b/AudioBackend/Shutdown.js
index fb89505..dcc57f7 100644
--- a/AudioBackend/Shutdown.js
+++ b/AudioBackend/Shutdown.js
@@ -24,12 +24,15 @@ import { updatePlaylist } from './PlayAudio.js';
import { audioState } from './AudioControl.js';
import { readFileSync, writeFile } from 'node:fs';
import { getVoiceConnection, VoiceConnectionStatus } from '@discordjs/voice';
+import i18next from '../Utilities/i18n.js';
+
const { statusChannel, txtFile } = JSON.parse(readFileSync('./config.json', 'utf-8'));
let fileData;
+const t = i18next.t;
export async function destroyAudio(interaction) {
if (txtFile) {
- fileData = 'Now Playing: Nothing';
+ fileData = t('txtNothing');
writeFile('now-playing.txt', fileData, (err) => {
if (err) { console.log(err); }
});
@@ -47,13 +50,13 @@ export async function destroyAudio(interaction) {
export async function stopBot(bot, interaction) {
const statusEmbed = new EmbedBuilder()
.setAuthor({ name: bot.user.username, iconURL: bot.user.avatarURL() })
- .setDescription(`That's all folks! Powering down ${bot.user.username}...`)
+ .setDescription(t('statusShutdown', { bot: bot.user.username }))
.setColor('#0066ff');
const channel = bot.channels.cache.get(statusChannel);
- if (!channel) return console.error('The status channel does not exist! Skipping.');
+ if (!channel) return console.error(t('statusChannelError'));
await channel.send({ embeds: [statusEmbed] });
- console.log(`Powering off ${bot.user.username}...`);
+ console.log(t('powerOff', { bot: bot.user.username }));
await destroyAudio(interaction);
await bot.destroy();
return process.exit(0);
diff --git a/AudioBackend/VoiceInitialization.js b/AudioBackend/VoiceInitialization.js
index ae1241b..0088fbd 100644
--- a/AudioBackend/VoiceInitialization.js
+++ b/AudioBackend/VoiceInitialization.js
@@ -19,13 +19,15 @@
*
***************************************************************************/
import { readFileSync } from 'node:fs';
-import { createAudioPlayer, joinVoiceChannel, VoiceConnectionStatus } from '@discordjs/voice';
+import { createAudioPlayer, joinVoiceChannel, VoiceConnectionStatus, AudioPlayerStatus } from '@discordjs/voice';
import { nextAudio } from './AudioControl.js';
import { shufflePlaylist, orderPlaylist } from './QueueSystem.js';
import { votes } from '../Utilities/Voting.js';
+import i18next from '../Utilities/i18n.js';
const { voiceChannel, shuffle } = JSON.parse(readFileSync('./config.json', 'utf-8'));
export const player = createAudioPlayer();
+const t = i18next.t;
export async function voiceInit(bot) {
bot.channels.fetch(voiceChannel).then(async channel => {
const connection = joinVoiceChannel({
@@ -35,16 +37,16 @@ export async function voiceInit(bot) {
});
connection.on(VoiceConnectionStatus.Connecting, () => {
- console.log(`Connecting to ${channel.name}...`);
+ console.log(t('voiceConnecting', { channel: channel.name }));
});
connection.on(VoiceConnectionStatus.Ready, async() => {
- console.log('Ready to blast some beats!');
+ console.log(t('voiceReady'));
return (shuffle) ? await shufflePlaylist(bot) : await orderPlaylist(bot);
});
connection.on(VoiceConnectionStatus.Destroyed, () => {
- console.log('Destroyed the beats...');
+ console.log(t('voiceDestroyed'));
});
player.on('error', error => {
@@ -52,8 +54,8 @@ export async function voiceInit(bot) {
nextAudio(bot);
});
- player.on('idle', () => {
- console.log('Beat has finished playing, now playing next beat...');
+ player.on(AudioPlayerStatus.Idle, () => {
+ console.log(t('musicsFinished'));
votes.clear();
nextAudio(bot);
});
diff --git a/Commands/about.js b/Commands/about.js
index e027d02..d8afa52 100644
--- a/Commands/about.js
+++ b/Commands/about.js
@@ -21,33 +21,34 @@
import { EmbedBuilder, version, ActionRowBuilder, ButtonBuilder, ButtonStyle, SlashCommandBuilder } from 'discord.js';
// import npmPackage from '../package.json' assert { type:'json' }
+import i18next from '../Utilities/i18n.js';
import { readFileSync } from 'node:fs';
const npmPackage = JSON.parse(readFileSync('./package.json', 'utf-8'));
-
+const t = i18next.t;
export default {
data: new SlashCommandBuilder()
.setName('about')
.setDescription('Information about the bot'),
async execute(interaction, bot) {
const aboutEmbed = new EmbedBuilder()
- .setAuthor({ name: `About ${bot.user.username}`, iconURL: bot.user.avatarURL() })
+ .setAuthor({ name: t('aboutBot', { bot: bot.user.username }), iconURL: bot.user.avatarURL() })
.addFields(
- { name: 'Information', value: 'A Discord bot that plays local audio tracks.' },
- { name: 'Version', value: `DLAP ${npmPackage.version}` },
- { name: 'Original Creator', value: 'Andrew Lee (Alee#4277)' }, // Do not remove this since I created this :)
- // { name: 'Contributors', value: '[your name] (discord#0000)' },
- // { name: 'Forked by', value: '[your name] (discord#0000)' },
- { name: 'Frameworks', value: `Discord.JS ${version} + Voice` },
- { name: 'License', value: 'GNU General Public License v3.0' }
+ { name: t('aboutInfo'), value: t('aboutInfoValue') },
+ { name: t('aboutBotVersion'), value: `DLAP ${npmPackage.version}` },
+ { name: t('aboutCreator'), value: 'Andrew Lee (alee)' }, // Do not remove this since I created this :)
+ // { name: t('aboutContributors'), value: '[your name] (username)' },
+ // { name: t('aboutForked'), value: '[your name] (username)' },
+ { name: t('aboutFrameworks'), value: `Discord.JS ${version} + Voice` },
+ { name: t('aboutLicense'), value: 'GNU General Public License v3.0' }
)
- .setFooter({ text: '© Copyright 2020-2023 Andrew Lee' })
+ .setFooter({ text: '© Copyright 2020-2024 Andrew Lee' })
.setColor('#0066ff');
const srcOrig = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setStyle(ButtonStyle.Link)
- .setLabel('Original Source Code')
+ .setLabel(t('aboutSrc'))
.setURL('https://github.com/Alee14/DLAP')
);
diff --git a/Commands/join.js b/Commands/join.js
index e9bac9b..2f513cf 100644
--- a/Commands/join.js
+++ b/Commands/join.js
@@ -23,17 +23,18 @@ import { SlashCommandBuilder } from 'discord.js';
import { voiceInit } from '../AudioBackend/VoiceInitialization.js';
import { PermissionFlagsBits } from 'discord-api-types/v10';
import { readFileSync } from 'node:fs';
+import i18next from '../Utilities/i18n.js';
const { djRole, ownerID } = JSON.parse(readFileSync('./config.json', 'utf-8'));
-
+const t = i18next.t;
export default {
data: new SlashCommandBuilder()
.setName('join')
.setDescription('Joins voice chat'),
async execute(interaction, bot) {
- if (!interaction.member.voice.channel) return await interaction.reply({ content: 'You need to be in a voice channel to use this command.', ephemeral: true });
- if (!interaction.member.roles.cache.has(djRole) && interaction.user.id !== ownerID && !interaction.memberPermissions.has(PermissionFlagsBits.ManageGuild)) return interaction.reply({ content: 'You need a specific role to execute this command', ephemeral: true });
+ if (!interaction.member.voice.channel) return await interaction.reply({ content: t('voicePermission'), ephemeral: true });
+ if (!interaction.member.roles.cache.has(djRole) && interaction.user.id !== ownerID && !interaction.memberPermissions.has(PermissionFlagsBits.ManageGuild)) return interaction.reply({ content: t('rolePermission'), ephemeral: true });
- await interaction.reply({ content: 'Joining voice channel', ephemeral: true });
+ await interaction.reply({ content: t('joinVoice'), ephemeral: true });
return await voiceInit(bot);
}
};
diff --git a/Commands/leave.js b/Commands/leave.js
index c90231c..e6559ca 100644
--- a/Commands/leave.js
+++ b/Commands/leave.js
@@ -23,18 +23,19 @@ import { SlashCommandBuilder } from 'discord.js';
import { destroyAudio } from '../AudioBackend/Shutdown.js';
import { PermissionFlagsBits } from 'discord-api-types/v10';
import { readFileSync } from 'node:fs';
+import i18next from '../Utilities/i18n.js';
const { djRole, ownerID } = JSON.parse(readFileSync('./config.json', 'utf-8'));
-
+const t = i18next.t;
export default {
data: new SlashCommandBuilder()
.setName('leave')
.setDescription('Leaves the voice chat'),
async execute(interaction, bot) {
- if (!interaction.member.voice.channel) return await interaction.reply({ content: 'You need to be in a voice channel to use this command.', ephemeral: true });
- if (!interaction.member.roles.cache.has(djRole) && interaction.user.id !== ownerID && !interaction.memberPermissions.has(PermissionFlagsBits.ManageGuild)) return interaction.reply({ content: 'You need a specific role to execute this command', ephemeral: true });
+ if (!interaction.member.voice.channel) return await interaction.reply({ content: t('voicePermission'), ephemeral: true });
+ if (!interaction.member.roles.cache.has(djRole) && interaction.user.id !== ownerID && !interaction.memberPermissions.has(PermissionFlagsBits.ManageGuild)) return interaction.reply({ content: t('rolePermission'), ephemeral: true });
- console.log('Leaving voice channel...');
+ console.log(t('leaveVoice'));
await destroyAudio(interaction);
- return await interaction.reply({ content: 'Leaving voice channel', ephemeral: true });
+ return await interaction.reply({ content: t('leaveVoice'), ephemeral: true });
}
};
diff --git a/Commands/list.js b/Commands/list.js
index 90d3cc6..4713a63 100644
--- a/Commands/list.js
+++ b/Commands/list.js
@@ -21,9 +21,10 @@
import { EmbedBuilder, SlashCommandBuilder } from 'discord.js';
import { readdir } from 'node:fs';
+import i18next from '../Utilities/i18n.js';
const musicFolder = './music';
-
+const t = i18next.t;
export default {
data: new SlashCommandBuilder()
.setName('list')
@@ -42,7 +43,7 @@ export default {
const pageSize = 20; // Number of tracks per page
const numPages = Math.ceil(trackList.length / pageSize); // Total number of pages
if (page < 1 || page > numPages) { // Check if the page number is valid
- return await interaction.reply({ content: `Invalid page number. Please specify a number between 1 and ${numPages}.`, ephemeral: true });
+ return await interaction.reply({ content: t('invalidPage', { numPages }), ephemeral: true });
}
// Split the track list into pages
const pages = [];
@@ -53,9 +54,9 @@ export default {
}
// Send the specified page with the page number and total number of pages
const listEmbed = new EmbedBuilder();
- listEmbed.setAuthor({ name: `${bot.user.username} List`, iconURL: bot.user.avatarURL() });
- listEmbed.addFields({ name: `Listing ${trackList.length} audio tracks...`, value: `\`\`\`\n${pages[page - 1].join('\n')}\n\`\`\`` });
- listEmbed.setFooter({ text: `Page ${page}/${numPages}` });
+ listEmbed.setAuthor({ name: t('listTitle', { bot: bot.user.username }), iconURL: bot.user.avatarURL() });
+ listEmbed.addFields({ name: t('listTracks', { trackList: trackList.length }), value: `\`\`\`\n${pages[page - 1].join('\n')}\n\`\`\`` });
+ listEmbed.setFooter({ text: t('listPage') + ` ${page}/${numPages}` });
listEmbed.setColor('#0066ff');
await interaction.reply({ embeds: [listEmbed] });
}
diff --git a/Commands/next.js b/Commands/next.js
index f082c89..c99fd6b 100644
--- a/Commands/next.js
+++ b/Commands/next.js
@@ -21,7 +21,8 @@
import { SlashCommandBuilder } from 'discord.js';
import { voteSkip } from '../Utilities/Voting.js';
-
+import i18next from '../Utilities/i18n.js';
+const t = i18next.t;
export default {
data: new SlashCommandBuilder()
.setName('next')
@@ -34,7 +35,7 @@ export default {
.setDescription('Forces skip this audio track')),
async execute(interaction, bot) {
- if (!interaction.member.voice.channel) return await interaction.reply({ content: 'You need to be in a voice channel to use this command.', ephemeral: true });
+ if (!interaction.member.voice.channel) return await interaction.reply({ content: t('voicePermission'), ephemeral: true });
await voteSkip(interaction, bot);
}
};
diff --git a/Commands/pause.js b/Commands/pause.js
index ab5a609..bc649a6 100644
--- a/Commands/pause.js
+++ b/Commands/pause.js
@@ -23,20 +23,22 @@ import { SlashCommandBuilder } from 'discord.js';
import { toggleAudioState, isAudioStatePaused } from '../AudioBackend/AudioControl.js';
import { PermissionFlagsBits } from 'discord-api-types/v10';
import { readFileSync } from 'node:fs';
+import i18next from '../Utilities/i18n.js';
+const t = i18next.t;
const { djRole, ownerID } = JSON.parse(readFileSync('./config.json', 'utf-8'));
export default {
data: new SlashCommandBuilder()
.setName('pause')
.setDescription('Pauses music'),
async execute(interaction, bot) {
- if (!interaction.member.voice.channel) return await interaction.reply({ content: 'You need to be in a voice channel to use this command.', ephemeral: true });
- if (!interaction.member.roles.cache.has(djRole) && interaction.user.id !== ownerID && !interaction.memberPermissions.has(PermissionFlagsBits.ManageGuild)) return interaction.reply({ content: 'You need a specific role to execute this command', ephemeral: true });
+ if (!interaction.member.voice.channel) return await interaction.reply({ content: t('voicePermission'), ephemeral: true });
+ if (!interaction.member.roles.cache.has(djRole) && interaction.user.id !== ownerID && !interaction.memberPermissions.has(PermissionFlagsBits.ManageGuild)) return interaction.reply({ content: t('rolePermission'), ephemeral: true });
if (!isAudioStatePaused) {
toggleAudioState();
- return await interaction.reply({ content: 'Pausing music', ephemeral: true });
+ return await interaction.reply({ content: t('pausingMusic'), ephemeral: true });
} else {
- return await interaction.reply({ content: 'Music is already paused', ephemeral: true });
+ return await interaction.reply({ content: t('pausedAlready'), ephemeral: true });
}
}
};
diff --git a/Commands/ping.js b/Commands/ping.js
index 72ef024..3ee61e0 100644
--- a/Commands/ping.js
+++ b/Commands/ping.js
@@ -20,12 +20,13 @@
***************************************************************************/
import { SlashCommandBuilder } from 'discord.js';
-
+import i18next from '../Utilities/i18n.js';
+const t = i18next.t;
export default {
data: new SlashCommandBuilder()
.setName('ping')
.setDescription('Pong!'),
async execute(interaction, bot) {
- return await interaction.reply(`Pong! ${Math.round(bot.ws.ping)}ms`);
+ return await interaction.reply(`${t('pong')} ${Math.round(bot.ws.ping)}ms`);
}
};
diff --git a/Commands/play.js b/Commands/play.js
index f6a7fd3..7cfe36b 100644
--- a/Commands/play.js
+++ b/Commands/play.js
@@ -26,7 +26,9 @@ import { audio } from '../AudioBackend/PlayAudio.js';
import { PermissionFlagsBits } from 'discord-api-types/v10';
import { readFileSync } from 'node:fs';
import { votes } from '../Utilities/Voting.js';
+import i18next from '../Utilities/i18n.js';
+const t = i18next.t;
const { djRole, ownerID } = JSON.parse(readFileSync('./config.json', 'utf-8'));
export let integer;
@@ -41,24 +43,24 @@ export default {
),
async execute(interaction, bot) {
- if (!interaction.member.voice.channel) return await interaction.reply({ content: 'You need to be in a voice channel to use this command.', ephemeral: true });
- if (!interaction.member.roles.cache.has(djRole) && interaction.user.id !== ownerID && !interaction.memberPermissions.has(PermissionFlagsBits.ManageGuild)) return interaction.reply({ content: 'You need a specific role to execute this command', ephemeral: true });
+ if (!interaction.member.voice.channel) return await interaction.reply({ content: t('voicePermission'), ephemeral: true });
+ if (!interaction.member.roles.cache.has(djRole) && interaction.user.id !== ownerID && !interaction.memberPermissions.has(PermissionFlagsBits.ManageGuild)) return interaction.reply({ content: t('rolePermission'), ephemeral: true });
integer = interaction.options.getInteger('int');
if (integer) {
if (integer < files.length) {
await inputAudio(bot, integer);
await votes.clear();
- return await interaction.reply({ content: `Now playing: ${audio}`, ephemeral: true });
+ return await interaction.reply({ content: t('nowPlayingFile', audio), ephemeral: true });
} else {
- return await interaction.reply({ content: 'Number is too big, choose a number that\'s less than ' + files.length + '.', ephemeral: true });
+ return await interaction.reply({ content: t('numBig', { files: files.length }), ephemeral: true });
}
}
if (isAudioStatePaused) {
toggleAudioState();
- return await interaction.reply({ content: 'Resuming music', ephemeral: true });
+ return await interaction.reply({ content: t('resumingMusic'), ephemeral: true });
} else {
- return await interaction.reply({ content: 'Music is already playing', ephemeral: true });
+ return await interaction.reply({ content: t('resumedAlready'), ephemeral: true });
}
}
};
diff --git a/Commands/previous.js b/Commands/previous.js
index aa732b2..c5a45b5 100644
--- a/Commands/previous.js
+++ b/Commands/previous.js
@@ -21,7 +21,9 @@
import { SlashCommandBuilder } from 'discord.js';
import { voteSkip } from '../Utilities/Voting.js';
+import i18next from '../Utilities/i18n.js';
+const t = i18next.t;
export default {
data: new SlashCommandBuilder()
.setName('previous')
@@ -33,7 +35,7 @@ export default {
subcommand.setName('force')
.setDescription('Forces skip this audio track')),
async execute(interaction, bot) {
- if (!interaction.member.voice.channel) return await interaction.reply({ content: 'You need to be in a voice channel to use this command.', ephemeral: true });
+ if (!interaction.member.voice.channel) return await interaction.reply({ content: t('voicePermission'), ephemeral: true });
await voteSkip(interaction, bot);
}
};
diff --git a/Commands/reshuffle.js b/Commands/reshuffle.js
index c16314e..d4ecfc3 100644
--- a/Commands/reshuffle.js
+++ b/Commands/reshuffle.js
@@ -24,22 +24,24 @@ import { shufflePlaylist } from '../AudioBackend/QueueSystem.js';
import { PermissionFlagsBits } from 'discord-api-types/v10';
import { readFileSync } from 'node:fs';
import { audioState } from '../AudioBackend/AudioControl.js';
+import i18next from '../Utilities/i18n.js';
+
// import config from './config.json' assert {type: 'json'}
const { shuffle, djRole, ownerID } = JSON.parse(readFileSync('./config.json', 'utf-8'));
-
+const t = i18next.t;
export default {
data: new SlashCommandBuilder()
.setName('reshuffle')
.setDescription('Reshuffles the playlist'),
async execute(interaction, bot) {
- if (!interaction.member.voice.channel) return await interaction.reply({ content: 'You need to be in a voice channel to use this command.', ephemeral: true });
- if (!interaction.member.roles.cache.has(djRole) && interaction.user.id !== ownerID && !interaction.memberPermissions.has(PermissionFlagsBits.ManageGuild)) return interaction.reply({ content: 'You need a specific role to execute this command', ephemeral: true });
+ if (!interaction.member.voice.channel) return await interaction.reply({ content: t('voicePermission'), ephemeral: true });
+ if (!interaction.member.roles.cache.has(djRole) && interaction.user.id !== ownerID && !interaction.memberPermissions.has(PermissionFlagsBits.ManageGuild)) return interaction.reply({ content: t('rolePermission'), ephemeral: true });
async function shuffleDetected(bot) {
- await interaction.reply({ content: 'Reshuffling the playlist...', ephemeral: true });
+ await interaction.reply({ content: t('reshufflePlaylist'), ephemeral: true });
await audioState(2);
await shufflePlaylist(bot);
}
- return (shuffle) ? await shuffleDetected(bot) : await interaction.reply({ content: 'Shuffle mode is disabled, enable it in the configuration file to access this command.', ephemeral: true });
+ return (shuffle) ? await shuffleDetected(bot) : await interaction.reply({ content: t('reShuffleDisabled'), ephemeral: true });
}
};
diff --git a/Commands/shutdown.js b/Commands/shutdown.js
index eefbab3..6e26f83 100644
--- a/Commands/shutdown.js
+++ b/Commands/shutdown.js
@@ -22,14 +22,17 @@
import { SlashCommandBuilder } from 'discord.js';
import { stopBot } from '../AudioBackend/Shutdown.js';
import { readFileSync } from 'node:fs';
+import i18next from '../Utilities/i18n.js';
+
+const t = i18next.t;
const { ownerID } = JSON.parse(readFileSync('./config.json', 'utf-8'));
export default {
data: new SlashCommandBuilder()
.setName('shutdown')
.setDescription('Powers off the bot'),
async execute(interaction, bot) {
- if (interaction.user.id !== ownerID) return interaction.reply({ content: 'You need to be the creator of this bot to execute this command', ephemeral: true });
- await interaction.reply({ content: 'Powering off...', ephemeral: true });
+ if (interaction.user.id !== ownerID) return interaction.reply({ content: t('creatorPermission'), ephemeral: true });
+ await interaction.reply({ content: t('powerOff', { bot: bot.user.username }), ephemeral: true });
return await stopBot(bot, interaction);
}
};
diff --git a/Commands/status.js b/Commands/status.js
index aeb909d..d762187 100644
--- a/Commands/status.js
+++ b/Commands/status.js
@@ -22,9 +22,11 @@
import { EmbedBuilder, SlashCommandBuilder } from 'discord.js';
import { parseFile } from 'music-metadata';
import { audio, metadataEmpty, duration, audioTitle, currentTrack } from '../AudioBackend/PlayAudio.js';
-import { files, playerState } from '../AudioBackend/AudioControl.js';
+import { files, playerState, playerStatus } from '../AudioBackend/AudioControl.js';
import { votes } from '../Utilities/Voting.js';
+import i18next from '../Utilities/i18n.js';
+const t = i18next.t;
export default {
data: new SlashCommandBuilder()
.setName('status')
@@ -44,7 +46,7 @@ export default {
const votesRequired = Math.ceil((members.size - votes.size) / 2);
if (audioID >= files.length) {
- audioName = 'Playlist Finished';
+ audioName = t('playlistDone');
} else {
audioName = files[audioID];
if (!metadataEmpty) {
@@ -60,25 +62,27 @@ export default {
}
const controlEmbed = new EmbedBuilder()
- .setAuthor({ name: `${bot.user.username} Status`, iconURL: bot.user.avatarURL() })
+ .setAuthor({ name: t('statusTitle', { bot: bot.user.username }), iconURL: bot.user.avatarURL() })
.addFields(
- { name: 'State', value: `${playerState}` },
- { name: 'Tracks', value: `${audioID}/${files.length}` },
- { name: 'Duration', value: `${duration}` },
- { name: 'Votes Needed', value: `${votesRequired}` }
+ { name: t('statusState'), value: `${playerState}` },
+ { name: t('statusTracks'), value: `${audioID}/${files.length}` },
+ { name: t('musicDuration'), value: `${duration}` },
+ { name: t('statusVotesNeeded'), value: `${votesRequired}` }
)
.setColor('#0066ff');
- if (metadataEmpty) {
- controlEmbed.addFields(
- { name: 'Currently Playing', value: `${audio}` },
- { name: 'Up Next', value: `${audioName}` }
- );
- } else {
- controlEmbed.addFields(
- { name: 'Currently Playing', value: `${audioTitle}` },
- { name: 'Up Next', value: `${audioName}` }
- );
+ if (playerStatus === 0 || playerStatus === 1) {
+ if (metadataEmpty) {
+ controlEmbed.addFields(
+ { name: t('currentlyPlaying'), value: `${audio}` },
+ { name: t('upNext'), value: `${audioName}` }
+ );
+ } else {
+ controlEmbed.addFields(
+ { name: t('currentlyPlaying'), value: `${audioTitle}` },
+ { name: t('upNext'), value: `${audioName}` }
+ );
+ }
}
interaction.reply({ embeds: [controlEmbed] });
}
diff --git a/Locales/en/translation.json b/Locales/en/translation.json
index 3e13246..c96f74f 100644
--- a/Locales/en/translation.json
+++ b/Locales/en/translation.json
@@ -1,4 +1,80 @@
{
"botReady": "Bot is ready!",
- "loggedIn": "Logged in as {{bot}}!"
+ "loggedIn": "Logged in as {{bot}}!",
+ "discordVersion": "Running on Discord.JS {{version}}",
+ "voiceChannel": "Voice Channel: {{voiceChannel}}",
+ "statusChannel": "Status Channel: {{statusChannel}}",
+ "djRole": "DJ Role: {{djRole}}",
+ "ownerID": "Owner ID: {{ownerID}}",
+ "shuffleEnabled": "Shuffle Enabled: {{shuffle}}",
+ "repeatEnabled": "Repeat Enabled: {{repeat}}",
+ "presenceSet": "Updated bot presence to {{activity}}",
+ "startingBot": "Starting bot...",
+ "statusChannelError": "The status channel does not exist! Skipping.",
+ "exception": "There was an error while executing this command...\nShare this to the bot owner or report it to the git repository in `/about`\n\nDetails:```{{e}}```",
+ "musicRepeatingFinished": "All music in the playlist has finished, repeating...",
+ "musicPlaylistFinished": "All music in the playlist has finished.",
+ "previousBeginningPlaylist": "You are at the beginning of the playlist, cannot go further than this",
+ "musicPrevious": "Playing previous music",
+ "playPlayerState": "Playing",
+ "pausePlayerState": "Paused",
+ "stopPlayerState": "Stopped",
+ "nowPlayingFile": "Now playing: {{audio}}",
+ "nowPlaying": "Now Playing",
+ "musicTitle": "Title",
+ "musicArtist": "Artist",
+ "musicYear": "Year",
+ "musicDuration": "Duration",
+ "notPlaying": "Not Playing",
+ "playerFooter": "Album: {{audioAlbum}}\nFilename: {{audioFile}}",
+ "musicPlayOrder": "Playing music by order...",
+ "musicPlayShuffle": "Playing music by shuffle...",
+ "musicShuffling": "Shuffling music...",
+ "txtNothing": "Now Playing: Nothing",
+ "statusShutdown": "That's all folks! Powering down {{bot}}...",
+ "powerOff": "Powering off {{bot}}...",
+ "voiceConnecting": "Connecting to {{channel}}...",
+ "voiceReady": "Ready to blast some beats!",
+ "voiceDestroyed": "Destroyed the music player...",
+ "musicsFinished": "Music has finished playing, now playing next music...",
+ "aboutBot": "About {{bot}}",
+ "aboutInfo": "Information",
+ "aboutInfoValue": "A Discord bot that plays local audio tracks.",
+ "aboutBotVersion": "Version",
+ "aboutCreator": "Original Creator",
+ "aboutContributors": "Contributors",
+ "aboutForked": "Forked by",
+ "aboutFrameworks": "Frameworks",
+ "aboutLicense": "License",
+ "aboutSrc": "Original Source Code",
+ "voicePermission": "You need to be in a voice channel to use this command.",
+ "rolePermission": "You need a specific role to execute this command",
+ "creatorPermission": "You need to be the creator of this bot to execute this command",
+ "joinVoice": "Joining voice channel",
+ "leaveVoice": "Leaving voice channel",
+ "invalidPage": "Invalid page number. Please specify a number between 1 and {{numPages}}.",
+ "listTitle": "{{bot}} List",
+ "listTracks": "Listing {{trackList}} audio tracks...",
+ "listPage": "Page",
+ "pausingMusic": "Pausing music",
+ "pausedAlready": "Music is already paused",
+ "pong": "Pong!",
+ "numBig": "Number is too big, choose a number that's less than {{files}}.",
+ "resumingMusic": "Resuming music",
+ "resumedAlready": "Music is already playing",
+ "reshufflePlaylist": "Reshuffling the playlist...",
+ "reshuffleDisabled": "Shuffle mode is disabled, enable it in the configuration file to access this command.",
+ "playlistDone": "Playlist Finished",
+ "statusTitle": "{{bot}} Status",
+ "statusState": "State",
+ "statusTracks": "Tracks",
+ "statusVotesNeeded": "Votes Needed",
+ "currentlyPlaying": "Currently Playing",
+ "upNext": "Up Next",
+ "musicNext": "Playing next music",
+ "alreadyVoted": "You have already voted, wait {{votesRequired}} more vote(s) to skip the audio track",
+ "enoughVotes": "Enough votes has passed, skipping audio file...",
+ "votesNeeded": "{{votesRequired}} more vote(s) needed to skip the audio track.",
+ "cannotPlay": "Cannot play next music. Player is currently stopped...",
+ "forceSkip": "Force skipping this audio track..."
}
diff --git a/README.md b/README.md
index efa90ac..b625e76 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# DLAP Bot (Discord.JS Local Audio Player)
-DLAP is a Discord bot that lets you play local audio tracks in your server. With DLAP, you can access your music files, and share your tunes with your friends and community. DLAP offers seamless integration with Discord, so you can enjoy your music without missing a beat.
+DLAP is a Discord bot that lets you play local audio tracks in your server. With DLAP, you can access your music files, and share your tunes with your friends and community. DLAP offers seamless integration with Discord, so you can enjoy your music without missing a music.
[Video Tutorial](https://youtu.be/Gvva8LHjOOo) |
[Support Server](https://discord.gg/EFhRDqG)
@@ -20,6 +20,34 @@ Also you must join my discord server (Support Server) to communicate with me.
- Yarn Package Manager
- NodeJS v18.5.0+
+# Docker
+First install Docker then using CMD or a terminal change directory to DLAP root folder and type the following:
+```
+docker build -t dlap .
+```
+
+- the -t flag specifies a tag that will be assigned to the image. With it, we can easily run the image that the tag was assigned to.
+- the dot at the end is basically the path to search for Dockerfile. The dot means current directory (./)
+- Run the container
+
+Follow the guide below and when ready type the following
+```
+docker run -d --name dlap dlap:latest
+```
+
+- -d flag tells Docker to run the container in detached mode, meaning it will run the container in the background of your terminal and not give us any output from it. If we don't provide it, the run will be giving us the output until the application exits. Discord bots aren't supposed to exit after certain time, so we do need this flag
+- --name assigns a name to the container. By default, container is identified by id that is not human-readable. To conveniently refer to container when needed, we can assign it a name
+- dlap:latest means "latest version of dlap image"
+
+View logs (optional)
+```
+docker logs -f mybot
+```
+
+- -f flag tells the docker to keep reading logs as they appear in container and is called "follow mode". To exit press CTRL + C.
+
+*Docker guide partially taken from [PythonDiscord](https://www.pythondiscord.com/pages/guides/python-guides/docker-hosting-guide/#creating-dockerfile)*
+
# Configuration
Make a new file called `config.json` inside the root of your project.
```
@@ -33,8 +61,9 @@ Make a new file called `config.json` inside the root of your project.
"clientID": "client_id",
"ownerID": "your_user_id",
"djRole": "role_id",
+ "locale": "en",
"presenceActivity": "activity_here",
- "activityType": [0 (Playing)/1 (Streaming)/2 (Listening)/3 (Watching)/4 (Custom)/5 (Competing)]
+ "activityType": [0 (Playing)/1 (Streaming)/2 (Listening)/3 (Watching)/4 (Custom)/5 (Competing)],
}
```
diff --git a/Utilities/Voting.js b/Utilities/Voting.js
index dc2f153..5bea29c 100644
--- a/Utilities/Voting.js
+++ b/Utilities/Voting.js
@@ -18,18 +18,20 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
***************************************************************************/
-import { nextAudio, playerState, previousAudio } from '../AudioBackend/AudioControl.js';
+import { nextAudio, playerStatus, previousAudio } from '../AudioBackend/AudioControl.js';
import { PermissionFlagsBits } from 'discord-api-types/v10';
import { readFileSync } from 'node:fs';
import { player } from '../AudioBackend/VoiceInitialization.js';
-const { djRole, ownerID } = JSON.parse(readFileSync('./config.json', 'utf-8'));
+import i18next from '../Utilities/i18n.js';
+const { djRole, ownerID } = JSON.parse(readFileSync('./config.json', 'utf-8'));
+const t = i18next.t;
export const votes = new Set();
let nextCheck;
async function commandCheck(interaction, bot) {
if (nextCheck) {
- await interaction.reply({ content: 'Playing next music' });
+ await interaction.reply({ content: t('musicNext') });
player.stop();
return await nextAudio(bot);
} else {
@@ -62,37 +64,37 @@ export async function voteSkip(interaction, bot) {
// Check if the message author has already voted
if (votes.has(interaction.user.id)) {
- return interaction.reply({ content: `You have already voted, wait ${votesRequired} more vote(s) to skip the audio track`, ephemeral: true });
+ return interaction.reply({ content: t('alreadyVoted', votesRequired), ephemeral: true });
}
- if (playerState === 'Playing' || playerState === 'Paused') {
+ if (playerStatus === 0 || playerStatus === 1) {
// Add the message author to the set of members who have voted
votes.add(interaction.user.id);
if (votes.size >= votesRequired) {
- console.log('Enough votes has passed, skipping audio file...');
+ console.log(t('enoughVotes'));
// Reset the number of votes
votes.clear();
// Do something to skip the audio track here (e.g. player.stop())
await commandCheck(interaction, bot);
} else {
// Send a message with the number of votes needed to skip the audio track
- console.log(`${votesRequired - 1} more vote(s) needed to skip the audio track.`);
- await interaction.reply({ content: `${votesRequired - 1} more vote(s) needed to skip the audio track.` });
+ console.log(t('votesNeeded', { votesRequired: votesRequired - 1 }));
+ await interaction.reply({ content: t('votesNeeded', { votesRequired: votesRequired - 1 }) });
}
- } else if (playerState === 'Stopped') {
- return await interaction.reply({ content: 'Cannot play next music. Player is currently stopped...', ephemeral: true });
+ } else if (playerStatus === 2) {
+ return await interaction.reply({ content: t('cannotPlay'), ephemeral: true });
}
}
if (interaction.options.getSubcommand() === 'force') {
- if (!interaction.member.roles.cache.has(djRole) && interaction.user.id !== ownerID && !interaction.memberPermissions.has(PermissionFlagsBits.ManageGuild)) return interaction.reply({ content: 'You need a specific role to execute this command', ephemeral: true });
- console.log('Force skipping this audio track...');
- if (playerState === 'Playing' || playerState === 'Paused') {
+ if (!interaction.member.roles.cache.has(djRole) && interaction.user.id !== ownerID && !interaction.memberPermissions.has(PermissionFlagsBits.ManageGuild)) return interaction.reply({ content: t('rolePermission'), ephemeral: true });
+ console.log(t('forceSkip'));
+ if (playerStatus === 0 || playerStatus === 1) {
votes.clear();
// Do something to skip the audio track here (e.g. player.stop())
await commandCheck(interaction, bot);
- } else if (playerState === 'Stopped') {
- return await interaction.reply({ content: 'Cannot play next music. Player is currently stopped...', ephemeral: true });
+ } else if (playerStatus === 2) {
+ return await interaction.reply({ content: t('cannotPlay'), ephemeral: true });
}
}
}
diff --git a/Utilities/i18n.js b/Utilities/i18n.js
index c667c4c..9b431a1 100644
--- a/Utilities/i18n.js
+++ b/Utilities/i18n.js
@@ -20,10 +20,12 @@
***************************************************************************/
import i18next from 'i18next';
import fsBackend from 'i18next-fs-backend';
+import { readFileSync } from 'node:fs';
+const { locale } = JSON.parse(readFileSync('./config.json', 'utf-8'));
i18next.use(fsBackend).init({
- lng: 'en', // if you're using a language detector, do not define the lng option
- debug: true,
+ lng: locale, // if you're using a language detector, do not define the lng option
+ debug: false,
fallbackLng: 'en',
backend: {
loadPath: './Locales/{{lng}}/{{ns}}.json'
@@ -32,7 +34,7 @@ i18next.use(fsBackend).init({
export default {
i18next,
- t(key) {
- return i18next.t(key);
+ t(key, option) {
+ return i18next.t(key, option);
}
};
diff --git a/bot.js b/bot.js
index e6bd3c0..9376c2a 100644
--- a/bot.js
+++ b/bot.js
@@ -41,13 +41,13 @@ for (const file of commandFiles) {
bot.once(Events.ClientReady, async() => {
console.log(t('botReady'));
console.log(t('loggedIn', { bot: bot.user.tag }));
- console.log(`Running on Discord.JS ${version}`);
- console.log(`Voice Channel: ${voiceChannel}`);
- console.log(`Status Channel: ${statusChannel}`);
- console.log(`DJ Role: ${djRole}`);
- console.log(`Owner ID: ${ownerID}`);
- console.log(`Shuffle Enabled: ${shuffle}`);
- console.log(`Repeat Enabled: ${repeat}`);
+ console.log(t('discordVersion', { version }));
+ console.log(t('voiceChannel', { voiceChannel }));
+ console.log(t('statusChannel', { statusChannel }));
+ console.log(t('djRole', { djRole }));
+ console.log(t('ownerID', { ownerID }));
+ console.log(t('shuffleEnabled', { shuffle }));
+ console.log(t('repeatEnabled', { repeat }));
// Set bots' presence
bot.user.setPresence({
@@ -59,16 +59,16 @@ bot.once(Events.ClientReady, async() => {
});
const activity = bot.presence.activities[0];
- console.log(`Updated bot presence to "${activity.name}"`);
+ console.log(t('presenceSet', { activity: activity.name }));
// Send bots' status to channel
const readyEmbed = new EmbedBuilder()
.setAuthor({ name: bot.user.username, iconURL: bot.user.avatarURL() })
- .setDescription('Starting bot...')
+ .setDescription(t('startingBot'))
.setColor('#0066ff');
const channel = bot.channels.cache.get(statusChannel);
- if (!channel) return console.error('The status channel does not exist! Skipping.');
+ if (!channel) return console.error(t('statusChannelError'));
await channel.send({ embeds: [readyEmbed] });
return await voiceInit(bot);
@@ -85,6 +85,6 @@ bot.on(Events.InteractionCreate, async interaction => {
await command.execute(interaction, bot);
} catch (e) {
console.error(e);
- await interaction.reply({ content: `There was an error while executing this command...\nShare this to the bot owner or report it to the git repository in \`/about\`\n\nDetails:\`\`\`${e}\`\`\``, ephemeral: true });
+ await interaction.reply({ content: t('exception', { e }), ephemeral: true });
}
});
diff --git a/package.json b/package.json
index c13b265..1383003 100644
--- a/package.json
+++ b/package.json
@@ -10,14 +10,14 @@
},
"dependencies": {
"@discordjs/opus": "^0.9.0",
- "@discordjs/rest": "^2.0.1",
- "@discordjs/voice": "^0.16.0",
- "discord-api-types": "^0.37.60",
- "discord.js": "^14.13.0",
+ "@discordjs/rest": "^2.2.0",
+ "@discordjs/voice": "^0.16.1",
+ "discord-api-types": "^0.37.69",
+ "discord.js": "^14.14.1",
"ffmpeg-static": "^5.2.0",
- "i18next": "^23.5.1",
- "i18next-fs-backend": "^2.2.0",
- "music-metadata": "^8.1.4",
+ "i18next": "^23.8.2",
+ "i18next-fs-backend": "^2.3.1",
+ "music-metadata": "^7.14.0",
"sodium": "^3.0.2"
},
"devDependencies": {
diff --git a/yarn.lock b/yarn.lock
index 731705a..7e3a0e1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,10 +2,10 @@
# yarn lockfile v1
-"@babel/runtime@^7.22.5":
- version "7.23.2"
- resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885"
- integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==
+"@babel/runtime@^7.23.2":
+ version "7.23.9"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.9.tgz#47791a15e4603bb5f905bc0753801cf21d6345f7"
+ integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==
dependencies:
regenerator-runtime "^0.14.0"
@@ -19,30 +19,35 @@
http-response-object "^3.0.1"
parse-cache-control "^1.0.1"
-"@discordjs/builders@^1.6.5":
- version "1.6.5"
- resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.6.5.tgz#3e23912eaab1d542b61ca0fa7202e5aaef2b7200"
- integrity sha512-SdweyCs/+mHj+PNhGLLle7RrRFX9ZAhzynHahMCLqp5Zeq7np7XC6/mgzHc79QoVlQ1zZtOkTTiJpOZu5V8Ufg==
+"@discordjs/builders@^1.7.0":
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.7.0.tgz#e2478c7e55b0f4c40837edb8f102bce977323a37"
+ integrity sha512-GDtbKMkg433cOZur8Dv6c25EHxduNIBsxeHrsRoIM8+AwmEZ8r0tEpckx/sHwTLwQPOF3e2JWloZh9ofCaMfAw==
dependencies:
- "@discordjs/formatters" "^0.3.2"
- "@discordjs/util" "^1.0.1"
- "@sapphire/shapeshift" "^3.9.2"
- discord-api-types "0.37.50"
+ "@discordjs/formatters" "^0.3.3"
+ "@discordjs/util" "^1.0.2"
+ "@sapphire/shapeshift" "^3.9.3"
+ discord-api-types "0.37.61"
fast-deep-equal "^3.1.3"
ts-mixer "^6.0.3"
- tslib "^2.6.1"
+ tslib "^2.6.2"
-"@discordjs/collection@^1.5.3":
+"@discordjs/collection@1.5.3":
version "1.5.3"
resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-1.5.3.tgz#5a1250159ebfff9efa4f963cfa7e97f1b291be18"
integrity sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==
-"@discordjs/formatters@^0.3.2":
- version "0.3.2"
- resolved "https://registry.yarnpkg.com/@discordjs/formatters/-/formatters-0.3.2.tgz#3ae054f7b3097cc0dc7645fade37a3f20fa1fb4b"
- integrity sha512-lE++JZK8LSSDRM5nLjhuvWhGuKiXqu+JZ/DsOR89DVVia3z9fdCJVcHF2W/1Zxgq0re7kCzmAJlCMMX3tetKpA==
+"@discordjs/collection@^2.0.0":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-2.0.0.tgz#409b80c74eb8486cc4ee6a9b83426aaff1380f8c"
+ integrity sha512-YTWIXLrf5FsrLMycpMM9Q6vnZoR/lN2AWX23/Cuo8uOOtS8eHB2dyQaaGnaF8aZPYnttf2bkLMcXn/j6JUOi3w==
+
+"@discordjs/formatters@^0.3.3":
+ version "0.3.3"
+ resolved "https://registry.yarnpkg.com/@discordjs/formatters/-/formatters-0.3.3.tgz#b16fdd79bb819680ab7e519193004e9dc124a749"
+ integrity sha512-wTcI1Q5cps1eSGhl6+6AzzZkBBlVrBdc9IUhJbijRgVjCNIIIZPgqnUj3ntFODsHrdbGU8BEG9XmDQmgEEYn3w==
dependencies:
- discord-api-types "0.37.50"
+ discord-api-types "0.37.61"
"@discordjs/node-pre-gyp@^0.4.5":
version "0.4.5"
@@ -67,51 +72,51 @@
"@discordjs/node-pre-gyp" "^0.4.5"
node-addon-api "^5.0.0"
-"@discordjs/rest@^2.0.1":
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/@discordjs/rest/-/rest-2.0.1.tgz#100c208a964e54b8d7cd418bbaed279c816b8ec5"
- integrity sha512-/eWAdDRvwX/rIE2tuQUmKaxmWeHmGealttIzGzlYfI4+a7y9b6ZoMp8BG/jaohs8D8iEnCNYaZiOFLVFLQb8Zg==
+"@discordjs/rest@^2.1.0", "@discordjs/rest@^2.2.0":
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/@discordjs/rest/-/rest-2.2.0.tgz#f4ec00d3faff965c00a879b7e87bb4b6f4446966"
+ integrity sha512-nXm9wT8oqrYFRMEqTXQx9DUTeEtXUDMmnUKIhZn6O2EeDY9VCdwj23XCPq7fkqMPKdF7ldAfeVKyxxFdbZl59A==
dependencies:
- "@discordjs/collection" "^1.5.3"
- "@discordjs/util" "^1.0.1"
+ "@discordjs/collection" "^2.0.0"
+ "@discordjs/util" "^1.0.2"
"@sapphire/async-queue" "^1.5.0"
"@sapphire/snowflake" "^3.5.1"
"@vladfrangu/async_event_emitter" "^2.2.2"
- discord-api-types "0.37.50"
- magic-bytes.js "^1.0.15"
- tslib "^2.6.1"
- undici "5.22.1"
+ discord-api-types "0.37.61"
+ magic-bytes.js "^1.5.0"
+ tslib "^2.6.2"
+ undici "5.27.2"
-"@discordjs/util@^1.0.1":
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/@discordjs/util/-/util-1.0.1.tgz#7d6f97b65425d3a8b46ea1180150dee6991a88cf"
- integrity sha512-d0N2yCxB8r4bn00/hvFZwM7goDcUhtViC5un4hPj73Ba4yrChLSJD8fy7Ps5jpTLg1fE9n4K0xBLc1y9WGwSsA==
+"@discordjs/util@^1.0.2":
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/@discordjs/util/-/util-1.0.2.tgz#dc1896d764452b1bd9707eb9aa99ccfbb30bd1c0"
+ integrity sha512-IRNbimrmfb75GMNEjyznqM1tkI7HrZOf14njX7tCAAUetyZM1Pr8hX/EK2lxBCOgWDRmigbp24fD1hdMfQK5lw==
-"@discordjs/voice@^0.16.0":
- version "0.16.0"
- resolved "https://registry.yarnpkg.com/@discordjs/voice/-/voice-0.16.0.tgz#9df1e492c8fea95113236a3de3ac52702c587729"
- integrity sha512-ToGCvHD1cBscuW3p+C7zOF5+L7MJmU4GjdOARfNk9mkHyFFZq4grK+Sxr3QXKbp27DtfDBc9uqD4GUOYgxngfA==
+"@discordjs/voice@^0.16.1":
+ version "0.16.1"
+ resolved "https://registry.yarnpkg.com/@discordjs/voice/-/voice-0.16.1.tgz#c4ad52b9308875d0462e22681060aecb97675941"
+ integrity sha512-uiWiW0Ta6K473yf8zs13RfKuPqm/xU4m4dAidMkIdwqgy1CztbbZBtPLfDkVSKzpW7s6m072C+uQcs4LwF3FhA==
dependencies:
- "@types/ws" "^8.5.4"
- discord-api-types "^0.37.37"
+ "@types/ws" "^8.5.9"
+ discord-api-types "0.37.61"
prism-media "^1.3.5"
- tslib "^2.5.0"
- ws "^8.13.0"
+ tslib "^2.6.2"
+ ws "^8.14.2"
-"@discordjs/ws@^1.0.1":
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/@discordjs/ws/-/ws-1.0.1.tgz#fab8aa4c1667040a95b5268a2875add27353d323"
- integrity sha512-avvAolBqN3yrSvdBPcJ/0j2g42ABzrv3PEL76e3YTp2WYMGH7cuspkjfSyNWaqYl1J+669dlLp+YFMxSVQyS5g==
+"@discordjs/ws@^1.0.2":
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/@discordjs/ws/-/ws-1.0.2.tgz#3933b12d4686aabf6a95dfe5fb6e744342a661d1"
+ integrity sha512-+XI82Rm2hKnFwAySXEep4A7Kfoowt6weO6381jgW+wVdTpMS/56qCvoXyFRY0slcv7c/U8My2PwIB2/wEaAh7Q==
dependencies:
- "@discordjs/collection" "^1.5.3"
- "@discordjs/rest" "^2.0.1"
- "@discordjs/util" "^1.0.1"
+ "@discordjs/collection" "^2.0.0"
+ "@discordjs/rest" "^2.1.0"
+ "@discordjs/util" "^1.0.2"
"@sapphire/async-queue" "^1.5.0"
- "@types/ws" "^8.5.5"
+ "@types/ws" "^8.5.9"
"@vladfrangu/async_event_emitter" "^2.2.2"
- discord-api-types "0.37.50"
- tslib "^2.6.1"
- ws "^8.13.0"
+ discord-api-types "0.37.61"
+ tslib "^2.6.2"
+ ws "^8.14.2"
"@eslint-community/eslint-utils@^4.2.0":
version "4.2.0"
@@ -145,6 +150,11 @@
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.36.0.tgz#9837f768c03a1e4a30bd304a64fb8844f0e72efe"
integrity sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==
+"@fastify/busboy@^2.0.0":
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.0.tgz#0709e9f4cb252351c609c6e6d8d6779a8d25edff"
+ integrity sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==
+
"@humanwhocodes/config-array@^0.11.8":
version "0.11.8"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9"
@@ -190,15 +200,15 @@
resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.5.0.tgz#2f255a3f186635c4fb5a2381e375d3dfbc5312d8"
integrity sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==
-"@sapphire/shapeshift@^3.9.2":
- version "3.9.3"
- resolved "https://registry.yarnpkg.com/@sapphire/shapeshift/-/shapeshift-3.9.3.tgz#89d26713044bc21cc5e0845e61a8a328ca3c1a84"
- integrity sha512-WzKJSwDYloSkHoBbE8rkRW8UNKJiSRJ/P8NqJ5iVq7U2Yr/kriIBx2hW+wj2Z5e5EnXL1hgYomgaFsdK6b+zqQ==
+"@sapphire/shapeshift@^3.9.3":
+ version "3.9.6"
+ resolved "https://registry.yarnpkg.com/@sapphire/shapeshift/-/shapeshift-3.9.6.tgz#bd9629c08641f5b94ae094e23f092187a3ed9a7d"
+ integrity sha512-4+Na/fxu2SEepZRb9z0dbsVh59QtwPuBg/UVaDib3av7ZY14b14+z09z6QVn0P6Dv6eOU2NDTsjIi0mbtgP56g==
dependencies:
fast-deep-equal "^3.1.3"
lodash "^4.17.21"
-"@sapphire/snowflake@^3.5.1":
+"@sapphire/snowflake@3.5.1", "@sapphire/snowflake@^3.5.1":
version "3.5.1"
resolved "https://registry.yarnpkg.com/@sapphire/snowflake/-/snowflake-3.5.1.tgz#254521c188b49e8b2d4cc048b475fb2b38737fec"
integrity sha512-BxcYGzgEsdlG0dKAyOm0ehLGm2CafIrfQTZGWgkfKYbj+pNNsorZ7EotuZukc2MT70E0UbppVbtpBrqpzVzjNA==
@@ -223,17 +233,17 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b"
integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==
-"@types/ws@^8.5.4":
- version "8.5.4"
- resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5"
- integrity sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==
+"@types/ws@8.5.9":
+ version "8.5.9"
+ resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.9.tgz#384c489f99c83225a53f01ebc3eddf3b8e202a8c"
+ integrity sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg==
dependencies:
"@types/node" "*"
-"@types/ws@^8.5.5":
- version "8.5.7"
- resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.7.tgz#1ca585074fe5d2c81dec7a3d451f244a2a6d83cb"
- integrity sha512-6UrLjiDUvn40CMrAubXuIVtj2PEfKDffJS7ychvnPU44j+KVeXmdHHTgqcM/dxLUTHxlXHiFM8Skmb8ozGdTnQ==
+"@types/ws@^8.5.9":
+ version "8.5.10"
+ resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787"
+ integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==
dependencies:
"@types/node" "*"
@@ -385,13 +395,6 @@ builtins@^5.0.1:
dependencies:
semver "^7.0.0"
-busboy@^1.6.0:
- version "1.6.0"
- resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
- integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==
- dependencies:
- streamsearch "^1.1.0"
-
call-bind@^1.0.0, call-bind@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
@@ -526,35 +529,35 @@ detect-libc@^2.0.0:
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd"
integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==
-discord-api-types@0.37.50:
- version "0.37.50"
- resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.50.tgz#6059eb8c0b784ad8194655a8b8b7f540fcfac428"
- integrity sha512-X4CDiMnDbA3s3RaUXWXmgAIbY1uxab3fqe3qwzg5XutR3wjqi7M3IkgQbsIBzpqBN2YWr/Qdv7JrFRqSgb4TFg==
-
-discord-api-types@^0.37.37, discord-api-types@^0.37.60:
- version "0.37.60"
- resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.60.tgz#435855217afcf867e6ea20d0a750b23550b734ed"
- integrity sha512-5BELXTsv7becqVHrD81nZrqT4oEyXXWBwbsO/kwDDu6X3u19VV1tYDB5I5vaVAK+c1chcDeheI9zACBLm41LiQ==
-
-discord.js@^14.13.0:
- version "14.13.0"
- resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.13.0.tgz#e7a00bdba70adb9e266a06884ca1acaf9a0b5c20"
- integrity sha512-Kufdvg7fpyTEwANGy9x7i4od4yu5c6gVddGi5CKm4Y5a6sF0VBODObI3o0Bh7TGCj0LfNT8Qp8z04wnLFzgnbA==
- dependencies:
- "@discordjs/builders" "^1.6.5"
- "@discordjs/collection" "^1.5.3"
- "@discordjs/formatters" "^0.3.2"
- "@discordjs/rest" "^2.0.1"
- "@discordjs/util" "^1.0.1"
- "@discordjs/ws" "^1.0.1"
- "@sapphire/snowflake" "^3.5.1"
- "@types/ws" "^8.5.5"
- discord-api-types "0.37.50"
- fast-deep-equal "^3.1.3"
- lodash.snakecase "^4.1.1"
- tslib "^2.6.1"
- undici "5.22.1"
- ws "^8.13.0"
+discord-api-types@0.37.61:
+ version "0.37.61"
+ resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.61.tgz#9dd8e58c624237e6f1b23be2d29579af268b8c5b"
+ integrity sha512-o/dXNFfhBpYHpQFdT6FWzeO7pKc838QeeZ9d91CfVAtpr5XLK4B/zYxQbYgPdoMiTDvJfzcsLW5naXgmHGDNXw==
+
+discord-api-types@^0.37.69:
+ version "0.37.69"
+ resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.69.tgz#4c78a03ef247dd8d6b95e4eaf199ed8d000a3955"
+ integrity sha512-c0rHc5YGNIXQkI+V7QwP8y77wxo74ITNeZmMwxtKC/l01aIF/gKBG/U2MKhUt2iaeRH9XwAt9PT3AI9JQVvKVA==
+
+discord.js@^14.14.1:
+ version "14.14.1"
+ resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.14.1.tgz#9a2bea23bba13819705ab87612837610abce9ee3"
+ integrity sha512-/hUVzkIerxKHyRKopJy5xejp4MYKDPTszAnpYxzVVv4qJYf+Tkt+jnT2N29PIPschicaEEpXwF2ARrTYHYwQ5w==
+ dependencies:
+ "@discordjs/builders" "^1.7.0"
+ "@discordjs/collection" "1.5.3"
+ "@discordjs/formatters" "^0.3.3"
+ "@discordjs/rest" "^2.1.0"
+ "@discordjs/util" "^1.0.2"
+ "@discordjs/ws" "^1.0.2"
+ "@sapphire/snowflake" "3.5.1"
+ "@types/ws" "8.5.9"
+ discord-api-types "0.37.61"
+ fast-deep-equal "3.1.3"
+ lodash.snakecase "4.1.1"
+ tslib "2.6.2"
+ undici "5.27.2"
+ ws "8.14.2"
doctrine@^2.1.0:
version "2.1.0"
@@ -834,7 +837,7 @@ esutils@^2.0.2:
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
-fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
+fast-deep-equal@3.1.3, fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
@@ -873,14 +876,14 @@ file-entry-cache@^6.0.1:
dependencies:
flat-cache "^3.0.4"
-file-type@^18.2.1:
- version "18.2.1"
- resolved "https://registry.yarnpkg.com/file-type/-/file-type-18.2.1.tgz#6d8f1fa3b079606f6ecf89483346f55fcd2c671b"
- integrity sha512-Yw5MtnMv7vgD2/6Bjmmuegc8bQEVA9GmAyaR18bMYWKqsWDG9wgYZ1j4I6gNMF5Y5JBDcUcjRQqNQx7Y8uotcg==
+file-type@^16.5.4:
+ version "16.5.4"
+ resolved "https://registry.yarnpkg.com/file-type/-/file-type-16.5.4.tgz#474fb4f704bee427681f98dd390058a172a6c2fd"
+ integrity sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==
dependencies:
- readable-web-to-node-stream "^3.0.2"
- strtok3 "^7.0.0"
- token-types "^5.0.1"
+ readable-web-to-node-stream "^3.0.0"
+ strtok3 "^6.2.4"
+ token-types "^4.1.1"
fill-range@^7.0.1:
version "7.0.1"
@@ -1104,17 +1107,17 @@ https-proxy-agent@^5.0.0:
agent-base "6"
debug "4"
-i18next-fs-backend@^2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/i18next-fs-backend/-/i18next-fs-backend-2.2.0.tgz#016c865344632a666ea80653deae466fbfa6042c"
- integrity sha512-VOPHhdDX0M/csRqEw+9Ectpf6wvTIg1MZDfAHxc3JKnAlJz7fcZSAKAeyDohOq0xuLx57esYpJopIvBaRb0Bag==
+i18next-fs-backend@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/i18next-fs-backend/-/i18next-fs-backend-2.3.1.tgz#0c7d2459ff4a039e2b3228131809fbc0e74ff1a8"
+ integrity sha512-tvfXskmG/9o+TJ5Fxu54sSO5OkY6d+uMn+K6JiUGLJrwxAVfer+8V3nU8jq3ts9Pe5lXJv4b1N7foIjJ8Iy2Gg==
-i18next@^23.5.1:
- version "23.5.1"
- resolved "https://registry.yarnpkg.com/i18next/-/i18next-23.5.1.tgz#7f7c35ffaa907618d9489f106d5006b09fbca3d3"
- integrity sha512-JelYzcaCoFDaa+Ysbfz2JsGAKkrHiMG6S61+HLBUEIPaF40WMwW9hCPymlQGrP+wWawKxKPuSuD71WZscCsWHg==
+i18next@^23.8.2:
+ version "23.8.2"
+ resolved "https://registry.yarnpkg.com/i18next/-/i18next-23.8.2.tgz#f3ff6ea929e0927d9717f0ed195ae46d05919900"
+ integrity sha512-Z84zyEangrlERm0ZugVy4bIt485e/H8VecGUZkZWrH7BDePG6jT73QdL9EA1tRTTVVMpry/MgWIP1FjEn0DRXA==
dependencies:
- "@babel/runtime" "^7.22.5"
+ "@babel/runtime" "^7.23.2"
ieee754@^1.2.1:
version "1.2.1"
@@ -1356,7 +1359,7 @@ lodash.merge@^4.6.2:
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
-lodash.snakecase@^4.1.1:
+lodash.snakecase@4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d"
integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==
@@ -1373,10 +1376,10 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"
-magic-bytes.js@^1.0.15:
- version "1.5.0"
- resolved "https://registry.yarnpkg.com/magic-bytes.js/-/magic-bytes.js-1.5.0.tgz#f5531ca53e1c8dab5692b8dcfb360f7ca6c6b6bc"
- integrity sha512-wJkXvutRbNWcc37tt5j1HyOK1nosspdh3dj6LUYYAvF6JYNqs53IfRvK9oEpcwiDA1NdoIi64yAMfdivPeVAyw==
+magic-bytes.js@^1.5.0:
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/magic-bytes.js/-/magic-bytes.js-1.8.0.tgz#8362793c60cd77c2dd77db6420be727192df68e2"
+ integrity sha512-lyWpfvNGVb5lu8YUAbER0+UMBTdR63w2mcSUlhhBTyVbxJvjgqwyAf3AZD6MprgK0uHuBoWXSDAMWLupX83o3Q==
make-dir@^3.1.0:
version "3.1.0"
@@ -1437,18 +1440,18 @@ ms@^2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
-music-metadata@^8.1.4:
- version "8.1.4"
- resolved "https://registry.yarnpkg.com/music-metadata/-/music-metadata-8.1.4.tgz#a65e59366bf347821ae2ea4fb0661bf202e37b7c"
- integrity sha512-q9mw2qeESeJY69cXtdaum/YJstDimpP+mwZnb801iq20JpyY75v6uzcp6VfVXZDixpD2f9yWneJtA0TgSEypxA==
+music-metadata@^7.14.0:
+ version "7.14.0"
+ resolved "https://registry.yarnpkg.com/music-metadata/-/music-metadata-7.14.0.tgz#74e3e5fc8e09b86d1a3e791fb5ce9ccdc4347ad9"
+ integrity sha512-xrm3w7SV0Wk+OythZcSbaI8mcr/KHd0knJieu8bVpaPfMv/Agz5EooCAPz3OR5hbYMiUG6dgAPKZKnMzV+3amA==
dependencies:
"@tokenizer/token" "^0.3.0"
content-type "^1.0.5"
debug "^4.3.4"
- file-type "^18.2.1"
+ file-type "^16.5.4"
media-typer "^1.1.0"
- strtok3 "^7.0.0"
- token-types "^5.0.1"
+ strtok3 "^6.3.0"
+ token-types "^4.2.1"
natural-compare@^1.4.0:
version "1.4.0"
@@ -1616,10 +1619,10 @@ path-parse@^1.0.7:
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
-peek-readable@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-5.0.0.tgz#7ead2aff25dc40458c60347ea76cfdfd63efdfec"
- integrity sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==
+peek-readable@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-4.1.0.tgz#4ece1111bf5c2ad8867c314c81356847e8a62e72"
+ integrity sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==
picomatch@^2.0.4, picomatch@^2.2.1:
version "2.3.1"
@@ -1665,7 +1668,7 @@ readable-stream@^3.0.2, readable-stream@^3.6.0:
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
-readable-web-to-node-stream@^3.0.2:
+readable-web-to-node-stream@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz#5d52bb5df7b54861fd48d015e93a2cb87b3ee0bb"
integrity sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==
@@ -1812,11 +1815,6 @@ sodium@^3.0.2:
dependencies:
node-addon-api "*"
-streamsearch@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
- integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
-
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
@@ -1868,13 +1866,13 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
-strtok3@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-7.0.0.tgz#868c428b4ade64a8fd8fee7364256001c1a4cbe5"
- integrity sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==
+strtok3@^6.2.4, strtok3@^6.3.0:
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-6.3.0.tgz#358b80ffe6d5d5620e19a073aa78ce947a90f9a0"
+ integrity sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==
dependencies:
"@tokenizer/token" "^0.3.0"
- peek-readable "^5.0.0"
+ peek-readable "^4.1.0"
supports-color@^5.5.0:
version "5.5.0"
@@ -1919,10 +1917,10 @@ to-regex-range@^5.0.1:
dependencies:
is-number "^7.0.0"
-token-types@^5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/token-types/-/token-types-5.0.1.tgz#aa9d9e6b23c420a675e55413b180635b86a093b4"
- integrity sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==
+token-types@^4.1.1, token-types@^4.2.1:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/token-types/-/token-types-4.2.1.tgz#0f897f03665846982806e138977dbe72d44df753"
+ integrity sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==
dependencies:
"@tokenizer/token" "^0.3.0"
ieee754 "^1.2.1"
@@ -1954,12 +1952,7 @@ tsconfig-paths@^3.14.1:
minimist "^1.2.6"
strip-bom "^3.0.0"
-tslib@^2.5.0:
- version "2.5.0"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf"
- integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==
-
-tslib@^2.6.1:
+tslib@2.6.2, tslib@^2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
@@ -2005,12 +1998,12 @@ undefsafe@^2.0.5:
resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c"
integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==
-undici@5.22.1:
- version "5.22.1"
- resolved "https://registry.yarnpkg.com/undici/-/undici-5.22.1.tgz#877d512effef2ac8be65e695f3586922e1a57d7b"
- integrity sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw==
+undici@5.27.2:
+ version "5.27.2"
+ resolved "https://registry.yarnpkg.com/undici/-/undici-5.27.2.tgz#a270c563aea5b46cc0df2550523638c95c5d4411"
+ integrity sha512-iS857PdOEy/y3wlM3yRp+6SNQQ6xU0mmZcwRSriqk+et/cwWAtwmIGf6WkoDN2EK/AMdCO/dfXzIwi+rFMrjjQ==
dependencies:
- busboy "^1.6.0"
+ "@fastify/busboy" "^2.0.0"
uri-js@^4.2.2:
version "4.4.1"
@@ -2084,11 +2077,16 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
-ws@^8.13.0:
+ws@8.14.2:
version "8.14.2"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f"
integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==
+ws@^8.14.2:
+ version "8.16.0"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4"
+ integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==
+
yallist@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"