From 3cb07107d55c1d5eb4b3f2267411fe1ec941f917 Mon Sep 17 00:00:00 2001 From: Matte <51192395+MatteZ02@users.noreply.github.com> Date: Tue, 8 Jun 2021 12:01:56 +0300 Subject: [PATCH] Updated src --- client/class.js | 62 ----- client/commands/play.js | 158 ----------- index.js | 2 - src/Client.ts | 67 +++++ {client => src/client}/commands/bug.js | 0 {client => src/client}/commands/help.js | 0 {client => src/client}/commands/invite.js | 0 {client => src/client}/commands/list.js | 0 .../client}/commands/maintenance.js | 104 ++++---- {client => src/client}/commands/nowplaying.js | 0 src/client/commands/play.js | 206 +++++++++++++++ {client => src/client}/commands/statistics.js | 76 +++--- {client => src/client}/commands/status.js | 0 {client => src/client}/commands/stop.js | 0 {client => src/client}/commands/volume.js | 0 {client => src/client}/datastore.js | 248 +++++++++--------- {client => src/client}/emojis.js | 74 +++--- {client => src/client}/events/msg.js | 0 {client => src/client}/events/ready.js | 2 +- .../client}/events/voiceStateUpdate.js | 0 {client => src/client}/funcs/check.js | 0 .../client}/funcs/checkFetchStatus.js | 0 {client => src/client}/funcs/isDev.js | 0 {client => src/client}/funcs/msToTime.js | 32 +-- .../client}/funcs/statisticsUpdate.js | 50 ++-- {client => src/client}/messages.js | 88 +++---- src/client/utils/adapter.ts | 70 +++++ src/client/utils/typings.ts | 3 + config.js => src/config.js | 0 src/index.js | 3 + 30 files changed, 686 insertions(+), 559 deletions(-) delete mode 100644 client/class.js delete mode 100644 client/commands/play.js delete mode 100644 index.js create mode 100644 src/Client.ts rename {client => src/client}/commands/bug.js (100%) rename {client => src/client}/commands/help.js (100%) rename {client => src/client}/commands/invite.js (100%) rename {client => src/client}/commands/list.js (100%) rename {client => src/client}/commands/maintenance.js (98%) rename {client => src/client}/commands/nowplaying.js (100%) create mode 100644 src/client/commands/play.js rename {client => src/client}/commands/statistics.js (98%) rename {client => src/client}/commands/status.js (100%) rename {client => src/client}/commands/stop.js (100%) rename {client => src/client}/commands/volume.js (100%) rename {client => src/client}/datastore.js (92%) rename {client => src/client}/emojis.js (83%) rename {client => src/client}/events/msg.js (100%) rename {client => src/client}/events/ready.js (95%) rename {client => src/client}/events/voiceStateUpdate.js (100%) rename {client => src/client}/funcs/check.js (100%) rename {client => src/client}/funcs/checkFetchStatus.js (100%) rename {client => src/client}/funcs/isDev.js (100%) rename {client => src/client}/funcs/msToTime.js (97%) rename {client => src/client}/funcs/statisticsUpdate.js (98%) rename {client => src/client}/messages.js (98%) create mode 100644 src/client/utils/adapter.ts create mode 100644 src/client/utils/typings.ts rename config.js => src/config.js (100%) create mode 100644 src/index.js diff --git a/client/class.js b/client/class.js deleted file mode 100644 index d291d04..0000000 --- a/client/class.js +++ /dev/null @@ -1,62 +0,0 @@ -const { Client, Collection } = require('discord.js'); -const Discord = require('discord.js'); -const fs = require('fs'); -const path = require('path'); -const events = './events/'; -const Datastore = require('./datastore.js'); - -const GatewayIntents = new Discord.Intents(); -GatewayIntents.add( - 1 << 0, // GUILDS - 1 << 7, // GUILD_VOICE_STATES - 1 << 9 // GUILD_MESSAGES -); - -module.exports = class extends Client { - constructor() { - super({ - disableEveryone: true, - disabledEvents: ['TYPING_START'], - ws: { - intents: GatewayIntents - } - }); - this.commands = new Collection(); - this.commandAliases = new Collection(); - this.radio = new Map(); - - this.funcs = {}; - this.funcs.check = require('./funcs/check.js'); - this.funcs.checkFetchStatus = require('./funcs/checkFetchStatus.js'); - this.funcs.isDev = require('./funcs/isDev.js'); - this.funcs.msToTime = require('./funcs/msToTime.js'); - this.funcs.statisticsUpdate = require('./funcs/statisticsUpdate.js'); - - this.config = require('../config.js'); - this.messages = require('./messages.js'); - - const commandFiles = fs.readdirSync('./client/commands/').filter(f => f.endsWith('.js')); - for (const file of commandFiles) { - const command = require(`./commands/${file}`); - command.uses = 0; - this.commands.set(command.name, command); - this.commandAliases.set(command.alias, command); - } - - this.on('ready', () => { - require(`${events}ready`).execute(this, Discord); - this.datastore = new Datastore(); - }); - this.on('message', (msg) => { - require(`${events}msg`).execute(this, msg, Discord); - }); - this.on('voiceStateUpdate', (oldState, newState) => { - require(`${events}voiceStateUpdate`).execute(this, oldState, newState); - }); - this.on('error', (error) => { - console.error(error); - }); - - this.login(this.config.token).catch(err => console.log('Failed to login: ' + err)); - } -}; diff --git a/client/commands/play.js b/client/commands/play.js deleted file mode 100644 index 93f39d6..0000000 --- a/client/commands/play.js +++ /dev/null @@ -1,158 +0,0 @@ -module.exports = { - name: 'play', - alias: 'p', - usage: '', - description: 'Play some music.', - permission: 'none', - category: 'radio', - async execute(msg, args, client, Discord, command) { - let message = {}; - let url = args[1] ? args[1].replace(/<(.+)>/g, "$1") : ""; - const radio = client.radio.get(msg.guild.id); - const voiceChannel = msg.member.voice.channel; - if (!radio) { - if (!msg.member.voice.channel) return msg.channel.send(client.messageEmojis["error"] + client.messages.noVoiceChannel); - } else { - if (voiceChannel !== radio.voiceChannel) return msg.channel.send(client.messageEmojis["error"] + client.messages.wrongVoiceChannel); - } - if(!client.stations) { - message.errorToGetPlaylist = client.messages.errorToGetPlaylist.replace("%client.config.supportGuild%", client.config.supportGuild); - return msg.channel.send(client.messageEmojis["error"] + message.errorToGetPlaylist); - } - if (!args[1]) return msg.channel.send(client.messages.noQuery); - const permissions = voiceChannel.permissionsFor(msg.client.user); - if (!permissions.has('CONNECT')) { - return msg.channel.send(client.messageEmojis["error"] + client.messages.noPermsConnect); - } - if (!permissions.has('SPEAK')) { - return msg.channel.send(client.messageEmojis["error"] + client.messages.noPermsSpeak); - } - let station; - const number = parseInt(args[1] - 1); - if (url.startsWith('http')) { - return msg.channel.send(client.messageEmojis["error"] + client.messages.errorStationURL); - } else if (!isNaN(number)) { - if (number > client.stations.length - 1) { - return msg.channel.send(client.messageEmojis["error"] + client.messages.wrongStationNumber); - } else { - url = client.stations[number].stream[client.stations[number].stream.default]; - station = client.stations[number]; - } - } else { - if (args[1].length < 3) return msg.channel.send(client.messageEmojis["error"] + client.messages.tooShortSearch); - const sstation = await searchStation(args.slice(1).join(' '), client); - if (!sstation) return msg.channel.send(client.messageEmojis["error"] + client.messages.noSearchResults); - url = sstation.stream[sstation.stream.default]; - station = sstation; - } - - if (radio) { - - client.funcs.statisticsUpdate(client, msg.guild, radio); - - radio.connection.dispatcher.destroy(); - radio.station = station; - radio.textChannel = msg.channel; - play(msg.guild, client, url); - - return; - } - - const construct = { - textChannel: msg.channel, - voiceChannel: voiceChannel, - connection: null, - station: station, - volume: 5, - }; - client.radio.set(msg.guild.id, construct); - - try { - const connection = await voiceChannel.join(); - construct.connection = connection; - let date = new Date(); - construct.startTime = date.getTime(); - play(msg.guild, client, url); - - client.datastore.checkEntry(msg.guild.id); - construct.currentGuild = client.datastore.getEntry(msg.guild.id); - - if(!construct.currentGuild.statistics[construct.station.name]){ - construct.currentGuild.statistics[construct.station.name] = {}; - construct.currentGuild.statistics[construct.station.name].time = 0; - construct.currentGuild.statistics[construct.station.name].used = 0; - client.datastore.updateEntry(msg.guild, construct.currentGuild); - } - - } catch (error) { - console.log(error); - client.radio.delete(msg.guild.id); - return msg.channel.send(client.messageEmojis["error"] + `An error occured: ${error}`); - } - } -}; -function play(guild, client, url) { - let message = {}; - const radio = client.radio.get(guild.id); - - const dispatcher = radio.connection - .play(url, { bitrate: "auto", volume: 1 }) - .on("finish", () => { - console.log("Stream finished"); - client.funcs.statisticsUpdate(client, guild, radio); - radio.voiceChannel.leave(); - client.radio.delete(guild.id); - return; - }); - - dispatcher.on('error', error => { - console.error(error); - radio.voiceChannel.leave(); - client.radio.delete(guild.id); - return radio.textChannel.send(client.messages.errorPlaying); - }); - - dispatcher.setVolume(radio.volume / 10); - - message.play = client.messages.play.replace("%radio.station.name%", radio.station.name); - radio.textChannel.send(client.messageEmojis["play"] + message.play); -}; - -function searchStation(key, client) { - if (client.stations === null) return false; - let foundStations = []; - if (!key) return false; - if (key == 'radio') return false; - if (key.startsWith("radio ")) key = key.slice(6); - const probabilityIncrement = 100 / key.split(' ').length / 2; - for (let i = 0; i < key.split(' ').length; i++) { - client.stations.filter(x => x.name.toUpperCase().includes(key.split(' ')[i].toUpperCase()) || x === key).forEach(x => foundStations.push({ station: x, name: x.name, probability: probabilityIncrement })); - } - if (foundStations.length === 0) return false; - for (let i = 0; i < foundStations.length; i++) { - for (let j = 0; j < foundStations.length; j++) { - if (foundStations[i] === foundStations[j] && i !== j) foundStations.splice(i, 1); - } - } - for (let i = 0; i < foundStations.length; i++) { - if (foundStations[i].name.length > key.length) { - foundStations[i].probability -= (foundStations[i].name.split(' ').length - key.split(' ').length) * (probabilityIncrement * 0.5); - } else if (foundStations[i].name.length === key.length) { - foundStations[i].probability += (probabilityIncrement * 0.9); - } - - for (let j = 0; j < key.split(' ').length; j++) { - if (!foundStations[i].name.toUpperCase().includes(key.toUpperCase().split(' ')[j])) { - foundStations[i].probability -= (probabilityIncrement * 0.5); - } - } - } - let highestProbabilityStation; - for (let i = 0; i < foundStations.length; i++) { - if (!highestProbabilityStation || highestProbabilityStation.probability < foundStations[i].probability) highestProbabilityStation = foundStations[i]; - if (highestProbabilityStation && highestProbabilityStation.probability === foundStations[i].probability) { - highestProbabilityStation = foundStations[i].station; - } - } - return highestProbabilityStation; -}; diff --git a/index.js b/index.js deleted file mode 100644 index 903ebfc..0000000 --- a/index.js +++ /dev/null @@ -1,2 +0,0 @@ -const radioClient = require("./client/class.js"); -const client = new radioClient(); \ No newline at end of file diff --git a/src/Client.ts b/src/Client.ts new file mode 100644 index 0000000..1e60e78 --- /dev/null +++ b/src/Client.ts @@ -0,0 +1,67 @@ +import Discord, { Client, Collection } from "discord.js"; +import fs from "fs"; +const events = "./client/events/"; +import Datastore from "./client/datastore.js"; +import { command, radio } from "./client/utils/typings.js"; +import config from "./config.js"; +import messages from "./client/messages.js"; +import path from "path" + +const GatewayIntents = new Discord.Intents(); +GatewayIntents.add( + 1 << 0, // GUILDS + 1 << 7, // GUILD_VOICE_STATES + 1 << 9 // GUILD_MESSAGES +); + +class RadioClient extends Client { + readonly commands: Collection; + readonly commandAliases: Collection; + readonly radio: Map; + public funcs: any; + readonly config = config; + readonly messages = messages; + public datastore: Datastore | null; + constructor() { + super({ + intents: GatewayIntents + }); + this.commands = new Collection(); + this.commandAliases = new Collection(); + this.radio = new Map(); + this.datastore = null; + + this.funcs = {}; + this.funcs.check = require("./client/funcs/check.js"); + this.funcs.checkFetchStatus = require("./client/funcs/checkFetchStatus.js"); + this.funcs.isDev = require("./client/funcs/isDev.js"); + this.funcs.msToTime = require("./client/funcs/msToTime.js"); + this.funcs.statisticsUpdate = require("./client/funcs/statisticsUpdate.js"); + + const commandFiles = fs.readdirSync("D:/GitHub/eximiabots-radiox/src/client/commands/"/*path.join("./client/commands")*/).filter(f => f.endsWith(".js")); + for (const file of commandFiles) { + const command = require(`./client/commands/${file}`); + command.uses = 0; + this.commands.set(command.name, command); + this.commandAliases.set(command.alias, command); + } + + this.on("ready", () => { + require(`${events}ready`).execute(this, Discord); + this.datastore = new Datastore(); + }); + this.on("message", msg => { + require(`${events}msg`).execute(this, msg, Discord); + }); + this.on("voiceStateUpdate", (oldState, newState) => { + require(`${events}voiceStateUpdate`).execute(this, oldState, newState); + }); + this.on("error", error => { + console.error(error); + }); + + this.login(this.config.token).catch(err => console.log("Failed to login: " + err)); + } +} + +export default RadioClient \ No newline at end of file diff --git a/client/commands/bug.js b/src/client/commands/bug.js similarity index 100% rename from client/commands/bug.js rename to src/client/commands/bug.js diff --git a/client/commands/help.js b/src/client/commands/help.js similarity index 100% rename from client/commands/help.js rename to src/client/commands/help.js diff --git a/client/commands/invite.js b/src/client/commands/invite.js similarity index 100% rename from client/commands/invite.js rename to src/client/commands/invite.js diff --git a/client/commands/list.js b/src/client/commands/list.js similarity index 100% rename from client/commands/list.js rename to src/client/commands/list.js diff --git a/client/commands/maintenance.js b/src/client/commands/maintenance.js similarity index 98% rename from client/commands/maintenance.js rename to src/client/commands/maintenance.js index 405baab..8794be3 100644 --- a/client/commands/maintenance.js +++ b/src/client/commands/maintenance.js @@ -1,53 +1,53 @@ -module.exports = { - name: 'maintenance', - alias: 'm', - usage: '', - description: 'Bot Maintenance', - permission: 'none', - category: 'info', - execute(msg, args, client, Discord, command) { - let message = {}; - - if(!client.funcs.isDev(client.config.devId, msg.author.id)) return msg.channel.send(client.messageEmojis["error"] + client.messages.notAllowed); - - if(!client.stations) { - message.errorToGetPlaylist = client.messages.errorToGetPlaylist.replace("%client.config.supportGuild%", client.config.supportGuild); - return msg.channel.send(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.currentGuild.guild, currentRadio); - currentRadio.connection.dispatcher?.destroy(); - currentRadio.voiceChannel.leave(); - const cembed = 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(client.messages.sendedMaintenanceMessage) - .setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, '')); - currentRadio.textChannel.send(cembed); - client.radio.delete(radio.value); - stoppedRadios += "-" + radio.value + ": " + currentRadio.currentGuild.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) - .setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, '')); - return msg.channel.send(embed); - } +module.exports = { + name: 'maintenance', + alias: 'm', + usage: '', + description: 'Bot Maintenance', + permission: 'none', + category: 'info', + execute(msg, args, client, Discord, command) { + let message = {}; + + if(!client.funcs.isDev(client.config.devId, msg.author.id)) return msg.channel.send(client.messageEmojis["error"] + client.messages.notAllowed); + + if(!client.stations) { + message.errorToGetPlaylist = client.messages.errorToGetPlaylist.replace("%client.config.supportGuild%", client.config.supportGuild); + return msg.channel.send(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.currentGuild.guild, currentRadio); + currentRadio.connection.dispatcher?.destroy(); + currentRadio.voiceChannel.leave(); + const cembed = 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(client.messages.sendedMaintenanceMessage) + .setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, '')); + currentRadio.textChannel.send(cembed); + client.radio.delete(radio.value); + stoppedRadios += "-" + radio.value + ": " + currentRadio.currentGuild.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) + .setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, '')); + return msg.channel.send(embed); + } }; \ No newline at end of file diff --git a/client/commands/nowplaying.js b/src/client/commands/nowplaying.js similarity index 100% rename from client/commands/nowplaying.js rename to src/client/commands/nowplaying.js diff --git a/src/client/commands/play.js b/src/client/commands/play.js new file mode 100644 index 0000000..aa01e4c --- /dev/null +++ b/src/client/commands/play.js @@ -0,0 +1,206 @@ +const { + createAudioPlayer, + createAudioResource, + getVoiceConnection, + joinVoiceChannel +} = require("@discordjs/voice"); +const { createDiscordJSAdapter } = require("../utils/adapter"); + +module.exports = { + name: "play", + alias: "p", + usage: "", + description: "Play some music.", + permission: "none", + category: "radio", + async execute(msg, args, client, Discord, command) { + let message = {}; + let url = args[1] ? args[1].replace(/<(.+)>/g, "$1") : ""; + const radio = client.radio.get(msg.guild.id); + const voiceChannel = msg.member.voice.channel; + if (!radio) { + if (!msg.member.voice.channel) + return msg.channel.send( + client.messageEmojis["error"] + client.messages.noVoiceChannel + ); + } else { + if (voiceChannel !== radio.voiceChannel) + return msg.channel.send( + client.messageEmojis["error"] + client.messages.wrongVoiceChannel + ); + } + if (!client.stations) { + message.errorToGetPlaylist = client.messages.errorToGetPlaylist.replace( + "%client.config.supportGuild%", + client.config.supportGuild + ); + return msg.channel.send(client.messageEmojis["error"] + message.errorToGetPlaylist); + } + if (!args[1]) return msg.channel.send(client.messages.noQuery); + const permissions = voiceChannel.permissionsFor(msg.client.user); + if (!permissions.has("CONNECT")) { + return msg.channel.send(client.messageEmojis["error"] + client.messages.noPermsConnect); + } + if (!permissions.has("SPEAK")) { + return msg.channel.send(client.messageEmojis["error"] + client.messages.noPermsSpeak); + } + let station; + const number = parseInt(args[1] - 1); + if (url.startsWith("http")) { + return msg.channel.send( + client.messageEmojis["error"] + client.messages.errorStationURL + ); + } else if (!isNaN(number)) { + if (number > client.stations.length - 1) { + return msg.channel.send( + client.messageEmojis["error"] + client.messages.wrongStationNumber + ); + } else { + url = client.stations[number].stream[client.stations[number].stream.default]; + station = client.stations[number]; + } + } else { + if (args[1].length < 3) + return msg.channel.send( + client.messageEmojis["error"] + client.messages.tooShortSearch + ); + const sstation = await searchStation(args.slice(1).join(" "), client); + if (!sstation) + return msg.channel.send( + client.messageEmojis["error"] + client.messages.noSearchResults + ); + url = sstation.stream[sstation.stream.default]; + station = sstation; + } + + if (radio) { + client.funcs.statisticsUpdate(client, msg.guild, radio); + + radio.connection.dispatcher.destroy(); + radio.station = station; + radio.textChannel = msg.channel; + play(msg.guild, client, url); + + return; + } + + const construct = { + textChannel: msg.channel, + voiceChannel: voiceChannel, + connection: null, + audioPlayer: createAudioPlayer(), + station: station, + volume: 5 + }; + client.radio.set(msg.guild.id, construct); + + try { + const connection = + getVoiceConnection(voiceChannel.guild.id) ?? + joinVoiceChannel({ + channelId: voiceChannel.id, + guildId: voiceChannel.guild.id, + adapterCreator: createDiscordJSAdapter(voiceChannel) + }); + construct.connection = connection; + let date = new Date(); + construct.startTime = date.getTime(); + play(msg.guild, client, url); + + client.datastore.checkEntry(msg.guild.id); + construct.currentGuild = client.datastore.getEntry(msg.guild.id); + + if (!construct.currentGuild.statistics[construct.station.name]) { + construct.currentGuild.statistics[construct.station.name] = {}; + construct.currentGuild.statistics[construct.station.name].time = 0; + construct.currentGuild.statistics[construct.station.name].used = 0; + client.datastore.updateEntry(msg.guild, construct.currentGuild); + } + } catch (error) { + console.log(error); + client.radio.delete(msg.guild.id); + return msg.channel.send(client.messageEmojis["error"] + `An error occured: ${error}`); + } + } +}; +function play(guild, client, url) { + let message = {}; + const radio = client.radio.get(guild.id); + const resource = createAudioResource(url); + radio.connection.subscribe(radio.audioPlayer); + radio.audioPlayer.play(resource); + resource.playStream + .on("readable", () => { + console.log("Stream started"); + }) + .on("finish", () => { + console.log("Stream finished"); + client.funcs.statisticsUpdate(client, guild, radio); + radio.voiceChannel.leave(); + client.radio.delete(guild.id); + return; + }) + .on("error", error => { + console.error(error); + radio.voiceChannel.leave(); + client.radio.delete(guild.id); + return radio.textChannel.send(client.messages.errorPlaying); + }); + + message.play = client.messages.play.replace("%radio.station.name%", radio.station.name); + radio.textChannel.send(client.messageEmojis["play"] + message.play); +} + +function searchStation(key, client) { + if (client.stations === null) return false; + let foundStations = []; + if (!key) return false; + if (key == "radio") return false; + if (key.startsWith("radio ")) key = key.slice(6); + const probabilityIncrement = 100 / key.split(" ").length / 2; + for (let i = 0; i < key.split(" ").length; i++) { + client.stations + .filter( + x => x.name.toUpperCase().includes(key.split(" ")[i].toUpperCase()) || x === key + ) + .forEach(x => + foundStations.push({ station: x, name: x.name, probability: probabilityIncrement }) + ); + } + if (foundStations.length === 0) return false; + for (let i = 0; i < foundStations.length; i++) { + for (let j = 0; j < foundStations.length; j++) { + if (foundStations[i] === foundStations[j] && i !== j) foundStations.splice(i, 1); + } + } + for (let i = 0; i < foundStations.length; i++) { + if (foundStations[i].name.length > key.length) { + foundStations[i].probability -= + (foundStations[i].name.split(" ").length - key.split(" ").length) * + (probabilityIncrement * 0.5); + } else if (foundStations[i].name.length === key.length) { + foundStations[i].probability += probabilityIncrement * 0.9; + } + + for (let j = 0; j < key.split(" ").length; j++) { + if (!foundStations[i].name.toUpperCase().includes(key.toUpperCase().split(" ")[j])) { + foundStations[i].probability -= probabilityIncrement * 0.5; + } + } + } + let highestProbabilityStation; + for (let i = 0; i < foundStations.length; i++) { + if ( + !highestProbabilityStation || + highestProbabilityStation.probability < foundStations[i].probability + ) + highestProbabilityStation = foundStations[i]; + if ( + highestProbabilityStation && + highestProbabilityStation.probability === foundStations[i].probability + ) { + highestProbabilityStation = foundStations[i].station; + } + } + return highestProbabilityStation; +} diff --git a/client/commands/statistics.js b/src/client/commands/statistics.js similarity index 98% rename from client/commands/statistics.js rename to src/client/commands/statistics.js index 33f144b..4d91085 100644 --- a/client/commands/statistics.js +++ b/src/client/commands/statistics.js @@ -1,39 +1,39 @@ -module.exports = { - name: 'statistics', - alias: 'stats', - usage: '', - description: 'Show usage statistics.', - permission: 'none', - category: 'info', - execute(msg, args, client, Discord, command) { - let message = {}; - let stations = client.stations; - let currentGuild = client.datastore.getEntry(msg.guild.id); - let statistics = ""; - - if(!client.stations) { - message.errorToGetPlaylist = client.messages.errorToGetPlaylist.replace("%client.config.supportGuild%", client.config.supportGuild); - return msg.channel.send(client.messageEmojis["error"] + message.errorToGetPlaylist); - } - - if(!currentGuild || currentGuild && !currentGuild.statistics){ - statistics = "You have not listened any radio station"; - } 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"; - statistics += "Time: " + client.funcs.msToTime(currentGuild.statistics[stations[station].name].time, "dd:hh:mm:ss") + "\n"; - statistics += "Used: " + currentGuild.statistics[stations[station].name].used + "\n"; - } - }); - } - - const embed = new Discord.MessageEmbed() - .setTitle(client.messages.statisticsTitle) - .setThumbnail("https://cdn.discordapp.com/emojis/" + client.messageEmojis["statistics"].replace(/[^0-9]+/g, '')) - .setColor(client.config.embedColor) - .setDescription(statistics) - .setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, '')); - return msg.channel.send(embed); - } +module.exports = { + name: 'statistics', + alias: 'stats', + usage: '', + description: 'Show usage statistics.', + permission: 'none', + category: 'info', + execute(msg, args, client, Discord, command) { + let message = {}; + let stations = client.stations; + let currentGuild = client.datastore.getEntry(msg.guild.id); + let statistics = ""; + + if(!client.stations) { + message.errorToGetPlaylist = client.messages.errorToGetPlaylist.replace("%client.config.supportGuild%", client.config.supportGuild); + return msg.channel.send(client.messageEmojis["error"] + message.errorToGetPlaylist); + } + + if(!currentGuild || currentGuild && !currentGuild.statistics){ + statistics = "You have not listened any radio station"; + } 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"; + statistics += "Time: " + client.funcs.msToTime(currentGuild.statistics[stations[station].name].time, "dd:hh:mm:ss") + "\n"; + statistics += "Used: " + currentGuild.statistics[stations[station].name].used + "\n"; + } + }); + } + + const embed = new Discord.MessageEmbed() + .setTitle(client.messages.statisticsTitle) + .setThumbnail("https://cdn.discordapp.com/emojis/" + client.messageEmojis["statistics"].replace(/[^0-9]+/g, '')) + .setColor(client.config.embedColor) + .setDescription(statistics) + .setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, '')); + return msg.channel.send(embed); + } }; \ No newline at end of file diff --git a/client/commands/status.js b/src/client/commands/status.js similarity index 100% rename from client/commands/status.js rename to src/client/commands/status.js diff --git a/client/commands/stop.js b/src/client/commands/stop.js similarity index 100% rename from client/commands/stop.js rename to src/client/commands/stop.js diff --git a/client/commands/volume.js b/src/client/commands/volume.js similarity index 100% rename from client/commands/volume.js rename to src/client/commands/volume.js diff --git a/client/datastore.js b/src/client/datastore.js similarity index 92% rename from client/datastore.js rename to src/client/datastore.js index 264ff69..7d94ee4 100644 --- a/client/datastore.js +++ b/src/client/datastore.js @@ -1,124 +1,124 @@ -const fs = require('fs'); -const path = require('path'); - -module.exports = class { - constructor() { - this.map = new Map(); - this.loadData(); - } - - loadData() { - //console.log(""); - const dataFiles = fs.readdirSync(path.join(path.dirname(__dirname), 'datastore')).filter(f => f.endsWith('.json')); - for (const file of dataFiles) { - try { - const json = require(`../datastore/${file}`); - this.map.set(json.guild.id, json); - //console.log('[LOADED] ' + file + " (" + json.guild.id + ")"); - //console.log(JSON.stringify(json, null, 4)); - } catch (error) { - //console.log('[ERROR] Loading ' + file + ' failed'); - } - } - //console.log(""); - } - - calculateGlobal(client){ - let guilds = this.map.keys(); - let stations = client.stations; - var statistics = {}; - - if(!client.stations) return; - - let calculation = guilds.next(); - - while (!calculation.done) { - let currentGuild = this.getEntry(calculation.value); - if(calculation.value != 'global'){ - if(stations){ - 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){ - if(!statistics[stations[station].name]){ - statistics[stations[station].name] = {}; - statistics[stations[station].name].time = 0; - statistics[stations[station].name].used = 0; - } - - statistics[stations[station].name].time = parseInt(statistics[stations[station].name].time)+parseInt(currentGuild.statistics[stations[station].name].time); - statistics[stations[station].name].used = parseInt(statistics[stations[station].name].used)+parseInt(currentGuild.statistics[stations[station].name].used); - } - }); - } - } - calculation = guilds.next(); - } - - let newData = {}; - newData.guild = {}; - newData.guild.id = "global"; - newData.guild.name = "global"; - newData.statistics = statistics; - this.updateEntry(newData.guild, newData); - } - - - checkEntry(id){ - if(!this.map.has(id)){ - this.createEntry(id); - //this.showEntry(this.getEntry(id)); - } else { - //this.showEntry(this.getEntry(id)); - } - } - - createEntry(id){ - let newData = {}; - newData.guild = {}; - newData.guild.id = id; - newData.statistics = {}; - this.map.set(id, newData); - this.saveEntry(id, newData); - } - - getEntry(id){ - return this.map.get(id); - } - - updateEntry(guild, newData) { - newData.guild.name = guild.name; - this.map.set(guild.id, newData); - this.saveEntry(guild.id, newData); - //this.showEntry(this.getEntry(guild.id)); - } - - showEntry(data){ - console.log(data); - } - - createTestFile () { - let newData = { - "guild": { - "id": "test", - "name": "Test" - }, - "statistics": { - "test": { - "time": 0, - "used": 0 - } - } - } - - this.updateEntry(newData.guild, newData); - } - - saveEntry(file, data) { - data = JSON.stringify(data, null, 4); - - fs.writeFile(path.join(path.dirname(__dirname), 'datastore') + "/" + file + ".json", data, 'utf8', function(err) { - if (err) { - //console.log(err); - } - }); - } -}; +const fs = require('fs'); +const path = require('path'); + +module.exports = class { + constructor() { + this.map = new Map(); + this.loadData(); + } + + loadData() { + //console.log(""); + const dataFiles = fs.readdirSync("D:/GitHub/eximiabots-radiox/datastore"/*path.join(path.dirname(__dirname), 'datastore')*/).filter(f => f.endsWith('.json')); + for (const file of dataFiles) { + try { + const json = require(`../../datastore/${file}`); + this.map.set(json.guild.id, json); + //console.log('[LOADED] ' + file + " (" + json.guild.id + ")"); + //console.log(JSON.stringify(json, null, 4)); + } catch (error) { + //console.log('[ERROR] Loading ' + file + ' failed'); + } + } + //console.log(""); + } + + calculateGlobal(client){ + let guilds = this.map.keys(); + let stations = client.stations; + var statistics = {}; + + if(!client.stations) return; + + let calculation = guilds.next(); + + while (!calculation.done) { + let currentGuild = this.getEntry(calculation.value); + if(calculation.value != 'global'){ + if(stations){ + 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){ + if(!statistics[stations[station].name]){ + statistics[stations[station].name] = {}; + statistics[stations[station].name].time = 0; + statistics[stations[station].name].used = 0; + } + + statistics[stations[station].name].time = parseInt(statistics[stations[station].name].time)+parseInt(currentGuild.statistics[stations[station].name].time); + statistics[stations[station].name].used = parseInt(statistics[stations[station].name].used)+parseInt(currentGuild.statistics[stations[station].name].used); + } + }); + } + } + calculation = guilds.next(); + } + + let newData = {}; + newData.guild = {}; + newData.guild.id = "global"; + newData.guild.name = "global"; + newData.statistics = statistics; + this.updateEntry(newData.guild, newData); + } + + + checkEntry(id){ + if(!this.map.has(id)){ + this.createEntry(id); + //this.showEntry(this.getEntry(id)); + } else { + //this.showEntry(this.getEntry(id)); + } + } + + createEntry(id){ + let newData = {}; + newData.guild = {}; + newData.guild.id = id; + newData.statistics = {}; + this.map.set(id, newData); + this.saveEntry(id, newData); + } + + getEntry(id){ + return this.map.get(id); + } + + updateEntry(guild, newData) { + newData.guild.name = guild.name; + this.map.set(guild.id, newData); + this.saveEntry(guild.id, newData); + //this.showEntry(this.getEntry(guild.id)); + } + + showEntry(data){ + console.log(data); + } + + createTestFile () { + let newData = { + "guild": { + "id": "test", + "name": "Test" + }, + "statistics": { + "test": { + "time": 0, + "used": 0 + } + } + } + + this.updateEntry(newData.guild, newData); + } + + saveEntry(file, data) { + data = JSON.stringify(data, null, 4); + + fs.writeFile(path.join(path.dirname(__dirname), 'datastore') + "/" + file + ".json", data, 'utf8', function(err) { + if (err) { + //console.log(err); + } + }); + } +}; diff --git a/client/emojis.js b/src/client/emojis.js similarity index 83% rename from client/emojis.js rename to src/client/emojis.js index f196a47..211aeaa 100644 --- a/client/emojis.js +++ b/src/client/emojis.js @@ -1,38 +1,38 @@ -module.exports = { - name: 'emojis', - async execute(client) { - let customEmojis = { - logo: "<:RadioX:688765708808487072>", - eximiabots: "<:EximiaBots:693277919929303132>", - list: "<:RadioXList:688541155519889482>", - play: "<:RadioXPlay:688541155712827458>", - stop: "<:RadioXStop:688541155377414168>", - statistics: "<:RadioXStatistics:694954485507686421>", - maintenance: "<:RadioXMaintenance:695043843057254493>", - error: "<:RadioXError:688541155792781320>" - }; - - let fallbackEmojis = { - logo: "RadioX", - eximiabots: "EximiaBots", - list: "📜", - play: "▶️", - stop: "⏹️", - statistics: "📊", - maintenance: "🛠️", - error: "❌" - }; - - client.messageEmojis = {}; - - for (customEmojiName in customEmojis) { - customEmojiID = customEmojis[customEmojiName].replace(/[^0-9]+/g, ''); - customEmoji = client.emojis.cache.get(customEmojiID); - if (customEmoji) { - client.messageEmojis[customEmojiName] = customEmojis[customEmojiName]; - } else { - client.messageEmojis[customEmojiName] = fallbackEmojis[customEmojiName]; - } - } - } +module.exports = { + name: 'emojis', + async execute(client) { + let customEmojis = { + logo: "<:RadioX:688765708808487072>", + eximiabots: "<:EximiaBots:693277919929303132>", + list: "<:RadioXList:688541155519889482>", + play: "<:RadioXPlay:688541155712827458>", + stop: "<:RadioXStop:688541155377414168>", + statistics: "<:RadioXStatistics:694954485507686421>", + maintenance: "<:RadioXMaintenance:695043843057254493>", + error: "<:RadioXError:688541155792781320>" + }; + + let fallbackEmojis = { + logo: "RadioX", + eximiabots: "EximiaBots", + list: "📜", + play: "▶️", + stop: "⏹️", + statistics: "📊", + maintenance: "🛠️", + error: "❌" + }; + + client.messageEmojis = {}; + + for (const customEmojiName in customEmojis) { + const customEmojiID = customEmojis[customEmojiName].replace(/[^0-9]+/g, ''); + const customEmoji = client.emojis.cache.get(customEmojiID); + if (customEmoji) { + client.messageEmojis[customEmojiName] = customEmojis[customEmojiName]; + } else { + client.messageEmojis[customEmojiName] = fallbackEmojis[customEmojiName]; + } + } + } } \ No newline at end of file diff --git a/client/events/msg.js b/src/client/events/msg.js similarity index 100% rename from client/events/msg.js rename to src/client/events/msg.js diff --git a/client/events/ready.js b/src/client/events/ready.js similarity index 95% rename from client/events/ready.js rename to src/client/events/ready.js index 93b0d07..8e0f778 100644 --- a/client/events/ready.js +++ b/src/client/events/ready.js @@ -10,7 +10,7 @@ module.exports = { client.developers = ""; let user = ""; - for (i = 0; i < client.config.devId.length; i++) { + for (let i = 0; i < client.config.devId.length; i++) { user = await client.users.fetch(client.config.devId[i]); if (i == client.config.devId.length - 1) { client.developers += user.tag; diff --git a/client/events/voiceStateUpdate.js b/src/client/events/voiceStateUpdate.js similarity index 100% rename from client/events/voiceStateUpdate.js rename to src/client/events/voiceStateUpdate.js diff --git a/client/funcs/check.js b/src/client/funcs/check.js similarity index 100% rename from client/funcs/check.js rename to src/client/funcs/check.js diff --git a/client/funcs/checkFetchStatus.js b/src/client/funcs/checkFetchStatus.js similarity index 100% rename from client/funcs/checkFetchStatus.js rename to src/client/funcs/checkFetchStatus.js diff --git a/client/funcs/isDev.js b/src/client/funcs/isDev.js similarity index 100% rename from client/funcs/isDev.js rename to src/client/funcs/isDev.js diff --git a/client/funcs/msToTime.js b/src/client/funcs/msToTime.js similarity index 97% rename from client/funcs/msToTime.js rename to src/client/funcs/msToTime.js index 8fafa43..666d545 100644 --- a/client/funcs/msToTime.js +++ b/src/client/funcs/msToTime.js @@ -1,17 +1,17 @@ -module.exports = function msToTime(duration, format) { - var seconds = Math.floor((duration / 1000) % 60), - minutes = Math.floor((duration / (1000 * 60)) % 60), - hours = Math.floor((duration / (1000 * 60 * 60)) % 24), - days = Math.floor((duration / (1000 * 60 * 60 * 24))); - - days = (days < 10) ? "0" + days : days; - hours = (hours < 10) ? "0" + hours : hours; - minutes = (minutes < 10) ? "0" + minutes : minutes; - seconds = (seconds < 10) ? "0" + seconds : seconds; - - if (format === "hh:mm:ss") { - return `${hours}:${minutes}:${seconds}`; - } else if (format === "dd:hh:mm:ss") { - return `${days}:${hours}:${minutes}:${seconds}`; - } +module.exports = function msToTime(duration, format) { + var seconds = Math.floor((duration / 1000) % 60), + minutes = Math.floor((duration / (1000 * 60)) % 60), + hours = Math.floor((duration / (1000 * 60 * 60)) % 24), + days = Math.floor((duration / (1000 * 60 * 60 * 24))); + + days = (days < 10) ? "0" + days : days; + hours = (hours < 10) ? "0" + hours : hours; + minutes = (minutes < 10) ? "0" + minutes : minutes; + seconds = (seconds < 10) ? "0" + seconds : seconds; + + if (format === "hh:mm:ss") { + return `${hours}:${minutes}:${seconds}`; + } else if (format === "dd:hh:mm:ss") { + return `${days}:${hours}:${minutes}:${seconds}`; + } } \ No newline at end of file diff --git a/client/funcs/statisticsUpdate.js b/src/client/funcs/statisticsUpdate.js similarity index 98% rename from client/funcs/statisticsUpdate.js rename to src/client/funcs/statisticsUpdate.js index bd8301f..d036f3b 100644 --- a/client/funcs/statisticsUpdate.js +++ b/src/client/funcs/statisticsUpdate.js @@ -1,26 +1,26 @@ -module.exports = function statisticsUpdate(client, guild, radio) { - - client.datastore.checkEntry(guild.id); - - radio.currentGuild = client.datastore.getEntry(guild.id); - - if(!radio.currentGuild.statistics[radio.station.name]){ - radio.currentGuild.statistics[radio.station.name] = {}; - radio.currentGuild.statistics[radio.station.name].time = 0; - radio.currentGuild.statistics[radio.station.name].used = 0; - client.datastore.updateEntry(guild, radio.currentGuild); - } - - if(!radio.connection.dispatcher){ - let date = new Date(); - radio.currentTime = date.getTime(); - radio.playTime = parseInt(radio.currentTime)-parseInt(radio.startTime); - radio.currentGuild.statistics[radio.station.name].time = parseInt(radio.currentGuild.statistics[radio.station.name].time)+parseInt(radio.playTime); - } else { - radio.currentGuild.statistics[radio.station.name].time = parseInt(radio.currentGuild.statistics[radio.station.name].time)+parseInt(radio.connection.dispatcher.streamTime.toFixed(0)); - } - - radio.currentGuild.statistics[radio.station.name].used = parseInt(radio.currentGuild.statistics[radio.station.name].used)+1; - client.datastore.updateEntry(guild, radio.currentGuild); - client.datastore.calculateGlobal(client); +module.exports = function statisticsUpdate(client, guild, radio) { + + client.datastore.checkEntry(guild.id); + + radio.currentGuild = client.datastore.getEntry(guild.id); + + if(!radio.currentGuild.statistics[radio.station.name]){ + radio.currentGuild.statistics[radio.station.name] = {}; + radio.currentGuild.statistics[radio.station.name].time = 0; + radio.currentGuild.statistics[radio.station.name].used = 0; + client.datastore.updateEntry(guild, radio.currentGuild); + } + + if(!radio.connection.dispatcher){ + let date = new Date(); + radio.currentTime = date.getTime(); + radio.playTime = parseInt(radio.currentTime)-parseInt(radio.startTime); + radio.currentGuild.statistics[radio.station.name].time = parseInt(radio.currentGuild.statistics[radio.station.name].time)+parseInt(radio.playTime); + } else { + radio.currentGuild.statistics[radio.station.name].time = parseInt(radio.currentGuild.statistics[radio.station.name].time)+parseInt(radio.connection.dispatcher.streamTime.toFixed(0)); + } + + radio.currentGuild.statistics[radio.station.name].used = parseInt(radio.currentGuild.statistics[radio.station.name].used)+1; + client.datastore.updateEntry(guild, radio.currentGuild); + client.datastore.calculateGlobal(client); } \ No newline at end of file diff --git a/client/messages.js b/src/client/messages.js similarity index 98% rename from client/messages.js rename to src/client/messages.js index 375fc84..a779720 100644 --- a/client/messages.js +++ b/src/client/messages.js @@ -1,45 +1,45 @@ -module.exports = { - wrongVoiceChannel: "You need to be in the same voice channel as RadioX to use this command!", - noPerms: "You need the %command.permission% permission to use this command!", - notPlaying: "There is nothing playing!", - runningCommandFailed: "Running this command failed!", - noPermsEmbed: "I cannot send embeds (Embed links).", - bugTitle: "Found a bug with %client.user.username%?", - bugDescription: "Join the support server \n %client.config.supportGuild%", - helpTitle: "%client.user.username% help:", - helpDescription: "%commands% \n %client.config.prefix%help to see more information about a command.", - helpCommandTitle: "%client.config.prefix%%command.name% %command.usage%", - helpCommandDescription: "%command.description% \n Command Alias: %command.alias%", - inviteTitle: "Invite %client.user.username% to your Discord server!", - listTitle: "Radio Stations", - nowplayingTitle: "Now Playing", - nowplayingDescription: "**%radio.station.name%** \n Owner: %radio.station.owner% \n %client.funcs.msToTime(completed, \"hh:mm:ss\")%", - noVoiceChannel: "You need to be in a voice channel to play radio!", - noQuery: "You need to use a number or search for a supported station!", - noPermsConnect: "I cannot connect to your voice channel.", - noPermsSpeak: "I cannot speak in your voice channel.", - wrongStationNumber: "No such station!", - tooShortSearch: "Station must be over 2 characters!", - noSearchResults: "No stations found!", - errorPlaying: "An error has occured while playing radio!", - play: "Start playing: %radio.station.name%", - stop: "Stopped playback!", - currentVolume: "Current volume: **%radio.volume%**", - maxVolume: "The max volume is `100`!", - invalidVolume: "You need to enter a valid __number__.", - negativeVolume: "The volume needs to be a positive number!", - newVolume: "Volume is now: **%volume%**", - statisticsTitle: "Statistics", - maintenanceTitle: "Maintenance", - errorToGetPlaylist: "You can't use this bot because it has no playlist available. Check more information in our Discord support server %client.config.supportGuild% !", - notAllowed: "You are not allowed to do that!", - sendedMaintenanceMessage: "This bot is going to be under maintenance!", - footerText: "EximiaBots by Warén Group", - statusTitle: "%client.user.username% Status", - statusField1: "Bot Latency", - statusField2: "API Latency", - statusField3: "Uptime", - statusField4: "Version", - statusField5: "Hosted by", - errorStationURL: "Station can't be URL" +module.exports = { + wrongVoiceChannel: "You need to be in the same voice channel as RadioX to use this command!", + noPerms: "You need the %command.permission% permission to use this command!", + notPlaying: "There is nothing playing!", + runningCommandFailed: "Running this command failed!", + noPermsEmbed: "I cannot send embeds (Embed links).", + bugTitle: "Found a bug with %client.user.username%?", + bugDescription: "Join the support server \n %client.config.supportGuild%", + helpTitle: "%client.user.username% help:", + helpDescription: "%commands% \n %client.config.prefix%help to see more information about a command.", + helpCommandTitle: "%client.config.prefix%%command.name% %command.usage%", + helpCommandDescription: "%command.description% \n Command Alias: %command.alias%", + inviteTitle: "Invite %client.user.username% to your Discord server!", + listTitle: "Radio Stations", + nowplayingTitle: "Now Playing", + nowplayingDescription: "**%radio.station.name%** \n Owner: %radio.station.owner% \n %client.funcs.msToTime(completed, \"hh:mm:ss\")%", + noVoiceChannel: "You need to be in a voice channel to play radio!", + noQuery: "You need to use a number or search for a supported station!", + noPermsConnect: "I cannot connect to your voice channel.", + noPermsSpeak: "I cannot speak in your voice channel.", + wrongStationNumber: "No such station!", + tooShortSearch: "Station must be over 2 characters!", + noSearchResults: "No stations found!", + errorPlaying: "An error has occured while playing radio!", + play: "Start playing: %radio.station.name%", + stop: "Stopped playback!", + currentVolume: "Current volume: **%radio.volume%**", + maxVolume: "The max volume is `100`!", + invalidVolume: "You need to enter a valid __number__.", + negativeVolume: "The volume needs to be a positive number!", + newVolume: "Volume is now: **%volume%**", + statisticsTitle: "Statistics", + maintenanceTitle: "Maintenance", + errorToGetPlaylist: "You can't use this bot because it has no playlist available. Check more information in our Discord support server %client.config.supportGuild% !", + notAllowed: "You are not allowed to do that!", + sendedMaintenanceMessage: "This bot is going to be under maintenance!", + footerText: "EximiaBots by Warén Group", + statusTitle: "%client.user.username% Status", + statusField1: "Bot Latency", + statusField2: "API Latency", + statusField3: "Uptime", + statusField4: "Version", + statusField5: "Hosted by", + errorStationURL: "Station can't be URL" }; \ No newline at end of file diff --git a/src/client/utils/adapter.ts b/src/client/utils/adapter.ts new file mode 100644 index 0000000..d34463d --- /dev/null +++ b/src/client/utils/adapter.ts @@ -0,0 +1,70 @@ +import { DiscordGatewayAdapterCreator, DiscordGatewayAdapterLibraryMethods } from '@discordjs/voice'; +import { VoiceChannel, Snowflake, Client, Constants, WebSocketShard, Guild, StageChannel } from 'discord.js'; +import { GatewayVoiceServerUpdateDispatchData, GatewayVoiceStateUpdateDispatchData } from 'discord-api-types/v8'; + +const adapters = new Map(); +const trackedClients = new Set(); + +/** + * Tracks a Discord.js client, listening to VOICE_SERVER_UPDATE and VOICE_STATE_UPDATE events. + * @param client - The Discord.js Client to track + */ +function trackClient(client: Client) { + if (trackedClients.has(client)) return; + trackedClients.add(client); + client.ws.on(Constants.WSEvents.VOICE_SERVER_UPDATE, (payload: GatewayVoiceServerUpdateDispatchData) => { + adapters.get(payload.guild_id)?.onVoiceServerUpdate(payload); + }); + client.ws.on(Constants.WSEvents.VOICE_STATE_UPDATE, (payload: GatewayVoiceStateUpdateDispatchData) => { + if (payload.guild_id && payload.session_id && payload.user_id === client.user?.id) { + adapters.get(payload.guild_id)?.onVoiceStateUpdate(payload); + } + }); +} + +const trackedGuilds = new Map>(); + +function cleanupGuilds(shard: WebSocketShard) { + const guilds = trackedGuilds.get(shard); + if (guilds) { + for (const guildID of guilds.values()) { + adapters.get(guildID)?.destroy(); + } + } +} + +function trackGuild(guild: Guild) { + let guilds = trackedGuilds.get(guild.shard); + if (!guilds) { + const cleanup = () => cleanupGuilds(guild.shard); + guild.shard.on('close', cleanup); + guild.shard.on('destroyed', cleanup); + guilds = new Set(); + trackedGuilds.set(guild.shard, guilds); + } + guilds.add(guild.id); +} + +/** + * Creates an adapter for a Voice Channel + * @param channel - The channel to create the adapter for + */ +export function createDiscordJSAdapter(channel: VoiceChannel | StageChannel): DiscordGatewayAdapterCreator { + return (methods) => { + adapters.set(channel.guild.id, methods); + trackClient(channel.client); + trackGuild(channel.guild); + return { + sendPayload(data) { + if (channel.guild.shard.status === Constants.Status.READY) { + channel.guild.shard.send(data); + return true; + } + return false; + }, + destroy() { + return adapters.delete(channel.guild.id); + }, + }; + }; +} diff --git a/src/client/utils/typings.ts b/src/client/utils/typings.ts new file mode 100644 index 0000000..79cc341 --- /dev/null +++ b/src/client/utils/typings.ts @@ -0,0 +1,3 @@ +export interface command { } + +export interface radio {} \ No newline at end of file diff --git a/config.js b/src/config.js similarity index 100% rename from config.js rename to src/config.js diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..ef051ed --- /dev/null +++ b/src/index.js @@ -0,0 +1,3 @@ +const { default: RadioClient } = require("./Client"); + +const client = new RadioClient();