diff --git a/src/Client.ts b/src/Client.ts index f1ebe9d..4dc9611 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -42,6 +42,8 @@ class RadioClient extends Client { this.funcs.searchStation = require("./client/funcs/searchStation.js"); this.funcs.play = require("./client/funcs/play.js"); this.funcs.listStations = require("./client/funcs/listStations.js"); + this.funcs.restoreRadios = require("./client/funcs/restoreRadios.js"); + this.funcs.saveRadios = require("./client/funcs/saveRadios.js"); console.log('RadioX ' + this.config.version); console.log('Internet Radio to your Discord guild'); diff --git a/src/client/commands.js b/src/client/commands.js index a1a8a14..0d737d5 100644 --- a/src/client/commands.js +++ b/src/client/commands.js @@ -20,6 +20,7 @@ module.exports = { if(command.options) { command.options.forEach(function(option) { if(option.type == "STRING") option.type = 3; + if(option.type == "NUMBER") option.type = 10; command.data.options.push(option); }); } @@ -49,6 +50,7 @@ module.exports = { client.funcs.logger('Slash Commands', 'Guild Applications – Successful' + "\n" + guild.id + " / " + guild.name); } catch (DiscordAPIError) { client.funcs.logger('Slash Commands', 'Guild Applications – Failed' + "\n" + guild.id + " / " + guild.name); + if(DiscordAPIError.name != "DiscordAPIError[50001]") console.error(DiscordAPIError.message + "\n\n"); } }); } else { diff --git a/src/client/commands/maintenance.js b/src/client/commands/maintenance.js index 9913f4d..b76af77 100644 --- a/src/client/commands/maintenance.js +++ b/src/client/commands/maintenance.js @@ -5,58 +5,156 @@ module.exports = { description: 'Bot Maintenance', permission: 'none', category: 'info', - execute(interaction, client) { + options: [ + { type: "NUMBER", name: "action", description: "Select action", required: false} + ], + async execute(interaction, client) { let message = {}; if(!client.funcs.isDev(client.config.devId, interaction.user.id)) return interaction.reply(client.messageEmojis["error"] + client.messages.notAllowed); - - if(client.config.version.includes("-dev")){ - interaction.reply({ - content: "Maintenance Initiated", - ephemeral: true - }); - - process.emit('SIGINT'); - } else { - if(!client.stations) { - message.errorToGetPlaylist = client.messages.errorToGetPlaylist.replace("%client.config.supportGuild%", client.config.supportGuild); - return interaction.reply(client.messageEmojis["error"] + message.errorToGetPlaylist); + let action = interaction.options?.getNumber("action") ?? interaction.values?.[0]; + const options = new Array( + { + emoji: "🌀", + label: "Restart Bot", + description: "", + value: "0" + }, + { + emoji: "<:RadioXStop:688541155377414168>", + label: "Save Radios", + description: "", + value: "4" + }, + { + emoji: "<:RadioXPlay:688541155712827458>", + label: "Restore Radios", + description: "", + value: "5" + }, + { + emoji: "#️⃣", + label: "Reload Commands", + description: "", + value: "6" + }, + { + emoji: "<:dnd:746069698139127831>", + label: "Enable Maintenance Mode", + description: "", + value: "8" + }, + { + emoji: "<:online:746069731836035098>", + label: "Disable Maintenance Mode", + description: "", + value: "9" } - - let currentRadios = client.radio.keys(); - let radio = currentRadios.next(); - let stoppedRadios = ""; - - client.user.setStatus('dnd'); - - while (!radio.done) { - let currentRadio = client.radio.get(radio.value); - currentRadio.guild = client.datastore.getEntry(radio.value).guild; - - if(currentRadio){ - client.funcs.statisticsUpdate(client, currentRadio.guild, currentRadio); - currentRadio.connection?.destroy(); - currentRadio.audioPlayer?.stop(); - currentRadio.message?.delete(); - client.radio.delete(radio.value); - stoppedRadios += "-" + radio.value + ": " + currentRadio.guild.name + "\n"; - } - radio = currentRadios.next(); - } - - const embed = new Discord.MessageEmbed() - .setTitle(client.messages.maintenanceTitle) - .setThumbnail("https://cdn.discordapp.com/emojis/" + client.messageEmojis["maintenance"].replace(/[^0-9]+/g, '')) - .setColor(client.config.embedColor) - .setDescription("Stopped all radios" + "\n" + stoppedRadios) - .setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png') - .setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, '')); - - interaction.reply({ - embeds: [embed], + ); + + const menu = new Discord.MessageActionRow() + .addComponents( + new Discord.MessageSelectMenu() + .setCustomId('maintenance') + .setPlaceholder('Select action') + .addOptions(options) + ); + + if(!action){ + return interaction.reply({ + content: "**" + client.messages.maintenanceTitle + "**", + components: [menu], ephemeral: true }); } + client.funcs.logger('Maintenance', options.find(option => option.value == action).label); + + const embed = new Discord.MessageEmbed() + .setTitle(client.messages.maintenanceTitle) + .setColor(client.config.embedColor) + .setDescription(options.find(option => option.value == action).label) + .setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, '')); + + interaction.reply({ + embeds: [embed], + ephemeral: true + }); + + switch(action){ + case "0": + process.emit('SIGINT'); + break; + case "4": + client.user.setStatus('idle'); + setTimeout(function () { + client.funcs.saveRadios(client); + }, 5000); + client.user.setStatus('online'); + break; + case "5": + client.user.setStatus('idle'); + let guilds = await client.guilds.fetch(); + setTimeout(function () { + client.funcs.restoreRadios(client, guilds); + }, 5000); + client.user.setStatus('online'); + break; + case "6": + client.user.setStatus('idle'); + require(`../commands.js`).execute(client); + client.user.setStatus('online'); + break; + case "8": + client.user.setStatus('dnd'); + break; + case "9": + client.user.setStatus('online'); + break; + default: + + } + + + /* + if(!client.stations) { + message.errorToGetPlaylist = client.messages.errorToGetPlaylist.replace("%client.config.supportGuild%", client.config.supportGuild); + return interaction.reply(client.messageEmojis["error"] + message.errorToGetPlaylist); + } + + let currentRadios = client.radio.keys(); + let radio = currentRadios.next(); + let stoppedRadios = ""; + + client.user.setStatus('dnd'); + + while (!radio.done) { + let currentRadio = client.radio.get(radio.value); + currentRadio.guild = client.datastore.getEntry(radio.value).guild; + + if(currentRadio){ + client.funcs.statisticsUpdate(client, currentRadio.guild, currentRadio); + currentRadio.connection?.destroy(); + currentRadio.audioPlayer?.stop(); + currentRadio.message?.delete(); + client.radio.delete(radio.value); + stoppedRadios += "-" + radio.value + ": " + currentRadio.guild.name + "\n"; + } + radio = currentRadios.next(); + } + + const embed = new Discord.MessageEmbed() + .setTitle(client.messages.maintenanceTitle) + .setThumbnail("https://cdn.discordapp.com/emojis/" + client.messageEmojis["maintenance"].replace(/[^0-9]+/g, '')) + .setColor(client.config.embedColor) + .setDescription("Stopped all radios" + "\n" + stoppedRadios) + .setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png') + .setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, '')); + + interaction.reply({ + embeds: [embed], + ephemeral: true + });*/ + } }; \ No newline at end of file diff --git a/src/client/commands/play.js b/src/client/commands/play.js index d8960fe..3384238 100644 --- a/src/client/commands/play.js +++ b/src/client/commands/play.js @@ -15,14 +15,14 @@ module.exports = { category: "radio", async execute(interaction, client) { let message = {}; + if(!client.stations) { + message.errorToGetPlaylist = client.messages.errorToGetPlaylist.replace("%client.config.supportGuild%", client.config.supportGuild); + return interaction.reply(client.messageEmojis["error"] + message.errorToGetPlaylist); + } + let query = interaction.options?.getString("query") ?? interaction.values?.[0]; if(!query){ - if(!client.stations) { - message.errorToGetPlaylist = client.messages.errorToGetPlaylist.replace("%client.config.supportGuild%", client.config.supportGuild); - return interaction.reply(client.messageEmojis["error"] + message.errorToGetPlaylist); - } - - client.funcs.listStations(client, interaction); + return client.funcs.listStations(client, interaction); } let url = query ? query.replace(/<(.+)>/g, "$1") : ""; const radio = client.radio.get(interaction.guild.id); @@ -40,16 +40,6 @@ module.exports = { ephemeral: true }); } - if (!client.stations) { - message.errorToGetPlaylist = client.messages.errorToGetPlaylist.replace( - "%client.config.supportGuild%", - client.config.supportGuild - ); - return interaction.reply({ - content: client.messageEmojis["error"] + message.errorToGetPlaylist, - ephemeral: true - }); - } if (!query) return interaction.reply(client.messages.noQuery); const permissions = voiceChannel.permissionsFor(interaction.client.user); if (!permissions.has("CONNECT")) { diff --git a/src/client/commands/statistics.js b/src/client/commands/statistics.js index b96ef04..7e87510 100644 --- a/src/client/commands/statistics.js +++ b/src/client/commands/statistics.js @@ -1,5 +1,6 @@ import Discord from "discord.js"; + module.exports = { name: 'statistics', description: 'Show statistics', @@ -18,13 +19,13 @@ module.exports = { } if(!currentGuild || currentGuild && !currentGuild.statistics){ - statistics = "You have not listened any radio station"; + statistics = "You have not listened any radio stations"; } else { Object.keys(stations).forEach(function(station) { if(currentGuild.statistics[stations[station].name] && currentGuild.statistics[stations[station].name].time && parseInt(currentGuild.statistics[stations[station].name].time) > 0 && currentGuild.statistics[stations[station].name].used && parseInt(currentGuild.statistics[stations[station].name].used) > 0){ statistics += `**${parseInt(station) + 1}. ` + stations[station].name + "** \n"; if(global && global.statistics[stations[station].name] && global.statistics[stations[station].name].time && parseInt(global.statistics[stations[station].name].time) > 0 && global.statistics[stations[station].name].used && parseInt(global.statistics[stations[station].name].used) > 0){ - statistics += "Guild – Time: " + client.funcs.msToTime(currentGuild.statistics[stations[station].name].time) + " (" + ((currentGuild.statistics[stations[station].name].time / global.statistics[stations[station].name].time) * 100) + "%" + ")" + " / " + "Used: " + currentGuild.statistics[stations[station].name].used + " (" + ((currentGuild.statistics[stations[station].name].used / global.statistics[stations[station].name].used) * 100) + "%" + ")" + "\n"; + statistics += "Guild – Time: " + client.funcs.msToTime(currentGuild.statistics[stations[station].name].time) + " (" + ((currentGuild.statistics[stations[station].name].time / global.statistics[stations[station].name].time) * 100).toFixed(0) + "%" + ")" + " / " + "Used: " + currentGuild.statistics[stations[station].name].used + " (" + ((currentGuild.statistics[stations[station].name].used / global.statistics[stations[station].name].used) * 100).toFixed(0) + "%" + ")" + "\n"; statistics += "Global – Time: " + client.funcs.msToTime(global.statistics[stations[station].name].time) + " / " + "Used: " + global.statistics[stations[station].name].used + "\n\n"; } else { statistics += "Time: " + client.funcs.msToTime(currentGuild.statistics[stations[station].name].time) + " / " + "Used: " + currentGuild.statistics[stations[station].name].used + "\n\n"; diff --git a/src/client/events/SIGINT.js b/src/client/events/SIGINT.js index 52ed167..55d2a0b 100644 --- a/src/client/events/SIGINT.js +++ b/src/client/events/SIGINT.js @@ -16,50 +16,8 @@ module.exports = { if (!client.stations) return process.exit(); - let currentRadios = client.radio.keys(); - let radio = currentRadios.next(); - - while (!radio.done) { - let currentRadio = client.radio.get(radio.value); - currentRadio.guild = client.datastore.getEntry(radio.value).guild; - - if (currentRadio) { - await client.funcs.statisticsUpdate(client, currentRadio.guild, currentRadio); - await client.funcs.saveState(client, currentRadio.guild, currentRadio); - currentRadio.connection?.destroy(); - currentRadio.audioPlayer?.stop(); - currentRadio.message?.delete(); - client.radio.delete(radio.value); - } - - radio = currentRadios.next(); - } - - /*const rest = new REST({ version: '9' }).setToken(token); - if(version.includes("-dev")){ - await rest.put( - Routes.applicationCommands(client.user.id), - { body: [] }, - ); - - let guilds = await client.guilds.fetch(); - guilds.forEach(async guild => { - try { - await rest.put( - Routes.applicationGuildCommands(client.user.id, guild.id), - { body: [] } - ); - } catch (DiscordAPIError) { - - } - }); - }*/ - - setInterval(() => { - if(radio.done){ - process.exit(); - } - }, 1000); + await client.funcs.saveRadios(client); + await process.exit(); }, 5000); } } \ No newline at end of file diff --git a/src/client/events/interactionCreate.js b/src/client/events/interactionCreate.js index 64e06b9..65d7168 100644 --- a/src/client/events/interactionCreate.js +++ b/src/client/events/interactionCreate.js @@ -3,7 +3,12 @@ module.exports = { async execute(client, interaction) { const permissions = interaction.channel.permissionsFor(interaction.client.user); - if (!permissions.has('EMBED_LINKS')) return interaction.reply(client.messages.noPermsEmbed); + if (!permissions.has('VIEW_CHANNEL')) return; + + if (!permissions.has('EMBED_LINKS')) return interaction.reply({ + content: client.messages.noPermsEmbed, + ephemeral: true + }); if(interaction.isCommand()){ const commandName = interaction.commandName; diff --git a/src/client/events/ready.js b/src/client/events/ready.js index f22dfcc..b712155 100644 --- a/src/client/events/ready.js +++ b/src/client/events/ready.js @@ -94,8 +94,8 @@ module.exports = { require(`../commands.js`).execute(client); setTimeout(function () { - /*RESTORE RADIO*/ - require(`../restoreradio.js`).execute(client, guilds); + /*RESTORE RADIOS*/ + client.funcs.restoreRadios(client, guilds); }, 5000); } diff --git a/src/client/funcs/listStations.js b/src/client/funcs/listStations.js index f410032..6befe09 100644 --- a/src/client/funcs/listStations.js +++ b/src/client/funcs/listStations.js @@ -2,27 +2,16 @@ import Discord from "discord.js"; module.exports = function (client, interaction){ let stations = new Array(); - let options = new Array(); - options[1] = new Array(); - options[2] = new Array(); - stations[1] = client.stations.slice(0,24).forEach(station => { + stations = client.stations.forEach(station => { + if(station.name == "GrooveFM") return; station = { label: station.name, description: station.owner, value: station.name }; - options[1].push(station); - }); - - stations[2] = client.stations.slice(25).forEach(station => { - station = { - label: station.name, - description: station.owner, - value: station.name - }; - options[2].push(station); + options.push(station); }); const menu = new Discord.MessageActionRow() @@ -30,8 +19,7 @@ module.exports = function (client, interaction){ new Discord.MessageSelectMenu() .setCustomId('play') .setPlaceholder('Nothing selected') - .addOptions(options[1]) - .addOptions(options[2]) + .addOptions(options) ); stations = null; diff --git a/src/client/funcs/restoreRadios.js b/src/client/funcs/restoreRadios.js new file mode 100644 index 0000000..c1a4a39 --- /dev/null +++ b/src/client/funcs/restoreRadios.js @@ -0,0 +1,62 @@ +import Discord from "discord.js"; +const { + createAudioPlayer, + getVoiceConnection, + joinVoiceChannel +} = require("@discordjs/voice"); + +module.exports = async function restoreRadios(client, guilds) { + if(!client.stations) return; + + guilds.forEach(async guild => { + let state = client.funcs.loadState(client, guild); + if(!state) return; + if(!state.station || !state.channels.voice || !state.channels.text) return; + let voiceChannel = client.channels.cache.get(state.channels.voice); + if(!voiceChannel) return; + if(voiceChannel.members.size === 0) return; + + + const sstation = await client.funcs.searchStation(state.station.name, client); + let url = sstation.stream[sstation.stream.default]; + let station = sstation; + + const construct = { + textChannel: client.channels.cache.get(state.channels.text), + voiceChannel: client.channels.cache.get(state.channels.voice), + connection: null, + message: null, + audioPlayer: createAudioPlayer(), + station: station + }; + client.radio.set(guild.id, construct); + + try { + const connection = + getVoiceConnection(guild.id) ?? + joinVoiceChannel({ + channelId: voiceChannel.id, + guildId: voiceChannel.guild.id, + adapterCreator: voiceChannel.guild.voiceAdapterCreator + }); + + construct.connection = connection; + let date = new Date(); + construct.startTime = date.getTime(); + + client.funcs.play(null, guild, client, url, Discord); + + client.datastore.checkEntry(guild.id); + construct.datastore = client.datastore.getEntry(guild.id); + + if (!construct.datastore.statistics[construct.station.name]) { + construct.datastore.statistics[construct.station.name] = {}; + construct.datastore.statistics[construct.station.name].time = 0; + construct.datastore.statistics[construct.station.name].used = 0; + client.datastore.updateEntry(guild, construct.datastore); + } + } catch (error) { + console.log(error); + } + }); +} \ No newline at end of file diff --git a/src/client/funcs/saveRadios.js b/src/client/funcs/saveRadios.js new file mode 100644 index 0000000..5311727 --- /dev/null +++ b/src/client/funcs/saveRadios.js @@ -0,0 +1,20 @@ +module.exports = async function saveRadios(client) { + let currentRadios = client.radio.keys(); + let radio = currentRadios.next(); + + while (!radio.done) { + let currentRadio = client.radio.get(radio.value); + currentRadio.guild = client.datastore.getEntry(radio.value).guild; + + if (currentRadio) { + await client.funcs.statisticsUpdate(client, currentRadio.guild, currentRadio); + await client.funcs.saveState(client, currentRadio.guild, currentRadio); + currentRadio.connection?.destroy(); + currentRadio.audioPlayer?.stop(); + currentRadio.message?.delete(); + client.radio.delete(radio.value); + } + + radio = currentRadios.next(); + } +} \ No newline at end of file diff --git a/src/client/restoreradio.js b/src/client/restoreradio.js deleted file mode 100644 index 4916bbc..0000000 --- a/src/client/restoreradio.js +++ /dev/null @@ -1,61 +0,0 @@ -import Discord from "discord.js"; -const { - createAudioPlayer, - getVoiceConnection, - joinVoiceChannel -} = require("@discordjs/voice"); - -module.exports = { - async execute(client, guilds) { - if(!client.stations) return; - - guilds.forEach(async guild => { - let state = client.funcs.loadState(client, guild); - if(!state) return; - if(!state.station || !state.channels.voice || !state.channels.text) return; - - const sstation = await client.funcs.searchStation(state.station.name, client); - let url = sstation.stream[sstation.stream.default]; - let station = sstation; - - const construct = { - textChannel: client.channels.cache.get(state.channels.text), - voiceChannel: client.channels.cache.get(state.channels.voice), - connection: null, - message: null, - audioPlayer: createAudioPlayer(), - station: station - }; - client.radio.set(guild.id, construct); - - try { - let voiceChannel = client.channels.cache.get(state.channels.voice); - const connection = - getVoiceConnection(guild.id) ?? - joinVoiceChannel({ - channelId: voiceChannel.id, - guildId: voiceChannel.guild.id, - adapterCreator: voiceChannel.guild.voiceAdapterCreator - }); - - construct.connection = connection; - let date = new Date(); - construct.startTime = date.getTime(); - - client.funcs.play(null, guild, client, url, Discord); - - client.datastore.checkEntry(guild.id); - construct.datastore = client.datastore.getEntry(guild.id); - - if (!construct.datastore.statistics[construct.station.name]) { - construct.datastore.statistics[construct.station.name] = {}; - construct.datastore.statistics[construct.station.name].time = 0; - construct.datastore.statistics[construct.station.name].used = 0; - client.datastore.updateEntry(guild, construct.datastore); - } - } catch (error) { - console.log(error); - } - }); - } -} \ No newline at end of file