diff --git a/Dockerfile b/Dockerfile index 21d70d0..35a68c2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM node:latest -RUN apt-get update && apt-get install -y ffmpeg --no-install-recommends +RUN apt-get update --no-install-recommends WORKDIR /usr/app diff --git a/README.md b/README.md index 2f58759..c160661 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,23 @@ > [!NOTE] -> Please note that this bot is not actively maintained. Some services, especially those related to embed fixes, may occasionally go down. +> Please note that this bot is not actively maintained. -> [!IMPORTANT] -> The YouTube / Spotify / SoundCloud resolver `play-dl` seems to keep breaking(?) and plays the wrong songs. You can install `youtube-ext`, `@distube/ytdl-core`, `yt-stream` or `ytdl-core` instead. To do this, remove `play-dl` by running `npm remove play-dl` and then install another resolver with `npm install ` in the `data/` directory. Make sure that you have installed NodeJS for this to work. Then recreate the Docker Compose project with `docker compose up --build --force-recreate --no-cache`. +# Discord Embed Buddy -# Discord-SM +This is a Node.js-based Discord bot that intercepts messages with potentially faulty social media embeds and provides proxy links for the content in response. -This is a Node.js-based Discord bot that plays music from various sources including Spotify, YouTube, and SoundCloud. It also includes features for managing the music queue, supports multiple commands, and handles social media link embeds. +## URL Replacements -## Features +Here's an overview of the URL replacements: -- **Music Playback**: Add, skip, stop, and manage songs in the queue from various sources. -- **Social Media Link Embeds**: Automatically replaces original embeds for social media links with custom embed fixes. -- **Multilanguage Support**: The bot can operate in multiple languages. Currently available languages are English and German. +| Original URL | Replacement URL | +|--------------------|------------------| +| `twitter.com` | `fxtwitter.com` | +| `x.com` | `fixupx.com` | +| `tiktok.com` | `d.tnktok.com` | +| `vm.tiktok.com` | `d.tnktok.com` | +| `reddit.com` | `rxddit.com` | +| `old.reddit.com` | `old.rxddit.com` | +| `instagram.com` | `ddinstagram.com`| ## Docker Setup @@ -26,6 +31,17 @@ Ensure you have Docker installed on your system. 1. **Clone this repository**. +1. **Configure your `.env` file**. + + Before running the Docker container, make sure you set up the necessary environment variables. Create a `.env` file in the `data` directory with the following content: + + ```sh + DISCORD_TOKEN= + CLIENT_ID= + ``` + + Replace `` and `` with your actual Discord bot token and client ID. + 2. **Navigate to the project directory** where the Dockerfile and `compose.yml` are located. 3. **Build and start the Docker container** using Docker Compose: @@ -37,44 +53,6 @@ Ensure you have Docker installed on your system. - Build the Docker image as defined in the `Dockerfile`. - Start the container with the configuration specified in `compose.yml`. -### Configuration - -Before running the Docker container, make sure you set up the necessary environment variables. Create a `.env` file in the `data` directory with the following content: - -```sh -DISCORD_TOKEN= -CLIENT_ID= -``` - -Replace `` and `` with your actual Discord bot token and client ID. - -## Commands - -Here's an overview of the available commands: - -### Slash Commands - -| Command | Description | Options | -|--------------|-----------------------------------------------------------------------------|---------------------------------------| -| `/play` | Add a song from Spotify, YouTube, SoundCloud, or similar to the queue. | `song`: Song title or link (required) | -| `/stop` | Clear the queue and kick the bot from the channel. | | -| `/skip` | Skip the current song in the queue. | | -| `/listqueue` | Display a list of the current queue. | | -| `/wrongsong` | Remove the last song requested by you from the queue. | | -| `/song` | Display the current song. | | -| `/language` | Set the bot's language for the entire server. | `lang`: Language choice (required) | - -### Text Commands - -| Command | Description | -|----------------|--------------------------------------------------| -| `!forcedelete` | Deletes all guild-specific commands for the bot. | -| `!forceupdate` | Updates all guild-specific commands for the bot. | - -## Localization - -The bot supports multiple languages. Currently available languages are English and German. You can change the bot's language using the `/language` command. - ## License This project is licensed under the MIT License. diff --git a/data/config/guild_data.json b/data/config/guild_data.json deleted file mode 100644 index 089408c..0000000 --- a/data/config/guild_data.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "798879966485151814": { - "language": "de" - }, - "1210936290262908959": { - "language": "de" - } -} diff --git a/data/config/messages.json b/data/config/messages.json deleted file mode 100644 index 31518f4..0000000 --- a/data/config/messages.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "en": { - "console.musicplayer.playerror": "Error whilst trying to play a song:\n\n%error", - "console.remotecode.error.execution": "Error whilst executing remote code:\n\n%error", - "console.remotecode.error.format": "Error whilst executing remote code: Invalid code block format", - "console.commandrefresh.application.refresh.start": "Started refreshing application (/) commands.", - "console.commandrefresh.application.refresh.finish": "Successfully reloaded application (/) commands.", - "console.commandrefresh.application.refresh.guild.start": "Started refreshing application guild (/) commands.", - "console.commandrefresh.application.refresh.guild.finish": "Successfully reloaded application guild (/) commands.", - "console.commandrefresh.application.deletion.guild.start": "Started deletion of application guild (/) commands.", - "console.commandrefresh.application.deletion.guild.finish": "Successfully deleted application guild (/) commands.", - "user.command.play.desc": "Add a song from Spotify, YouTube, SoundCloud, or similar to the queue.", - "user.command.play.song.desc": "Song title or a link to the song on Spotify, YouTube, or SoundCloud.", - "user.command.stop.desc": "Clear the queue and kick the bot from the channel.", - "user.command.skip.desc": "Skip the current song in the queue.", - "user.command.listqueue.desc": "Display a list of the current queue.", - "user.command.wrongsong.desc": "Remove the last song requested by you from the queue.", - "user.command.song.desc": "Display the current song.", - "user.command.language.desc": "Setting the language of the bot for the entire server.", - "user.command.language.lang.desc": "The language you want me to use.", - "user.command.language.lang.en": "English", - "user.command.language.lang.de": "German", - "user.locale.changed": "OK! From now on I will answer here in English.", - "user.embedfix.output": " %url", - "user.musicplayer.playerror": "Sorry, I couldn't play the song. ", - "user.musicplayer.missing.voicechat": "You are not connected to a voice channel.", - "user.musicplayer.missing.result": "I don't know where you got that from, but I just searched the entire internet and couldn't find anything about it. ", - "user.musicplayer.song.queued": " The song \"[%title](%url)\" has been added to the queue.", - "user.musicplayer.playlist.queued": " I found **%count songs** in the playlist and added them to the queue. The song \"[%title](%url)\" will be played first.", - "user.musicplayer.skip": "Ok. Was the song that bad? nvm, I'm doing it already... ", - "user.musicplayer.undo": "Ok. I've removed your last song request. ", - "user.musicplayer.missing.undo": "No worries, there wasn't any song requested by you in the first place. ", - "user.musicplayer.not.playing": "There's no music playing right now. You could change that... ", - "user.musicplayer.queue.empty": "The queue is empty. ", - "user.musicplayer.song.info": "Currently playing \"[%title](%url)\" by %user. ", - "user.musicplayer.queue.info": "Here's the current queue:\n\n%queue", - "user.musicplayer.queue.info.limit": "\n... and **%count more songs**.", - "user.musicplayer.queue.info.bullet": "- \"[%title](<%url>)\" by %user", - "user.musicplayer.goodbye": "Ok. See you soon! |Ok. Goodbye! |Ok. Farewell! " - }, - "de": { - "user.command.play.desc": "Einen Song von Spotify, YouTube, Soundcloud oder ähnlichem zur Warteschlange hinzufügen.", - "user.command.play.song.desc": "Songtitel oder ein Link zu dem Song auf Spotify, YouTube oder z.B. Soundcloud.", - "user.command.stop.desc": "Warteschlange löschen und Dingsbums aus dem Channel werfen.", - "user.command.skip.desc": "Den aktuellen Song in der Warteschlange überspringen.", - "user.command.listqueue.desc": "Gibt eine Liste der aktuellen Warteschlange aus.", - "user.command.wrongsong.desc": "Den letzten von dir gewünschten Song aus der Warteschlange entfernen.", - "user.command.song.desc": "Den Aktuellen Song anzeigen lassen.", - "user.command.language.desc": "Einstellen der Sprache des Bots für den gesamten Server.", - "user.command.language.lang.desc": "Die Sprache, die ich verwenden soll.", - "user.command.language.lang.en": "Englisch", - "user.command.language.lang.de": "Deutsch", - "user.locale.changed": "OK! Von nun an werde ich hier auf Deutsch antworten.", - "user.embedfix.output": " %url", - "user.musicplayer.playerror": "Sorry, ich konnte den Song nicht abspielen ", - "user.musicplayer.missing.voicechat": "Du bist nicht mit einem Voice-Channel verbunden", - "user.musicplayer.missing.result": "Ich weiß nicht, woher du das hast, aber ich habe gerade das ganze Internet durchsucht und konnte nichts dazu finden ", - "user.musicplayer.song.queued": " Der Song \"[%title](%url)\" ist jetzt in der Warteschlange", - "user.musicplayer.playlist.queued": " Ich habe **%count Songs** in der Playlist gefunden und sie in die Warteschlange gestellt. Der Song \"[%title](%url)\" wird davon als erstes abgespielt.", - "user.musicplayer.skip": "Ok. War der Song so schlecht? nvm, ich mach ja schon ... ", - "user.musicplayer.undo": "Ok. Ich hab deinen letzten Song wieder entfernt ", - "user.musicplayer.missing.undo": "Kein Stress, da war eh kein Song mehr der von dir kam ", - "user.musicplayer.not.playing": "Es Spielt gerade gar keine Musik. Du könntest das ändern ... ", - "user.musicplayer.queue.empty": "Die Warteschlange ist leer ", - "user.musicplayer.song.info": "Aktuell Spielt \"[%title](%url)\" von %user ", - "user.musicplayer.queue.info": "Hier ist die aktuelle Warteschlange:\n\n%queue", - "user.musicplayer.queue.info.limit": "\n... und noch **%count weitere Songs**.", - "user.musicplayer.queue.info.bullet": "- \"[%title](<%url>)\" von %user", - "user.musicplayer.goodbye": "Ok. Bis bald! " - } -} diff --git a/data/package.json b/data/package.json index 081dd58..5e0eaac 100644 --- a/data/package.json +++ b/data/package.json @@ -1,7 +1,7 @@ { - "name": "discord-sm", - "version": "1.0.0", - "description": "Discord bot that fixes social media embeds and enables music playback", + "name": "embeds-buddy", + "version": "1.1.0", + "description": "Discord bot that fixes social media embeds", "main": "src/index.js", "scripts": { "start": "node --no-deprecation --env-file=.env src/index.js" @@ -9,10 +9,6 @@ "author": "philippgitpush", "license": "MIT", "dependencies": { - "@discordjs/opus": "^0.9.0", - "discord-player": "^6.6.8", - "discord.js": "^14.14.1", - "fs": "^0.0.1-security", - "play-dl": "^1.9.7" + "discord.js": "^14.14.1" } } diff --git a/data/src/commandRefresh.js b/data/src/commandRefresh.js deleted file mode 100644 index 7fb97d6..0000000 --- a/data/src/commandRefresh.js +++ /dev/null @@ -1,153 +0,0 @@ -const { REST, Routes } = require('discord.js'); -const { getMessage } = require('./locales'); -const Client = require('./discordClient'); -const Discord = require('discord.js'); - -Client.on(Discord.Events.MessageCreate, async message => { - if (message.author.bot) return; - - switch (message.content) { - case "!forcedelete": - await forceDelete(message.member.guild.id); - break; - case "!forceupdate": - await forceUpdate(message.member.guild.id); - break; - default: - break; - } -}); - -const commands = [ - { - name: 'play', - description: getMessage('user.command.play.desc'), - description_localizations: { - de: getMessage('user.command.play.desc', 'de') - }, - options: [ - { - name: 'song', - type: 3, - description: getMessage('user.command.play.song.desc'), - description_localizations: { - de: getMessage('user.command.play.song.desc', 'de') - }, - required: true - } - ] - }, - { - name: 'stop', - description: getMessage('user.command.stop.desc'), - description_localizations: { - de: getMessage('user.command.stop.desc', 'de') - } - }, - { - name: 'skip', - description: getMessage('user.command.skip.desc'), - description_localizations: { - de: getMessage('user.command.skip.desc', 'de') - } - }, - { - name: 'listqueue', - description: getMessage('user.command.listqueue.desc'), - description_localizations: { - de: getMessage('user.command.listqueue.desc', 'de') - } - }, - { - name: 'wrongsong', - description: getMessage('user.command.wrongsong.desc'), - description_localizations: { - de: getMessage('user.command.wrongsong.desc', 'de') - } - }, - { - name: 'song', - description: getMessage('user.command.song.desc'), - description_localizations: { - de: getMessage('user.command.song.desc', 'de') - } - }, - { - name: 'language', - description: getMessage('user.command.language.desc'), - description_localizations: { - de: getMessage('user.command.language.desc', 'de') - }, - options: [ - { - name: 'lang', - type: 3, - choices: [ - { - name: getMessage('user.command.language.lang.en'), - name_localizations: { - de: getMessage('user.command.language.lang.en', 'de') - }, - value: 'en' - }, - { - name: getMessage('user.command.language.lang.de'), - name_localizations: { - de: getMessage('user.command.language.lang.de', 'de') - }, - value: 'de' - }, - ], - description: getMessage('user.command.language.lang.desc'), - description_localizations: { - de: getMessage('user.command.language.lang.desc', 'de') - }, - required: true - } - ] - } -]; - -const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN); - -(async () => { - try { - console.log(getMessage('console.commandrefresh.application.refresh.start')); - - await rest.put( - Routes.applicationCommands(process.env.CLIENT_ID), { body: commands }, - ); - - console.log(getMessage('console.commandrefresh.application.refresh.finish')); - } catch (error) { - console.error(error); - } -})(); - -async function forceUpdate(guild_id) { - try { - console.log(getMessage('console.commandrefresh.application.refresh.guild.start')); - - await rest.put( - Routes.applicationGuildCommands(process.env.CLIENT_ID, guild_id), { body: commands }, - ); - - console.log(getMessage('console.commandrefresh.application.refresh.guild.finish')); - } catch (error) { - console.error(error); - } -} - -async function forceDelete(guild_id) { - try { - console.log(getMessage('console.commandrefresh.application.deletion.guild.start')); - - await rest.put( - Routes.applicationGuildCommands(process.env.CLIENT_ID, guild_id), { body: [] }, - ); - - console.log(getMessage('console.commandrefresh.application.deletion.guild.finish')); - } catch (error) { - console.error(error); - } -} diff --git a/data/src/discordClient.js b/data/src/discordClient.js index 954b33c..4fede84 100644 --- a/data/src/discordClient.js +++ b/data/src/discordClient.js @@ -4,8 +4,7 @@ const Client = new Discord.Client({ intents: [ Discord.GatewayIntentBits.Guilds, Discord.GatewayIntentBits.GuildMessages, Discord.GatewayIntentBits.MessageContent, - Discord.GatewayIntentBits.GuildMembers , - Discord.GatewayIntentBits.GuildVoiceStates + Discord.GatewayIntentBits.GuildMembers ] }); const token = process.env.DISCORD_TOKEN; diff --git a/data/src/embedFix.js b/data/src/embedFix.js index 1174bfb..69c8adf 100644 --- a/data/src/embedFix.js +++ b/data/src/embedFix.js @@ -1,4 +1,3 @@ -const { getMessage, getGuildLang } = require('./locales'); const Client = require('./discordClient'); const Discord = require('discord.js'); @@ -8,7 +7,7 @@ Client.on(Discord.Events.MessageCreate, message => { if (hasMatchingDomain(message.content)) { const url = replaceDomains(extractURL(message.content)); message.suppressEmbeds(true); - message.reply(getMessage('user.embedfix.output', getGuildLang(message.guild.id), { url })); + message.reply(url); } }); diff --git a/data/src/index.js b/data/src/index.js index ff04665..b44c6a1 100644 --- a/data/src/index.js +++ b/data/src/index.js @@ -1,5 +1,2 @@ -require('./discordClient'); // Discord Bot Client -require('./locales'); // Localization / translation -require('./embedFix'); // Soecial media link embed fixes -require('./musicPlayer'); // Music player -require('./commandRefresh'); // Discord commands config +require('./discordClient'); +require('./embedFix'); diff --git a/data/src/locales.js b/data/src/locales.js deleted file mode 100644 index 3e5c43b..0000000 --- a/data/src/locales.js +++ /dev/null @@ -1,59 +0,0 @@ -const messages = require('../config/messages.json'); -const Client = require('./discordClient'); -const Discord = require('discord.js'); -const fs = require('fs'); - -Client.on(Discord.Events.InteractionCreate, async interaction => { - if (!interaction.isChatInputCommand()) return; - - const { commandName, options } = interaction; - - switch (commandName) { - case 'language': - const lang = options.getString('lang'); - await setGuildLang(interaction, lang); - break; - default: - break; - } -}); - -function getMessage(key, language = 'en', replacements) { - // use english as default when anything localized is missing - let message = (messages[language] && messages[language][key]) || messages['en'][key] || ''; - - - if (replacements) { // variable replacement - for (const [placeholder, value] of Object.entries(replacements)) { - message = message.replace(new RegExp(`%${placeholder}`, 'g'), value); - } - } - - return message; -} - -function getGuildLang(guildId) { - const guildDataPath = './config/guild_data.json'; - const guildData = JSON.parse(fs.readFileSync(guildDataPath, 'utf8')); - return guildData[guildId] ? guildData[guildId].language : 'en'; -} - -async function setGuildLang(interaction, lang) { - await interaction.deferReply({ ephemeral: true }); - - const guildDataPath = './config/guild_data.json'; - const guildData = JSON.parse(fs.readFileSync(guildDataPath, 'utf8')); - - guildData[interaction.guildId] = { - language: lang - }; - - fs.writeFileSync(guildDataPath, JSON.stringify(guildData, null, 2)); - - interaction.editReply({ content: getMessage('user.locale.changed', getGuildLang(interaction.guild.id)), ephemeral: true }); -} - -module.exports = { - getGuildLang, - getMessage -}; diff --git a/data/src/musicPlayer.js b/data/src/musicPlayer.js deleted file mode 100644 index dea2d61..0000000 --- a/data/src/musicPlayer.js +++ /dev/null @@ -1,187 +0,0 @@ -const { YoutubeExtractor, SpotifyExtractor, SoundCloudExtractor, AttachmentExtractor } = require('@discord-player/extractor'); -const { getMessage, getGuildLang } = require('./locales'); -const { Player } = require('discord-player'); -const Client = require('./discordClient'); -const Discord = require('discord.js'); - -const player = new Player(Client); -player.extractors.register(YoutubeExtractor, {}); -player.extractors.register(SpotifyExtractor, {}); -player.extractors.register(SoundCloudExtractor, {}); -player.extractors.register(AttachmentExtractor, {}); - -player.events.on('playerError', (error) => { - console.log(getMessage('console.musicplayer.playerror', getGuildLang(interaction.guild.id), { error })) -}); - -Client.on(Discord.Events.InteractionCreate, async interaction => { - if (!interaction.isChatInputCommand()) return; - - const { commandName, options } = interaction; - - switch (commandName) { - case 'play': - const query = options.getString('song'); - await playSong(interaction, query); - break; - case 'skip': - await skipSong(interaction); - break; - case 'stop': - await stopSong(interaction); - break; - case 'wrongsong': - await undoSong(interaction); - break; - case 'listqueue': - await listQueue(interaction); - break; - case 'song': - await currentSong(interaction); - break; - default: - break; - } -}); - -async function playSong(interaction, query) { - const channel = interaction.member.voice.channel; - - // return if user is not in a voice channel - if (!channel) return await interaction.reply({ content: getMessage('user.musicplayer.missing.voicechat', getGuildLang(interaction.guild.id)), ephemeral: true }); - - // defer reply early to avoid message timeout while searching - await interaction.deferReply({ ephemeral: true }); - - try { - const searchResults = await player.search(query, { requestedBy: interaction.member }); - - // return if no result was found - if (searchResults.tracks.length === 0) { - return await interaction.editReply({ content: getMessage('user.musicplayer.missing.result', getGuildLang(interaction.guild.id)), ephemeral: true }); - } - - // queue first song in search result - const track = searchResults.tracks[0]; - await player.play(channel, track, { requestedBy: interaction.member }); - player.nodes.get(interaction.guild).node.setVolume(3); - - if (searchResults.playlist) return interaction.editReply({ - content: getMessage('user.musicplayer.playlist.queued', getGuildLang(interaction.guild.id), { title: track.title, url: track.url, count: searchResults.playlist.tracks.length }), - ephemeral: true - }); - - interaction.editReply({ - content: getMessage('user.musicplayer.song.queued', getGuildLang(interaction.guild.id), { title: track.title, url: track.url }), - ephemeral: true - }); - } catch (error) { - console.error(getMessage('console.musicplayer.playerror', getGuildLang(interaction.guild.id), { error })); - interaction.editReply({ content: getMessage('user.musicplayer.playerror', getGuildLang(interaction.guild.id)), ephemeral: true }); - } -} - -async function stopSong(interaction) { - await interaction.deferReply({ ephemeral: true }); - const queue = player.nodes.get(interaction.guild); - - if (!queue) return interaction.editReply({ content: getMessage('user.musicplayer.not.playing', getGuildLang(interaction.guild.id)), ephemeral: true }); - queue.delete(); - - // respond with randomized goodbye message - const messageVariations = getMessage('user.musicplayer.goodbye', getGuildLang(interaction.guild.id)).split('|'); - const selectedMessage = messageVariations[Math.floor(Math.random() * messageVariations.length)]; - interaction.editReply({ content: selectedMessage, ephemeral: true }); -} - -async function skipSong(interaction) { - await interaction.deferReply({ ephemeral: true }); - const queue = player.nodes.get(interaction.guild); - - if (!queue) return interaction.editReply({ content: getMessage('user.musicplayer.not.playing', getGuildLang(interaction.guild.id)), ephemeral: true }); - queue.node.skip(); - - interaction.editReply({ content: getMessage('user.musicplayer.skip', getGuildLang(interaction.guild.id)), ephemeral: true }); -} - -async function undoSong(interaction) { - await interaction.deferReply({ ephemeral: true }); - - // skip if there is nothing playing - const queue = player.nodes.get(interaction.guild); - if (!queue) return interaction.editReply({ content: getMessage('user.musicplayer.not.playing', getGuildLang(interaction.guild.id)), ephemeral: true }); - - // remove track if found in queue - const result = queue.tracks.data.find(track => track.requestedBy.id === interaction.member.id); - if (!result) return interaction.editReply({ content: getMessage('user.musicplayer.missing.undo', getGuildLang(interaction.guild.id)), ephemeral: true }); - - // let the user know nothing was recetly requested - queue.removeTrack(result); - interaction.editReply({ content: getMessage('user.musicplayer.undo', getGuildLang(interaction.guild.id)), ephemeral: true }); -} - -async function currentSong(interaction) { - await interaction.deferReply({ephemeral: true}); - const node = player.nodes.get(interaction.guild); - - // return if there is nothing playing - if (!node) return interaction.editReply({ - content: getMessage('user.musicplayer.not.playing', getGuildLang(interaction.guild.id)), - ephemeral: true - }); - - const title = node.currentTrack.title; - const url = node.currentTrack.url; - const user = node.currentTrack.requestedBy.displayName; - interaction.editReply({ content: getMessage('user.musicplayer.song.info', getGuildLang(interaction.guild.id), { title, url, user }), ephemeral: true }); -} - -async function listQueue(interaction) { - await interaction.deferReply({ ephemeral: true }); - const node = player.nodes.get(interaction.guild); - - // return if there is nothing playing - if (!node) return interaction.editReply({ - content: getMessage('user.musicplayer.not.playing', getGuildLang(interaction.guild.id)), - ephemeral: true - }); - - // return if queue is empty - if (!node.tracks.data.length) return interaction.editReply({ - content: getMessage('user.musicplayer.queue.empty', getGuildLang(interaction.guild.id)), - ephemeral: true - }); - - // limit total characters to avoid 2000 char message limit - const MAX_CHARACTERS = 1800; - let displayedQueueList = []; - let totalChars = 0; - - // map queue entries and calculate total characters - const queueList = node.tracks.data.map(track => { - const message = getMessage('user.musicplayer.queue.info.bullet', getGuildLang(interaction.guild.id), {title: track.title, url: track.url, user: track.requestedBy.displayName}); - totalChars += message.length; - return message; - }); - - // only trim queue list if needed - if (totalChars <= MAX_CHARACTERS) { - displayedQueueList = queueList; - } else { - let currentChars = 0; - displayedQueueList = []; - - for (const track of queueList) { - currentChars += track.length; - if (currentChars > MAX_CHARACTERS) break; - displayedQueueList.push(track); - } - - displayedQueueList.push(getMessage('user.musicplayer.queue.info.limit', getGuildLang(interaction.guild.id), { count: queueList.length - displayedQueueList.length })); - } - - interaction.editReply({ - content: getMessage('user.musicplayer.queue.info', getGuildLang(interaction.guild.id), { queue: displayedQueueList.join('\n')}), - ephemeral: true - }); -}