aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AudioBackend/AudioControl.js2
-rw-r--r--AudioBackend/PlayAudio.js14
-rw-r--r--AudioBackend/VoiceInitialization.js2
-rw-r--r--Commands/join.js7
-rw-r--r--Commands/leave.js9
-rw-r--r--Commands/list.js3
-rw-r--r--Commands/next.js20
-rw-r--r--Commands/pause.js10
-rw-r--r--Commands/play.js10
-rw-r--r--Commands/previous.js16
-rw-r--r--Commands/reshuffle.js7
-rw-r--r--Commands/shutdown.js8
-rw-r--r--Commands/status.js27
-rw-r--r--Locales/custom/translation.json3
-rw-r--r--Locales/en/translation.json3
-rw-r--r--README.md22
-rw-r--r--Utilities/Voting.js101
-rw-r--r--Utilities/i18n.js38
-rw-r--r--bot.js14
-rw-r--r--package.json1
-rw-r--r--yarn.lock252
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!"
+}
diff --git a/README.md b/README.md
index bb11674..5fb4728 100644
--- a/README.md
+++ b/README.md
@@ -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);
+ }
+};
diff --git a/bot.js b/bot.js
index 53c5cb1..244f2d5 100644
--- a/bot.js
+++ b/bot.js
@@ -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"
},
diff --git a/yarn.lock b/yarn.lock
index e33fdcc..bbe22c5 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -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"