diff options
| -rw-r--r-- | AudioBackend/AudioControl.js | 2 | ||||
| -rw-r--r-- | AudioBackend/PlayAudio.js | 14 | ||||
| -rw-r--r-- | AudioBackend/VoiceInitialization.js | 2 | ||||
| -rw-r--r-- | Commands/join.js | 7 | ||||
| -rw-r--r-- | Commands/leave.js | 9 | ||||
| -rw-r--r-- | Commands/list.js | 3 | ||||
| -rw-r--r-- | Commands/next.js | 20 | ||||
| -rw-r--r-- | Commands/pause.js | 10 | ||||
| -rw-r--r-- | Commands/play.js | 10 | ||||
| -rw-r--r-- | Commands/previous.js | 16 | ||||
| -rw-r--r-- | Commands/reshuffle.js | 7 | ||||
| -rw-r--r-- | Commands/shutdown.js | 8 | ||||
| -rw-r--r-- | Commands/status.js | 27 | ||||
| -rw-r--r-- | Locales/custom/translation.json | 3 | ||||
| -rw-r--r-- | Locales/en/translation.json | 3 | ||||
| -rw-r--r-- | README.md | 22 | ||||
| -rw-r--r-- | Utilities/Voting.js | 101 | ||||
| -rw-r--r-- | Utilities/i18n.js | 38 | ||||
| -rw-r--r-- | bot.js | 14 | ||||
| -rw-r--r-- | package.json | 1 | ||||
| -rw-r--r-- | yarn.lock | 252 |
21 files changed, 381 insertions, 188 deletions
diff --git a/AudioBackend/AudioControl.js b/AudioBackend/AudioControl.js index 447e040..3a9c7a5 100644 --- a/AudioBackend/AudioControl.js +++ b/AudioBackend/AudioControl.js @@ -55,7 +55,7 @@ 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 }); } else { - await interaction.reply({ content: 'Playing previous music', ephemeral: true }); + await interaction.reply({ content: 'Playing previous music' }); player.stop(); updatePlaylist('back'); return await playAudio(bot); diff --git a/AudioBackend/PlayAudio.js b/AudioBackend/PlayAudio.js index 5d18534..e8a0f3a 100644 --- a/AudioBackend/PlayAudio.js +++ b/AudioBackend/PlayAudio.js @@ -45,7 +45,7 @@ export async function playAudio(bot) { const resource = createAudioResource('music/' + audio); player.play(resource); - console.log('Now playing: ' + audio); + console.log(`Now playing: ${audio}`); audioState(0); @@ -70,7 +70,7 @@ export async function playAudio(bot) { audio = audio.split('.').slice(0, -1).join('.'); if (txtFile) { - fileData = 'Now Playing: ' + audio; + fileData = `Now Playing: ${audio}`; writeFile('./now-playing.txt', fileData, (err) => { if (err) { console.log(err); } }); @@ -80,17 +80,17 @@ export async function playAudio(bot) { if (metadataEmpty) { statusEmbed.setTitle('Now Playing'); statusEmbed.addFields( - { name: 'Title', value: audio }, - { name: 'Duration', value: duration } + { name: 'Title', value: `${audio}` }, + { name: 'Duration', value: `${duration}` } ); statusEmbed.setColor('#0066ff'); } else { statusEmbed.setTitle('Now Playing'); statusEmbed.addFields( - { name: 'Title', value: audioTitle, inline: true }, - { name: 'Artist', value: audioArtist, inline: true }, + { name: 'Title', value: `${audioTitle}`, inline: true }, + { name: 'Artist', value: `${audioArtist}`, inline: true }, { name: 'Year', value: `${audioYear}` }, - { name: 'Duration', value: duration } + { name: 'Duration', value: `${duration}` } ); statusEmbed.setFooter({ text: `Album: ${audioAlbum}\nFilename: ${audioFile}` }); statusEmbed.setColor('#0066ff'); diff --git a/AudioBackend/VoiceInitialization.js b/AudioBackend/VoiceInitialization.js index a9e1149..ae1241b 100644 --- a/AudioBackend/VoiceInitialization.js +++ b/AudioBackend/VoiceInitialization.js @@ -22,6 +22,7 @@ import { readFileSync } from 'node:fs'; import { createAudioPlayer, joinVoiceChannel, VoiceConnectionStatus } from '@discordjs/voice'; import { nextAudio } from './AudioControl.js'; import { shufflePlaylist, orderPlaylist } from './QueueSystem.js'; +import { votes } from '../Utilities/Voting.js'; const { voiceChannel, shuffle } = JSON.parse(readFileSync('./config.json', 'utf-8')); export const player = createAudioPlayer(); @@ -53,6 +54,7 @@ export async function voiceInit(bot) { player.on('idle', () => { console.log('Beat has finished playing, now playing next beat...'); + votes.clear(); nextAudio(bot); }); diff --git a/Commands/join.js b/Commands/join.js index 739ebba..2649784 100644 --- a/Commands/join.js +++ b/Commands/join.js @@ -22,14 +22,17 @@ import { SlashCommandBuilder } from 'discord.js'; import { voiceInit } from '../AudioBackend/VoiceInitialization.js'; import { PermissionFlagsBits } from 'discord-api-types/v10'; +import { readFileSync } from 'node:fs'; +const { djRole, ownerID } = JSON.parse(readFileSync('./config.json', 'utf-8')); export default { data: new SlashCommandBuilder() .setName('join') - .setDescription('Joins voice chat') - .setDefaultMemberPermissions(PermissionFlagsBits.Administrator), + .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.member.permission.has(PermissionFlagsBits.ManageGuild)) return interaction.reply({ content: 'You need a specific role to execute this command', ephemeral: true }); + await interaction.reply({ content: 'Joining voice channel', ephemeral: true }); return await voiceInit(bot); } diff --git a/Commands/leave.js b/Commands/leave.js index cea791a..5d01822 100644 --- a/Commands/leave.js +++ b/Commands/leave.js @@ -22,14 +22,17 @@ import { SlashCommandBuilder } from 'discord.js'; import { destroyAudio } from '../AudioBackend/Shutdown.js'; import { PermissionFlagsBits } from 'discord-api-types/v10'; +import { readFileSync } from 'node:fs'; +const { djRole, ownerID } = JSON.parse(readFileSync('./config.json', 'utf-8')); export default { data: new SlashCommandBuilder() .setName('leave') - .setDescription('Leaves the voice chat') - .setDefaultMemberPermissions(PermissionFlagsBits.Administrator), - async execute(interaction) { + .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.member.permission.has(PermissionFlagsBits.ManageGuild)) return interaction.reply({ content: 'You need a specific role to execute this command', ephemeral: true }); + console.log('Leaving voice channel...'); await destroyAudio(interaction); return await interaction.reply({ content: 'Leaving voice channel', ephemeral: true }); diff --git a/Commands/list.js b/Commands/list.js index 176ae61..90d3cc6 100644 --- a/Commands/list.js +++ b/Commands/list.js @@ -18,6 +18,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. * ***************************************************************************/ + import { EmbedBuilder, SlashCommandBuilder } from 'discord.js'; import { readdir } from 'node:fs'; @@ -38,7 +39,7 @@ export default { console.error(err); } else { const trackList = files.map((file, i) => `${i}: ${file}`); // Create an array of track names - const pageSize = 10; // Number of tracks per page + 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 }); diff --git a/Commands/next.js b/Commands/next.js index d3240cf..f082c89 100644 --- a/Commands/next.js +++ b/Commands/next.js @@ -20,23 +20,21 @@ ***************************************************************************/ import { SlashCommandBuilder } from 'discord.js'; -import { player } from '../AudioBackend/VoiceInitialization.js'; -import { nextAudio, playerState } from '../AudioBackend/AudioControl.js'; -import { PermissionFlagsBits } from 'discord-api-types/v10'; +import { voteSkip } from '../Utilities/Voting.js'; export default { data: new SlashCommandBuilder() .setName('next') .setDescription('Goes to next music') - .setDefaultMemberPermissions(PermissionFlagsBits.Administrator), + .addSubcommand(subcommand => + subcommand.setName('vote') + .setDescription('Voting to skip this audio track')) + .addSubcommand(subcommand => + 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 (playerState === 'Playing' || playerState === 'Paused') { - await interaction.reply({ content: 'Playing next music', ephemeral: true }); - player.stop(); - return await nextAudio(bot); - } else if (playerState === 'Stopped') { - return await interaction.reply({ content: 'Cannot play next music. Player is currently stopped...', ephemeral: true }); - } + await voteSkip(interaction, bot); } }; diff --git a/Commands/pause.js b/Commands/pause.js index e461a4c..51376cd 100644 --- a/Commands/pause.js +++ b/Commands/pause.js @@ -22,14 +22,16 @@ import { SlashCommandBuilder } from 'discord.js'; import { toggleAudioState, isAudioStatePaused } from '../AudioBackend/AudioControl.js'; import { PermissionFlagsBits } from 'discord-api-types/v10'; - +import { readFileSync } from 'node:fs'; +const { djRole } = JSON.parse(readFileSync('./config.json', 'utf-8')); export default { data: new SlashCommandBuilder() .setName('pause') - .setDescription('Pauses music') - .setDefaultMemberPermissions(PermissionFlagsBits.Administrator), - async execute(interaction) { + .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.member.permission.has(PermissionFlagsBits.ManageGuild)) return interaction.reply({ content: 'You need a specific role to execute this command', ephemeral: true }); + if (!isAudioStatePaused) { toggleAudioState(); return await interaction.reply({ content: 'Pausing music', ephemeral: true }); diff --git a/Commands/play.js b/Commands/play.js index a0af3f3..49f0d3e 100644 --- a/Commands/play.js +++ b/Commands/play.js @@ -24,6 +24,10 @@ import { inputAudio } from '../AudioBackend/QueueSystem.js'; import { files, isAudioStatePaused, toggleAudioState } from '../AudioBackend/AudioControl.js'; import { audio } from '../AudioBackend/PlayAudio.js'; import { PermissionFlagsBits } from 'discord-api-types/v10'; +import { readFileSync } from 'node:fs'; +import { votes } from '../Utilities/Voting.js'; + +const { djRole, ownerID } = JSON.parse(readFileSync('./config.json', 'utf-8')); export let integer; @@ -34,15 +38,17 @@ export default { .addIntegerOption(option => option.setName('int') .setDescription('Input a number for the selection for the audio file.') - ) - .setDefaultMemberPermissions(PermissionFlagsBits.Administrator), + ), 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.member.permission.has(PermissionFlagsBits.ManageGuild)) return interaction.reply({ content: 'You need a specific role to execute this command', 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 }); } else { return await interaction.reply({ content: 'Number is too big, choose a number that\'s less than ' + files.length + '.', ephemeral: true }); diff --git a/Commands/previous.js b/Commands/previous.js index dd79a98..aa732b2 100644 --- a/Commands/previous.js +++ b/Commands/previous.js @@ -20,20 +20,20 @@ ***************************************************************************/ import { SlashCommandBuilder } from 'discord.js'; -import { playerState, previousAudio } from '../AudioBackend/AudioControl.js'; -import { PermissionFlagsBits } from 'discord-api-types/v10'; +import { voteSkip } from '../Utilities/Voting.js'; export default { data: new SlashCommandBuilder() .setName('previous') .setDescription('Goes to previous music') - .setDefaultMemberPermissions(PermissionFlagsBits.Administrator), + .addSubcommand(subcommand => + subcommand.setName('vote') + .setDescription('Voting to skip this audio track')) + .addSubcommand(subcommand => + 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 (playerState === 'Playing' || playerState === 'Paused') { - return await previousAudio(bot, interaction); - } else if (playerState === 'Stopped') { - return await interaction.reply({ content: 'Cannot play next music. Player is currently stopped...', ephemeral: true }); - } + await voteSkip(interaction, bot); } }; diff --git a/Commands/reshuffle.js b/Commands/reshuffle.js index f3e70c2..340c91b 100644 --- a/Commands/reshuffle.js +++ b/Commands/reshuffle.js @@ -25,15 +25,16 @@ import { PermissionFlagsBits } from 'discord-api-types/v10'; import { readFileSync } from 'node:fs'; import { audioState } from '../AudioBackend/AudioControl.js'; // import config from './config.json' assert {type: 'json'} -const { shuffle } = JSON.parse(readFileSync('./config.json', 'utf-8')); +const { shuffle, djRole, ownerID } = JSON.parse(readFileSync('./config.json', 'utf-8')); export default { data: new SlashCommandBuilder() .setName('reshuffle') - .setDescription('Reshuffles the playlist') - .setDefaultMemberPermissions(PermissionFlagsBits.Administrator), + .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.member.permission.has(PermissionFlagsBits.ManageGuild)) return interaction.reply({ content: 'You need a specific role to execute this command', ephemeral: true }); + async function shuffleDetected(bot) { await interaction.reply({ content: 'Reshuffling the playlist...', ephemeral: true }); await audioState(2); diff --git a/Commands/shutdown.js b/Commands/shutdown.js index d97922c..eefbab3 100644 --- a/Commands/shutdown.js +++ b/Commands/shutdown.js @@ -21,14 +21,14 @@ import { SlashCommandBuilder } from 'discord.js'; import { stopBot } from '../AudioBackend/Shutdown.js'; -import { PermissionFlagsBits } from 'discord-api-types/v10'; - +import { readFileSync } from 'node:fs'; +const { ownerID } = JSON.parse(readFileSync('./config.json', 'utf-8')); export default { data: new SlashCommandBuilder() .setName('shutdown') - .setDescription('Powers off the bot') - .setDefaultMemberPermissions(PermissionFlagsBits.Administrator), + .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 }); return await stopBot(bot, interaction); } diff --git a/Commands/status.js b/Commands/status.js index fc1229d..bf88772 100644 --- a/Commands/status.js +++ b/Commands/status.js @@ -23,6 +23,7 @@ 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 { votes } from '../Utilities/Voting.js'; export default { data: new SlashCommandBuilder() @@ -33,11 +34,19 @@ export default { let audioID = currentTrack; audioID++; - let audioName = files[audioID]; + let audioName; - if (audioName === undefined) { + // Get the members of the voice channel who have not voted yet + const voiceChannel = interaction.member.voice.channel; + const members = voiceChannel.members.filter(m => !votes.has(m.id)); + + // Calculate the number of votes required to skip the audio track + const votesRequired = Math.ceil(members.size / 2); + + if (audioID >= files.length) { audioName = 'Playlist Finished'; } else { + audioName = files[audioID]; if (!metadataEmpty) { try { const { common } = await parseFile('music/' + audioName); @@ -53,22 +62,22 @@ export default { const controlEmbed = new EmbedBuilder() .setAuthor({ name: `${bot.user.username} Status`, iconURL: bot.user.avatarURL() }) .addFields( - { name: 'State', value: playerState }, + { name: 'State', value: `${playerState}` }, { name: 'Tracks', value: `${audioID}/${files.length}` }, - { name: 'Duration', value: duration } - // { name: 'Session Uptime', value: `` } + { name: 'Duration', value: `${duration}` }, + { name: 'Votes Needed', value: `${votesRequired}` } ) .setColor('#0066ff'); if (metadataEmpty) { controlEmbed.addFields( - { name: 'Currently Playing', value: audio }, - { name: 'Up Next', value: audioName } + { name: 'Currently Playing', value: `${audio}` }, + { name: 'Up Next', value: `${audioName}` } ); } else { controlEmbed.addFields( - { name: 'Currently Playing', value: audioTitle }, - { name: 'Up Next', value: audioName } + { name: 'Currently Playing', value: `${audioTitle}` }, + { name: 'Up Next', value: `${audioName}` } ); } interaction.reply({ embeds: [controlEmbed] }); diff --git a/Locales/custom/translation.json b/Locales/custom/translation.json new file mode 100644 index 0000000..f86d2ad --- /dev/null +++ b/Locales/custom/translation.json @@ -0,0 +1,3 @@ +{ + "botReady": "Bot is ready!" +} diff --git a/Locales/en/translation.json b/Locales/en/translation.json new file mode 100644 index 0000000..f86d2ad --- /dev/null +++ b/Locales/en/translation.json @@ -0,0 +1,3 @@ +{ + "botReady": "Bot is ready!" +} @@ -7,6 +7,11 @@ DLAP is a Discord bot that lets you play local audio tracks in your server. With If you want to add a feature or there's anything wrong, feel free to make a fork and put a pull request. +## Looking for Maintainers +As you know, I may not keep up the project at times. I will need to form a team in order to implement new features and make this project better. + +If you want to become a maintainer, you must at least know this source code, JavaScript and NodeJS. Also you must join my discord server (Support Server) to communicate with me. + # Recommended Software Requirements - Latest version of NodeJS (v16.9.0+) - Linux (or WSL for Windows users) @@ -21,9 +26,11 @@ Make a new file called `config.json`. "repeat": true/false, "statusChannel": "channel_id", "voiceChannel": "voice_channel_id", + "clientID": "client_id", + "ownerID": "your_user_id", + "djRole": "role_id", "presenceActivity": "activity_here", - "activityType": [0 (Playing)/1 (Streaming)/2 (Listening)/3 (Watching)/4 (Custom)/5 (Competing)], - "clientID": "client_id" + "activityType": [0 (Playing)/1 (Streaming)/2 (Listening)/3 (Watching)/4 (Custom)/5 (Competing)] } ``` @@ -44,15 +51,17 @@ status - Checks what audio file is playing currently. about - Information about the bot. list - Lists the available audio tracks. list (page) - Input a number to change the page of the list. +next vote - Goes to next music by vote. +previous vote - Goes to previous music by vote. -Bot Owner Only +Special Permissions Only -------------- join - Joins voice chat. play - Resumes music. play (int) - Input a number for the selection for the audio file. pause - Pauses music. -next - Goes to next music. -previous - Goes to previous music. +next force - Goes to next music by force. +previous force - Goes to previous music by force. reshuffle - Reshuffles the playlist. leave - Leaves voice chat. shutdown - Powers off the bot. @@ -67,3 +76,6 @@ Be sure to replace that with your name. # Contributing When contributing, be sure to add yourself to the contributors list in `/commands/about.js`. + +# Credits +ChatGPT: Some code in this codebase used ChatGPT diff --git a/Utilities/Voting.js b/Utilities/Voting.js new file mode 100644 index 0000000..2ee0629 --- /dev/null +++ b/Utilities/Voting.js @@ -0,0 +1,101 @@ +/************************************************************************** + * + * DLAP Bot: A Discord bot that plays local audio tracks. + * (C) Copyright 2022 + * Programmed by Andrew Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + ***************************************************************************/ +import { nextAudio, playerState, 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')); + +export const votes = new Set(); +let nextCheck; + +async function commandCheck(interaction, bot) { + if (nextCheck) { + await interaction.reply({ content: 'Playing next music' }); + player.stop(); + return await nextAudio(bot); + } else { + await previousAudio(bot, interaction); + } +} + +export async function voteSkip(interaction, bot) { + if (interaction.commandName === 'next') { + if (nextCheck !== true) { + // Reset the votes if the current value of nextCheck is different from the command being executed + console.log('Vote has reset due to previous command being executed'); + votes.clear(); + } + nextCheck = true; + } else if (interaction.commandName === 'previous') { + if (nextCheck !== false) { + // Reset the votes if the current value of nextCheck is different from the command being executed + console.log('Vote has reset due to next command being executed'); + votes.clear(); + } + nextCheck = false; + } + + if (interaction.options.getSubcommand() === 'vote') { + // Get the members of the voice channel who have not voted yet + const voiceChannel = interaction.member.voice.channel; + const members = voiceChannel.members.filter(m => !votes.has(m.id)); + + // Calculate the number of votes required to skip the audio track + const votesRequired = Math.ceil(members.size / 2); + + // 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 }); + } + + // Add the message author to the set of members who have voted + votes.add(interaction.user.id); + + if (playerState === 'Playing' || playerState === 'Paused') { + if (votes.size >= votesRequired) { + console.log('Enough votes has passed, skipping audio file...'); + // 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 - votes.size} more vote(s) needed to skip the audio track.`); + await interaction.reply({ content: `${votesRequired - votes.size} more vote(s) needed to skip the audio track.` }); + } + } else if (playerState === 'Stopped') { + return await interaction.reply({ content: 'Cannot play next music. Player is currently stopped...', ephemeral: true }); + } + } + + if (interaction.options.getSubcommand() === 'force') { + if (!interaction.member.roles.cache.has(djRole) && interaction.user.id !== ownerID && !interaction.member.permission.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') { + 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 }); + } + } +} diff --git a/Utilities/i18n.js b/Utilities/i18n.js new file mode 100644 index 0000000..c667c4c --- /dev/null +++ b/Utilities/i18n.js @@ -0,0 +1,38 @@ +/************************************************************************** + * + * DLAP Bot: A Discord bot that plays local audio tracks. + * (C) Copyright 2022 + * Programmed by Andrew Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + ***************************************************************************/ +import i18next from 'i18next'; +import fsBackend from 'i18next-fs-backend'; + +i18next.use(fsBackend).init({ + lng: 'en', // if you're using a language detector, do not define the lng option + debug: true, + fallbackLng: 'en', + backend: { + loadPath: './Locales/{{lng}}/{{ns}}.json' + } +}); + +export default { + i18next, + t(key) { + return i18next.t(key); + } +}; @@ -18,11 +18,11 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. * ***************************************************************************/ -import { Client, GatewayIntentBits, EmbedBuilder, Collection, version, InteractionType } from 'discord.js'; +import { Client, Events, GatewayIntentBits, EmbedBuilder, Collection, version, InteractionType } from 'discord.js'; import { voiceInit } from './AudioBackend/VoiceInitialization.js'; import { readdirSync, readFileSync } from 'node:fs'; // import config from './config.json' assert { type: 'json' } Not supported by ESLint yet -const { token, statusChannel, voiceChannel, shuffle, repeat, presenceActivity, activityType } = JSON.parse(readFileSync('./config.json', 'utf-8')); +const { token, statusChannel, voiceChannel, djRole, ownerID, shuffle, repeat, presenceActivity, activityType } = JSON.parse(readFileSync('./config.json', 'utf-8')); const bot = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildVoiceStates] }); bot.login(token); @@ -41,12 +41,14 @@ for (const file of commandFiles) { bot.commands.set(command.data.name, command); } -bot.once('ready', async() => { +bot.once(Events.ClientReady, async() => { console.log('Bot is ready!'); console.log(`Logged in as ${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}`); @@ -75,9 +77,9 @@ bot.once('ready', async() => { return await voiceInit(bot); }); -bot.on('interactionCreate', async interaction => { +bot.on(Events.InteractionCreate, async interaction => { if (interaction.type === !InteractionType.ApplicationCommand) return; - + if (!interaction.isChatInputCommand()) return; const command = bot.commands.get(interaction.commandName); if (!command) return; @@ -86,6 +88,6 @@ bot.on('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!\n\nDetails:\`\`\`${e}\`\`\``, ephemeral: true }); + 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 }); } }); diff --git a/package.json b/package.json index 5757d02..45bb323 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "discord.js": "^14.6.0", "ffmpeg-static": "^5.1.0", "i18next": "^22.4.5", + "i18next-fs-backend": "^2.1.0", "music-metadata": "^8.1.0", "sodium": "^3.0.2" }, @@ -60,15 +60,15 @@ node-addon-api "^5.0.0" "@discordjs/rest@^1.3.0", "@discordjs/rest@^1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@discordjs/rest/-/rest-1.4.0.tgz#ceaff2a63680c5a0d8c43b85c8a2b023413d4080" - integrity sha512-k3Ip7ffFSAfp7Mu4H/3BEXFvFz+JsbXRrRtpeBMnSp1LefhtlZWJE6xdXzNlblktKNQltnRwY+z0NZrGQdxAMw== + version "1.5.0" + resolved "https://registry.yarnpkg.com/@discordjs/rest/-/rest-1.5.0.tgz#dc15474ab98cf6f31291bf61bbc72bcf4f30cea2" + integrity sha512-lXgNFqHnbmzp5u81W0+frdXN6Etf4EUi8FAPcWpSykKd8hmlWh1xy6BmE0bsJypU1pxohaA8lQCgp70NUI3uzA== dependencies: "@discordjs/collection" "^1.3.0" "@discordjs/util" "^0.1.0" "@sapphire/async-queue" "^1.5.0" "@sapphire/snowflake" "^3.2.2" - discord-api-types "^0.37.20" + discord-api-types "^0.37.23" file-type "^18.0.0" tslib "^2.4.1" undici "^5.13.0" @@ -89,25 +89,25 @@ tslib "^2.4.1" ws "^8.11.0" -"@eslint/eslintrc@^1.3.3": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.3.tgz#2b044ab39fdfa75b4688184f9e573ce3c5b0ff95" - integrity sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg== +"@eslint/eslintrc@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.4.0.tgz#8ec64e0df3e7a1971ee1ff5158da87389f167a63" + integrity sha512-7yfvXy6MWLgWSFsLhz5yH3iQ52St8cdUY6FoGieKkRDVxuxmrNuUetIuu6cmjNWwniUHiWXjxCr5tTXDrbYS5A== dependencies: ajv "^6.12.4" debug "^4.3.2" espree "^9.4.0" - globals "^13.15.0" + globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" js-yaml "^4.1.0" minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@humanwhocodes/config-array@^0.11.6": - version "0.11.7" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.7.tgz#38aec044c6c828f6ed51d5d7ae3d9b9faf6dbb0f" - integrity sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw== +"@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" + integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g== dependencies: "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" @@ -150,17 +150,17 @@ integrity sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA== "@sapphire/shapeshift@^3.7.1": - version "3.7.1" - resolved "https://registry.yarnpkg.com/@sapphire/shapeshift/-/shapeshift-3.7.1.tgz#11f6b7bedc5bc980a1de57bd98ea2566d679d39f" - integrity sha512-JmYN/0GW49Vl8Hi4PwrsDBNjcuCylH78vWYolVys74LRIzilAAMINxx4RHQOdvYoz+ceJKVp4+zBbQ5kuIFOLw== + version "3.8.1" + resolved "https://registry.yarnpkg.com/@sapphire/shapeshift/-/shapeshift-3.8.1.tgz#b98dc6a7180f9b38219267917b2e6fa33f9ec656" + integrity sha512-xG1oXXBhCjPKbxrRTlox9ddaZTvVpOhYLmKmApD/vIWOV1xEYXnpoFs68zHIZBGbqztq6FrUPNPerIrO1Hqeaw== dependencies: fast-deep-equal "^3.1.3" - lodash.uniqwith "^4.5.0" + lodash "^4.17.21" "@sapphire/snowflake@^3.2.2": - version "3.2.2" - resolved "https://registry.yarnpkg.com/@sapphire/snowflake/-/snowflake-3.2.2.tgz#faacdc1b5f7c43145a71eddba917de2b707ef780" - integrity sha512-ula2O0kpSZtX9rKXNeQMrHwNd7E4jPDJYUXmEGTFdMRfyfMw+FPyh04oKMjAiDuOi64bYgVkOV3MjK+loImFhQ== + version "3.3.0" + resolved "https://registry.yarnpkg.com/@sapphire/snowflake/-/snowflake-3.3.0.tgz#247413e4d7924a9f508c6a5c8d427e4105ac0fe6" + integrity sha512-Hec5N6zEkZuZFLybVKyLFLlcSgYmR6C1/+9NkIhxPwOf6tgX52ndJCSz8ADejmbrNE0VuNCNkpzhRZzenEC9vA== "@tokenizer/token@^0.3.0": version "0.3.0" @@ -173,9 +173,9 @@ integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== "@types/node@*": - version "18.11.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.9.tgz#02d013de7058cea16d36168ef2fc653464cfbad4" - integrity sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg== + version "18.11.17" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.17.tgz#5c009e1d9c38f4a2a9d45c0b0c493fe6cdb4bcb5" + integrity sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng== "@types/node@^10.0.3": version "10.17.60" @@ -234,9 +234,9 @@ ansi-styles@^4.1.0: color-convert "^2.0.1" anymatch@~3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" @@ -260,24 +260,24 @@ argparse@^2.0.1: integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== array-includes@^3.1.4: - version "3.1.5" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.5.tgz#2c320010db8d31031fd2a5f6b3bbd4b1aad31bdb" - integrity sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ== + version "3.1.6" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" + integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== dependencies: call-bind "^1.0.2" define-properties "^1.1.4" - es-abstract "^1.19.5" - get-intrinsic "^1.1.1" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" is-string "^1.0.7" array.prototype.flat@^1.2.5: - version "1.3.0" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz#0b0c1567bf57b38b56b4c97b8aa72ab45e4adc7b" - integrity sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw== + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" + integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" balanced-match@^1.0.0: @@ -465,15 +465,15 @@ 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.15, discord-api-types@^0.37.20: - version "0.37.20" - resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.20.tgz#f23894e3e6b894abb5431ff6c4aa52471360377c" - integrity sha512-uAO+55E11rMkYR36/paE1vKN8c2bZa1mgrIaiQIBgIZRKZTDIGOZB+8I5eMRPFJcGxrg16riUu+0aTu2JQEPew== +discord-api-types@^0.37.15, discord-api-types@^0.37.20, discord-api-types@^0.37.23: + version "0.37.24" + resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.24.tgz#f3ee0ad6b2b70925b5225f9beac6e24bc4de6e46" + integrity sha512-1+Fb4huJCihdbkJLcq2p7nBmtlmAryNwjefT8wwJnL8c7bc7WA87Oaa5mbLe96QvZyfwnwRCDX40H0HhcVV50g== discord.js@^14.6.0: - version "14.7.0" - resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.7.0.tgz#1411ad6703d8e5ca58f8295e5acd2994b890cc51" - integrity sha512-CR2JAoqR+82D7mfMZ7toPAqdIk2sMF8wgTc8yDGPPMHzJknIKtkEPtzWFhBYGMZUkK+M4POw08ngBWqK2A4RMg== + version "14.7.1" + resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.7.1.tgz#26079d0ff4d27daf02480a403c456121f0682bd9" + integrity sha512-1FECvqJJjjeYcjSm0IGMnPxLqja/pmG1B0W2l3lUY2Gi4KXiyTeQmU1IxWcbXHn2k+ytP587mMWqva2IA87EbA== dependencies: "@discordjs/builders" "^1.4.0" "@discordjs/collection" "^1.3.0" @@ -512,10 +512,10 @@ env-paths@^2.2.0: resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== -es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5: - version "1.20.4" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.4.tgz#1d103f9f8d78d4cf0713edcd6d0ed1a46eed5861" - integrity sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA== +es-abstract@^1.19.0, es-abstract@^1.20.4: + version "1.20.5" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.5.tgz#e6dc99177be37cacda5988e692c3fa8b218e95d2" + integrity sha512-7h8MM2EQhsCA7pU/Nv78qOXFpD8Rhqd12gYiSJVkrH9+e8VuA8JlPJK/hQjjlLv6pJvx/z1iRFKzYb0XT/RuAQ== dependencies: call-bind "^1.0.2" es-to-primitive "^1.2.1" @@ -523,6 +523,7 @@ es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19 function.prototype.name "^1.1.5" get-intrinsic "^1.1.3" get-symbol-description "^1.0.0" + gopd "^1.0.1" has "^1.0.3" has-property-descriptors "^1.0.0" has-symbols "^1.0.3" @@ -538,8 +539,8 @@ es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19 object.assign "^4.1.4" regexp.prototype.flags "^1.4.3" safe-regex-test "^1.0.0" - string.prototype.trimend "^1.0.5" - string.prototype.trimstart "^1.0.5" + string.prototype.trimend "^1.0.6" + string.prototype.trimstart "^1.0.6" unbox-primitive "^1.0.2" es-shim-unscopables@^1.0.0: @@ -611,9 +612,9 @@ eslint-plugin-import@^2.25.2: tsconfig-paths "^3.14.1" eslint-plugin-n@^15.0.0: - version "15.5.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-15.5.1.tgz#b3991857d1edaa47e0108ead825470ce63f391c1" - integrity sha512-kAd+xhZm7brHoFLzKLB7/FGRFJNg/srmv67mqb7tto22rpr4wv/LV6RuXzAfv3jbab7+k1wi42PsIhGviywaaw== + version "15.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-15.6.0.tgz#cfb1d2e2e427d620eb9008f8b3b5a40de0c84120" + integrity sha512-Hd/F7wz4Mj44Jp0H6Jtty13NcE69GNTY0rVlgTIj1XBnGGVI6UTdDrpE6vqu3AHo07bygq/N+7OH/lgz1emUJw== dependencies: builtins "^5.0.1" eslint-plugin-es "^4.1.0" @@ -667,12 +668,12 @@ eslint-visitor-keys@^3.3.0: integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== eslint@^8.20.0: - version "8.28.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.28.0.tgz#81a680732634677cc890134bcdd9fdfea8e63d6e" - integrity sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ== + version "8.30.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.30.0.tgz#83a506125d089eef7c5b5910eeea824273a33f50" + integrity sha512-MGADB39QqYuzEGov+F/qb18r4i7DohCDOfatHaxI2iGlPuC65bwG2gxgO+7DkyL38dRFaRH7RaRAgU6JKL9rMQ== dependencies: - "@eslint/eslintrc" "^1.3.3" - "@humanwhocodes/config-array" "^0.11.6" + "@eslint/eslintrc" "^1.4.0" + "@humanwhocodes/config-array" "^0.11.8" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" ajv "^6.10.0" @@ -691,7 +692,7 @@ eslint@^8.20.0: file-entry-cache "^6.0.1" find-up "^5.0.0" glob-parent "^6.0.2" - globals "^13.15.0" + globals "^13.19.0" grapheme-splitter "^1.0.4" ignore "^5.2.0" import-fresh "^3.0.0" @@ -760,9 +761,9 @@ fast-levenshtein@^2.0.6: integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fastq@^1.6.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" - integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + version "1.14.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.14.0.tgz#107f69d7295b11e0fccc264e1fc6389f623731ce" + integrity sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg== dependencies: reusify "^1.0.4" @@ -872,7 +873,7 @@ gauge@^3.0.0: strip-ansi "^6.0.1" wide-align "^1.1.2" -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== @@ -915,13 +916,20 @@ glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -globals@^13.15.0: - version "13.18.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.18.0.tgz#fb224daeeb2bb7d254cd2c640f003528b8d0c1dc" - integrity sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A== +globals@^13.19.0: + version "13.19.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.19.0.tgz#7a42de8e6ad4f7242fbcca27ea5b23aca367b5c8" + integrity sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ== dependencies: type-fest "^0.20.2" +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + grapheme-splitter@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" @@ -988,10 +996,15 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +i18next-fs-backend@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/i18next-fs-backend/-/i18next-fs-backend-2.1.1.tgz#07c6393be856c5a398e3dfc1257bf8439841cd89" + integrity sha512-FTnj+UmNgT3YRml5ruRv0jMZDG7odOL/OP5PF5mOqvXud2vHrPOOs68Zdk6iqzL47cnnM0ZVkK2BAvpFeDJToA== + i18next@^22.4.5: - version "22.4.5" - resolved "https://registry.yarnpkg.com/i18next/-/i18next-22.4.5.tgz#7324e4946c2facbe743ca25bca8980af05b0a109" - integrity sha512-Kc+Ow0guRetUq+kv02tj0Yof9zveROPBAmJ8UxxNODLVBRSwsM4iD0Gw3BEieOmkWemF6clU3K1fbnCuTqiN2Q== + version "22.4.6" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-22.4.6.tgz#876352c3ba81bdfedc38eeda124e2bbd05f46988" + integrity sha512-9Tm1ezxWyzV+306CIDMBbYBitC1jedQyYuuLtIv7oxjp2ohh8eyxP9xytIf+2bbQfhH784IQKPSYp+Zq9+YSbw== dependencies: "@babel/runtime" "^7.20.6" @@ -1005,15 +1018,10 @@ ignore-by-default@^1.0.1: resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== -ignore@^5.1.1: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" - integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== - -ignore@^5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.1.tgz#c2b1f76cb999ede1502f3a226a9310fdfe88d46c" - integrity sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA== +ignore@^5.1.1, ignore@^5.2.0: + version "5.2.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" @@ -1042,11 +1050,11 @@ inherits@2, inherits@^2.0.3: integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== internal-slot@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" - integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== + version "1.0.4" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.4.tgz#8551e7baf74a7a6ba5f749cfb16aa60722f0d6f3" + integrity sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ== dependencies: - get-intrinsic "^1.1.0" + get-intrinsic "^1.1.3" has "^1.0.3" side-channel "^1.0.4" @@ -1077,20 +1085,13 @@ is-callable@^1.1.4, is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.11.0, is-core-module@^2.9.0: +is-core-module@^2.11.0, is-core-module@^2.8.1, is-core-module@^2.9.0: version "2.11.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== dependencies: has "^1.0.3" -is-core-module@^2.8.1: - version "2.10.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed" - integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg== - dependencies: - has "^1.0.3" - is-date-object@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" @@ -1232,10 +1233,10 @@ lodash.snakecase@^4.1.1: resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw== -lodash.uniqwith@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.uniqwith/-/lodash.uniqwith-4.5.0.tgz#7a0cbf65f43b5928625a9d4d0dc54b18cadc7ef3" - integrity sha512-7lYL8bLopMoy4CTICbxygAUq6CdRJ36vFc80DucPueUee+d5NBRxz3FdT9Pes/HEx5mPoT9jwnsEJWz1N7uq7Q== +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== lru-cache@^6.0.0: version "6.0.0" @@ -1269,9 +1270,16 @@ minimist@^1.2.0, minimist@^1.2.6: integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== minipass@^3.0.0: - version "3.3.4" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.4.tgz#ca99f95dd77c43c7a76bf51e6d200025eee0ffae" - integrity sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw== + version "3.3.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== + dependencies: + yallist "^4.0.0" + +minipass@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.0.0.tgz#7cebb0f9fa7d56f0c5b17853cbe28838a8dbbd3b" + integrity sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw== dependencies: yallist "^4.0.0" @@ -1404,13 +1412,13 @@ object.assign@^4.1.4: object-keys "^1.1.1" object.values@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" - integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== + version "1.1.6" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" + integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" + define-properties "^1.1.4" + es-abstract "^1.20.4" once@^1.3.0: version "1.4.0" @@ -1660,9 +1668,9 @@ signal-exit@^3.0.0: integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== simple-update-notifier@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-1.0.7.tgz#7edf75c5bdd04f88828d632f762b2bc32996a9cc" - integrity sha512-BBKgR84BJQJm6WjWFMHgLVuo61FBDSj1z/xSFUIozqO6wO7ii0JxCqlIud7Enr/+LhlbNI0whErq96P2qHNWew== + version "1.1.0" + resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz#67694c121de354af592b347cdba798463ed49c82" + integrity sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg== dependencies: semver "~7.0.0" @@ -1687,23 +1695,23 @@ streamsearch@^1.1.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string.prototype.trimend@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" - integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== +string.prototype.trimend@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" + integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== dependencies: call-bind "^1.0.2" define-properties "^1.1.4" - es-abstract "^1.19.5" + es-abstract "^1.20.4" -string.prototype.trimstart@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" - integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== +string.prototype.trimstart@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" + integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== dependencies: call-bind "^1.0.2" define-properties "^1.1.4" - es-abstract "^1.19.5" + es-abstract "^1.20.4" string_decoder@^1.1.1: version "1.3.0" @@ -1757,13 +1765,13 @@ supports-preserve-symlinks-flag@^1.0.0: integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== tar@^6.1.11: - version "6.1.11" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" - integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== + version "6.1.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.13.tgz#46e22529000f612180601a6fe0680e7da508847b" + integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw== dependencies: chownr "^2.0.0" fs-minipass "^2.0.0" - minipass "^3.0.0" + minipass "^4.0.0" minizlib "^2.1.1" mkdirp "^1.0.3" yallist "^4.0.0" @@ -1853,9 +1861,9 @@ undefsafe@^2.0.5: integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== undici@^5.13.0: - version "5.13.0" - resolved "https://registry.yarnpkg.com/undici/-/undici-5.13.0.tgz#56772fba89d8b25e39bddc8c26a438bd73ea69bb" - integrity sha512-UDZKtwb2k7KRsK4SdXWG7ErXiL7yTGgLWvk2AXO1JMjgjh404nFo6tWSCM2xMpJwMPx3J8i/vfqEh1zOqvj82Q== + version "5.14.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.14.0.tgz#1169d0cdee06a4ffdd30810f6228d57998884d00" + integrity sha512-yJlHYw6yXPPsuOH0x2Ib1Km61vu4hLiRRQoafs+WUgX1vO64vgnxiCEN9dpIrhZyHFsai3F0AEj4P9zy19enEQ== dependencies: busboy "^1.6.0" |
