Vesion 0.1.0 - Initial Version with First Commands

This commit is contained in:
CICD - Pipeline 2023-08-20 17:00:50 +02:00
parent 8739acb402
commit f80e23827a
22 changed files with 4114 additions and 9 deletions

View File

@ -1,9 +0,0 @@
MIT License
Copyright (c) <year> <copyright holders>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

5
data/config.json Normal file
View File

@ -0,0 +1,5 @@
{
"token": "MTA5NDcyNzc4OTY4MjM4MDkyMg.GXRu96.tkaQ8XqbDgk4blhGk4sme6JGwQkgf9hQ92W15s",
"guild_id": "137291455336022018",
"voice_channel_id": "1094727789682380922"
}

8
data/nodes.json Normal file
View File

@ -0,0 +1,8 @@
[
{
"host": "lavalink.devamop.in",
"password": "DevamOP",
"port": 443,
"secure": true
}
]

2721
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

28
package.json Normal file
View File

@ -0,0 +1,28 @@
{
"name": "subsonics-web",
"author": "Raphix",
"version": "0.1.0",
"nodemonConfig": {
"ext": "js, html",
"ignore": [
"*.json",
"*.html",
"./src/modules/sub-log.js"
],
"delay": "2000000"
},
"dependencies": {
"cookie-parser": "^1.4.6",
"discord.js": "^14.9.0",
"erela.js": "^2.4.0",
"express": "^4.18.2",
"nodemon": "^2.0.22",
"socket.io": "^4.6.1",
"uuid": "^9.0.0",
"ytfps": "^1.1.0"
},
"scripts": {
"start": "nodemon src/main.js",
"dev": "set DEV=true& nodemon src/main.js"
}
}

View File

@ -0,0 +1,20 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
const { __glob } = require("../modules/global-variables");
const { LogType } = require("../modules/sub-log");
const { List } = require("../modules/sub-list");
const subplayer = require(__glob.SUBPLAYER);
const packageJson = require(__glob.PACKAGE);
module.exports = {
data: new SlashCommandBuilder()
.setName("web")
.setDescription("[NEW] Donne le lien vers le panel !"),
async execute(client, interaction) {
}
}

29
src/commands/about.js Normal file
View File

@ -0,0 +1,29 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
const { __glob } = require("../modules/global-variables");
const packageJson = require(__glob.PACKAGE);
module.exports = {
data: new SlashCommandBuilder()
.setName("about")
.setDescription("[NEW] Affiche les informations principales du bot !"),
async execute(client, interaction) {
const uptime = process.uptime();
const hours = Math.floor(uptime / 3600);
const minutes = Math.floor((uptime % 3600) / 60);
const seconds = Math.floor(uptime % 60);
const embed = new EmbedBuilder()
.setColor(0xb0f542)
.setThumbnail("https://cdn.discordapp.com/avatars/" + client.user.id + "/" + client.user.avatar + ".png")
.setTitle('Subsonics - Web')
.addFields({name: "Version ", value: packageJson.version},{name:"Uptime", value: hours + " heure(s), " + minutes + " minute(s) et " + seconds + " seconde(s)"},{name: "Ping", value: client.ws.ping + " ms"},{name: "Un bot fait par un fan pour les fans !", value: ":heart:"})
.setTimestamp();
interaction.reply({embeds: [embed]})
}
}

33
src/commands/help.js Normal file
View File

@ -0,0 +1,33 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
module.exports = {
data:new SlashCommandBuilder()
.setName("help")
.setDescription("[NEW] Affiche toutes les commandes disponibles du bot"),
async execute(client, interaction) {
const embed = new EmbedBuilder()
.setColor(0x03ff2d)
.setTitle('Comment assister au concert ?')
.setDescription("**Eh ! Tu as eu ton ticket ? Tant mieux ! Voici la liste des commandes à utiliser dans le salon <#664355637685256203>**")
.addFields({name: "/play <nom/playlist>", value: "Cette commande te permet de lire des titres comme des playlists depuis Youtube / SoundCloud / Vimeo, n'importe quelle musique !"},
{name: "/leave", value: "Si tu ne veux plus du meilleur groupe du monde (faire partir le bot), cette commande le fera partir aussi vite qu'il est arrivé !"},
{name: "/pause", value: "Besoin d'un entracte ? Cette commande te permettera de mettre en pause ou de remettre le morceau en cours !"},
{name: "/state", value: "Donne le titre de la musique"},
{name: "/skip", value: "Passer à la chanson suivante."},
{name: "/previous", value: "Revenir à la chanson précédente."},
{name: "/about", value: "Affiche les informations principales !"},
{name: "/web", value: "Donne le lien vers le panel !"})
.setTimestamp()
.addFields({name: "La queue et la gestion du redémarrage se fait par le site https://subsonics.raphix.fr/", value: ":star:"})
.setThumbnail("https://static.wikia.nocookie.net/codelyoko/images/9/95/Subdigitals.jpg/revision/latest/scale-to-width-down/180?cb=20120105180510&path-prefix=fr");
interaction.reply({embeds: [embed]})
}
}

24
src/commands/leave.js Normal file
View File

@ -0,0 +1,24 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
const { __glob } = require("../modules/global-variables");
const subplayer = require(__glob.SUBPLAYER);
module.exports = {
data: new SlashCommandBuilder()
.setName("leave")
.setDescription("[NEW] Déconnecte le Bot !"),
async execute(client, interaction) {
// CHECK MEMBER IF IN VOICE CHANNEL
if(!interaction.member.voice.channel) return interaction.reply({content:"Vous devez rejoindre un salon vocal pour contrôler le Bot !", ephemeral: true})
subplayer.leave(client, interaction)
}
}

22
src/commands/pause.js Normal file
View File

@ -0,0 +1,22 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
const { __glob } = require("../modules/global-variables");
const { LogType } = require("../modules/sub-log");
const { List } = require("../modules/sub-list");
const subplayer = require(__glob.SUBPLAYER);
const packageJson = require(__glob.PACKAGE);
module.exports = {
data: new SlashCommandBuilder()
.setName("pause")
.setDescription("[NEW] Cette commande te permettera de mettre en pause ou de remettre le morceau en cours !"),
async execute(client, interaction) {
if(!interaction.member.voice.channel) return interaction.reply({content:"Vous devez rejoindre un salon vocal pour contrôler le Bot !", ephemeral: true})
subplayer.pause(client, interaction)
}
}

27
src/commands/play.js Normal file
View File

@ -0,0 +1,27 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
const { __glob } = require("../modules/global-variables");
const subplayer = require(__glob.SUBPLAYER);
module.exports = {
data: new SlashCommandBuilder()
.setName("play")
.setDescription("[NEW] Permet de lire des titres comme des playlists depuis Youtube / SoundCloud / Vimeo !")
.addStringOption(option => option.setName("nom_ou_lien").setDescription("Lien ou nom du titre ou de la playlist").setRequired(true)),
async execute(client, interaction) {
// CHECK MEMBER IF IN VOICE CHANNEL
if(!interaction.member.voice.channel) return interaction.reply({content:"Vous devez rejoindre un salon vocal pour lire un(e) titre / playlist !", ephemeral: true})
//CHECK OF PLAYER
subplayer.play(client, interaction)
}
}

19
src/commands/previous.js Normal file
View File

@ -0,0 +1,19 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
const { __glob } = require("../modules/global-variables");
const { LogType } = require("../modules/sub-log");
const { List } = require("../modules/sub-list");
const subplayer = require(__glob.SUBPLAYER);
const packageJson = require(__glob.PACKAGE);
module.exports = {
data: new SlashCommandBuilder()
.setName("previous")
.setDescription("[NEW] Revenir à la chanson précédente."),
async execute(client, interaction) {
subplayer.previous(client, interaction)
}
}

20
src/commands/skip.js Normal file
View File

@ -0,0 +1,20 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
const { __glob } = require("../modules/global-variables");
const { LogType } = require("../modules/sub-log");
const { List } = require("../modules/sub-list");
const subplayer = require(__glob.SUBPLAYER);
const packageJson = require(__glob.PACKAGE);
module.exports = {
data: new SlashCommandBuilder()
.setName("skip")
.setDescription("[NEW] Passer à la chanson suivante."),
async execute(client, interaction) {
subplayer.skip(client, interaction)
}
}

19
src/commands/state.js Normal file
View File

@ -0,0 +1,19 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
const { __glob } = require("../modules/global-variables");
const { LogType } = require("../modules/sub-log");
const { List } = require("../modules/sub-list");
const subplayer = require(__glob.SUBPLAYER);
const packageJson = require(__glob.PACKAGE);
module.exports = {
data: new SlashCommandBuilder()
.setName("state")
.setDescription("[NEW] Donne le titre de la musique !"),
async execute(client, interaction) {
subplayer.getState(client, interaction)
}
}

24
src/commands/web.js Normal file
View File

@ -0,0 +1,24 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
const { __glob } = require("../modules/global-variables");
const packageJson = require(__glob.PACKAGE);
module.exports = {
data: new SlashCommandBuilder()
.setName("web")
.setDescription("[NEW] Donne le lien vers le panel !"),
async execute(client, interaction) {
const embed = new EmbedBuilder()
.setColor(0xffffff)
.setThumbnail("https://cdn.discordapp.com/avatars/" + client.user.id + "/" + client.user.avatar + ".png")
.setTitle('Subsonics - Web')
.addFields({name: "Lien", value:"https://subsonics.raphix.fr"})
.setTimestamp();
interaction.reply({embeds: [embed]})
}
}

66
src/main.js Normal file
View File

@ -0,0 +1,66 @@
const fs = require("node:fs")
const path = require("path")
const { LogType } = require("./modules/sub-log")
const { DiscordBot } = require("./modules/discord-bot")
const { __glob } = require("./modules/global-variables")
setup()
function setup() {
//Log - INIT PHASE
const dlog = new LogType("Discord")
const wlog = new LogType("Web")
const alog = new LogType("Authentification")
// Discord Bot - INIT PHASE
const bot = new DiscordBot(getConfig(dlog), dlog)
}
// Config GETTER
function getConfig(dlog) {
dlog.step.init("getConfig", "Récupération du fichier de configuration")
if(fs.existsSync(__glob.CONFIG)) {
try {
var config_data = JSON.parse(fs.readFileSync(__glob.CONFIG))
dlog.log("Fichier de configuration trouvé : TOKEN : " + config_data.token)
dlog.step.end("getConfig")
return config_data
} catch(error) {
dlog.step.error("getConfig", error)
}
} else {
dlog.warn("Fichier de configuration introuvable !")
try {
var new_config = {
"token":"",
"guild_id":"",
"voice_channel_id":""
}
fs.writeFileSync(__glob.CONFIG, JSON.stringify(new_config, null, 2))
dlog.log("Création d'un fichier de configuration ! Redémarrage de l'application nécéssaire !")
dlog.step.error("getConfig", "Redémarrage requis pour lire la nouvelle configuration !")
process.exit(0)
} catch(error) {
dlog.step.error("getConfig", "Tentative de création du fichier de configuration échoué !" + error)
}
}
}

241
src/modules/discord-bot.js Normal file
View File

@ -0,0 +1,241 @@
const { Client, GatewayIntentBits, Collection, ActivityType, REST, Routes } = require("discord.js")
const fs = require("node:fs")
const path = require("path")
const { Manager } = require("erela.js")
const { __glob } = require("./global-variables")
const { LogType } = require("../modules/sub-log")
const { List } = require("./sub-list")
const nodeFinder = require("./nodes-finder")
const { platform } = require("node:os")
const client = new Client({
intents:[GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildMembers],
})
const membersVoices = new Map()
module.exports.DiscordBot = class {
constructor(config, dlog) {
dlog.step.init("d_init", "Démarrage du Bot Discord")
init(dlog, config)
}
}
function init(dlog, config) {
client.commands = new Collection()
client.dictator = false;
dlog.step.init("d_get_commands", "Récupération des commandes en local depuis : " + __glob.COMMANDS)
const commands = [];
// Grab all the command files from the commands directory you created earlier
const commandsPath = __glob.COMMANDS
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
for (const file of commandFiles) {
const command = require(commandsPath + path.sep + file);
client.commands.set(command.data.name, command)
commands.push(command.data.toJSON());
}
dlog.step.end("d_get_commands")
const rest = new REST().setToken(config.token);
(async () => {
try {
dlog.step.init("d_commands_refresh", `Refreshing ${commands.length} application (/) commands.`);
// The put method is used to fully refresh all commands in the guild with the current set
const data = await rest.put(
Routes.applicationGuildCommands("1094727789682380922", "137291455336022018"),
{ body: commands },
);
dlog.log("COMMANDS : Sended to Discord [" + data.length + "]")
dlog.step.end("d_commands_refresh")
} catch (error) {
// And of course, make sure you catch and log any errors!
dlog.error(error)
}
})();
rest.on("rateLimited", (datawarn) => {
dlog.warn("REST - Limite de requête atteinte ! TimeToReset : " + datawarn.timeToReset);
})
client.once("ready", () => {
dlog.log("Connexion au Bot Discord réussi ! Connecté à : " + client.user.username + "#" + client.user.discriminator)
client.user.setPresence({
activities: [{ name: `toutes les musiques possible !`, type: ActivityType.Listening }],
status: 'online',
});
client.manager.init(client.user.id);
const commandManager = client.application.commands;
if (!commandManager) {
dlog.error('Command manager not available.');
} else {
commandManager.set([]);
}
dlog.step.end("d_init")
})
client.on("interactionCreate", (interaction) => {
if(!interaction.isCommand()) return;
const command = client.commands.get(interaction.commandName)
try {
dlog.log(interaction.member.user.username + "-> /" + interaction.commandName)
command.execute(client, interaction)
} catch(error) {
dlog.error(interaction.member.user.username + "-> /" + interaction.commandName + " : ERREUR RENCONTRE")
dlog.error(error)
interaction.reply({content:"Erreur lors de l'éxécution de la commande !", ephemeral: true})
}
})
startErelaManager(dlog, config)
client.login(config.token)
}
function startErelaManager(dlog, config) {
const elog = new LogType("Lavalink-Manager")
client.on("voiceStateUpdate", (oldMember, newMember) => {
membersVoices.set(newMember.id, newMember.channelId)
let player = client.manager.players.get(oldMember.guild.id)
if(player) {
client.channels.fetch(player.options.voiceChannel).then(channel => {
if(channel.members.size <= 1) {
player.destroy()
}
})
}
})
const nodes = nodeFinder.getNodes()
client.manager = new Manager({
// The nodes to connect to, optional if using default lavalink options
nodes,
// Method to send voice data to Discord
send: (id, payload) => {
const guild = client.guilds.cache.get(id);
// NOTE: FOR ERIS YOU NEED JSON.stringify() THE PAYLOAD
if (guild) guild.shard.send(payload);
}
});
const plog = new LogType("Lavalink-Player")
client.on("voiceStateUpdate", (oldMember, newMember) => {
membersVoices.set(newMember.id, newMember.channelId)
let player = client.manager.players.get(oldMember.guild.id)
if(player) {
client.channels.fetch(player.options.voiceChannel).then(channel => {
if(channel.members.size <= 1) {
player.destroy()
plog.log("[Automatic Task] Player supprimé dans : " + channel.name)
}
})
}
})
const list = new List()
client.manager.on("playerCreate", (player) => {
client.channels.fetch(player.options.voiceChannel).then(channel => {
plog.log("Nouveau Player instancié dans : " + channel.name)
})
})
client.manager.on("playerDestroy", (player) => {
list.destroy()
client.channels.fetch(player.options.voiceChannel).then(channel => {
plog.log("Player supprimé dans : " + channel.name)
})
})
client.manager.on("trackStart", (song) => {
plog.log("Lecture de '" + song.queue.current.title + "' de '" + song.queue.current.author + "'")
list.setCurrent(song)
})
client.manager.on("queueEnd", () => {
let player = client.manager.players.get("137291455336022018")
if(player) {
list.passCurrent()
if(list.haveSongs()) {
player.play(list.next())
}
}
})
// Emitted whenever a node connects
client.manager.on("nodeConnect", node => {
elog.log(`Connecté au serveur Lavalink : "${node.options.identifier}"` )
})
// Emitted whenever a node encountered an error
client.manager.on("nodeError", (node, error) => {
elog.warn(`Node "${node.options.identifier}" encountered an error: ${error.message}.`)
})
// THIS IS REQUIRED. Send raw events to Erela.js
client.on("raw", d => client.manager.updateVoiceState(d));
}

View File

@ -0,0 +1,19 @@
const path = require("path")
const root = path.resolve(__dirname, '../../')
const __glob = {
CONFIG: root + path.sep + "data" + path.sep + "config.json",
ROOT: root,
WEB: root + path.sep + "src" + path.sep + "web",
COMMANDS: root + path.sep + "src" + path.sep + "commands",
SUBLOG: root + path.sep + "src" + path.sep + "modules" + path.sep + "sub-log.js",
SUBPLAYER: root + path.sep + "src" + path.sep + "modules" + path.sep + "sub-player.js",
SUBLIST: root + path.sep + "src" + path.sep + "modules" + path.sep + "sub-list.js",
PACKAGE: root + path.sep + "package.json",
DATA: root + path.sep + "data" + path.sep,
NODES: root + path.sep + "data" + path.sep + "nodes.json",
};
module.exports = { __glob };

View File

@ -0,0 +1,85 @@
const { __glob } = require("./global-variables")
const { LogType } = require("../modules/sub-log")
const fs = require("fs")
const nlog = new LogType("Node-Finder")
module.exports.getNodes = function () {
nlog.step.init("find_nodes", "Récupération des nodes de la base de donnée")
if(fs.existsSync(__glob.NODES)) {
try {
var nodes_data = JSON.parse(fs.readFileSync(__glob.NODES))
const nodes_array = new Array()
for(node of nodes_data) {
nodes_array.push(node)
}
nlog.log("Récupération de " + nodes_array.length + " nodes dans la base de donnée !")
nlog.step.end("find_nodes")
return nodes_array
} catch(error) {
nlog.step.error("find_nodes", error)
}
} else {
nlog.warn("Fichier de configuration introuvable !")
try {
var nodes = {}
fs.writeFileSync(__glob.NODES, JSON.stringify(nodes, null, 2))
nlog.log("Création d'un fichier de base de donnée de nodes ! Redémarrage de l'application nécéssaire !")
nlog.step.error("find_nodes", "Redémarrage requis pour lire la nouvelle base de donnée des nodes !")
process.exit(0)
} catch(error) {
nlog.step.error("find_nodes", "Tentative de création du fichier de base de donnée nodes échoué !" + error)
}
}
}
module.exports.addNodes = function (data) {
nlog.step.init("add_nodes", "Ajout d'un nouveau noeud dans la base de donnée de nodes : " + data.host)
if(fs.existsSync(__glob.NODES)) {
try {
var nodes_data = JSON.parse(fs.readFileSync(__glob.NODES))
nodes_data.push(data)
fs.writeFileSync(__glob.NODES, JSON.stringify(nodes_data, null, 2))
nlog.step.end("add_nodes")
} catch(error) {
nlog.step.error("add_nodes", error)
}
} else {
nlog.warn("Fichier de configuration introuvable !")
try {
var nodes = []
fs.writeFileSync(__glob.NODES, JSON.stringify(nodes, null, 2))
nlog.log("Création d'un fichier de base de donnée de nodes ! Redémarrage de l'application nécéssaire !")
nlog.step.error("add_nodes", "Redémarrage requis pour lire la nouvelle base de donnée des nodes !")
process.exit(0)
} catch(error) {
nlog.step.error("add_nodes", "Tentative de création du fichier de nodes échoué !" + error)
}
}
}

182
src/modules/sub-list.js Normal file
View File

@ -0,0 +1,182 @@
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
const { __glob } = require("../modules/global-variables");
const { LogType } = require('./sub-log');
const dlog = new LogType("Queue-List")
var next = new Array()
var previous = new Array()
var current = null;
module.exports.List = class {
constructor() {
}
destroy() {
next = new Array()
previous = new Array()
current = null
}
next() {
const song = next[0]
next.splice(0, 1)
return song
}
previous() {
const song = previous[0]
previous.splice(0, 1)
return song
}
setCurrent(song) {
current = song.queue.current
}
passCurrent() {
previous.unshift(current)
}
haveSongs() {
if(next.length == 0) {
return false
} else{
return true
}
}
havePreviousSongs() {
if(previous.length == 0) {
return false
} else{
return true
}
}
getNextSong() {
const song = next[0]
return song
}
async add(song, interaction) {
dlog.log("Ajout d'un titre dans la liste de lecture : '" + song.title + "' de '" + song.author + "'")
next.push(song)
if(interaction) {
const embed = await new EmbedBuilder()
.setColor(0x15e6ed)
.setTitle('**Ajout dans la liste de lecture **: ' + song.title)
.setDescription('**Demandé par **' + interaction.member.user.username)
.addFields({name: "Auteur", value: song.author},
{name: "URL", value: song.uri})
.setThumbnail(song.thumbnail)
.setTimestamp();
interaction.reply({embeds: [embed]})
}
}
remove(song) {
dlog.log("Supression d'un titre dans la liste de lecture : '" + song.title + "' de '" + song.author + "'")
const index = next.indexOf(song)
next.splice(index, 1)
}
__previous_add(song) {
previous.unshift(song)
}
__previous_remove(song) {
const index = next.indexOf(song)
previous.splice(index, 1)
}
__next_add(song) {
next.unshift(song)
}
async playlistAdd(playlist, interaction) {
if(interaction) {
const client = interaction.client
var author = "Artiste inconnu !"
if(typeof playlist.author != "undefined" ) {
author == playlist.author.name
}
const embed = await new EmbedBuilder()
.setColor(0x15e6ed)
.setTitle('**Lecture de la playlist : **' + playlist.title)
.setDescription('**Demandé par **' + interaction.member.user.username)
.addFields({name: "Auteur", value: author},
{name: "URL", value:playlist.url},
{name: "Nombre de videos", value:playlist.video_count + " vidéos"})
.setThumbnail(playlist.thumbnail_url)
.setTimestamp();
interaction.reply({embeds: [embed]})
let player = client.manager.players.get(interaction.guild.id)
for(var song of playlist.videos) {
const song_finded = await client.manager.search(song.url)
next.push(song_finded.tracks[0])
}
if(!player.playing) {
player.play(next[0])
this.remove(next[0])
}
} else {
// [A FINIR POUR WEB]
}
}
}

217
src/modules/sub-log.js Normal file
View File

@ -0,0 +1,217 @@
const fs = require("fs")
const path = require("path")
var logStream = null
var logInstance = new Map()
setup()
function getDate(formated) {
var date = new Date()
// [Date Format] - Format de la date
var gmonth = date.getMonth()
var gday = date.getDate()
var gHour = date.getHours()
var gMinute = date.getMinutes()
var gSecondes = date.getSeconds()
if(date.getMonth() + 1 <= 10) {
gmonth = "0" + (date.getMonth() + 1)
}
if(date.getDate() + 1 <= 10) {
gday = "0" + date.getDate()
}
if(date.getHours() + 1 <= 10) {
gHour = "0" + date.getHours()
}
if(date.getMinutes() + 1 <= 10) {
gMinute = "0" + date.getMinutes()
}
if(date.getSeconds() + 1 <= 10) {
gSecondes = "0" + date.getSeconds()
}
if(!formated) {
return gday + "/" + gmonth + " - " + gHour + "h" + "-" + gMinute + "m" + "-" + gSecondes + "s"
} else {
return date.getFullYear() + "-" + gmonth + "-" + gday + "-" + gHour + "h" + "-" + gMinute + "m" + "-" + gSecondes + "s"
}
}
function setup() {
if(!fs.existsSync(__dirname + path.sep + "logs" + path.sep)) {
fs.mkdir(__dirname + path.sep + "logs", (err) => {
if(!err) {
console.log("[Logs] - Dossier de logs crée ! !")
} else {
console.log("[Logs] -Erreur d'écriture par manque de permission ")
console.log(err)
}
})
}
logStream = fs.createWriteStream(__dirname + path.sep + "logs" + path.sep + getDate(true) + ".log", {
flags: 'a'
});
logStream.write("[" + require("../../package.json").name + "@"+ require("../../package.json").version + "] - [" + getDate(true) + "]" + "\n")
logStream.write("Subsonics-Web by Raphix" + "\n")
logStream.write("----------------------------------------------------------------" + "\n")
process.on('uncaughtException', (err) => {
console.error("["+ "FATAL" + "] - The application has encountered an error ! Please Restart ! #E = " + err + "\n")
console.error(err)
logStream.write("[" + getDate() + "] - ["+ "FATAL" + "] - The application has encountered an error ! Please Restart ! #E = " + err + "\n")
logStream.end( "["+ "UNCAUGHT_EXCEPTION" + "]" + " - [END OF LOGS] - [" + getDate() + ']')
logStream.close()
});
process.on('beforeExit', (err) => {
logStream.end( "["+ "BEFORE_EXIT" + "]" + " - [END OF LOGS] - [" + getDate(true) + ']')
logStream.close()
});
}
module.exports.getInstance = function (name) {
if(logInstance.has(name)) {
return logInstance.get(name)
} else {
var logtext = "[Logs] - [ERROR] - '" + name + "' n'est pas enregistré en tant qu'instance de log !"
logStream.write("[" + getDate() + "] - " + logtext + "\n")
console.log(logtext)
return null
}
}
module.exports.LogType = class {
constructor(typeName) {
this.type = typeName;
this.steps = new Map()
this.step = this.initializeStep()
logInstance.set(typeName, this)
}
log(txt) {
var logtext = "[" + this.type + "] - [INFO] - " + txt
logStream.write("[" + getDate() + "] - " + logtext + "\n")
console.log(logtext)
}
warn(txt) {
var logtext = "[" + this.type + "] - [WARN] - " + txt
logStream.write("[" + getDate() + "] - " + logtext + "\n")
console.log(logtext)
}
error(txt) {
var logtext = "[" + this.type + "] - [ERROR] - " + txt
logStream.write("[" + getDate() + "] - " + logtext + "\n")
console.log(logtext)
}
initializeStep() {
const parent = this;
return {
init: function(id, desc) {
parent.steps.set(id, desc)
var logtext = "[" + parent.type + "] - [INFO] - [STEP] - " + desc + " - En cours ..."
logStream.write("[" + getDate() + "] - " + logtext + "\n")
console.log(logtext)
},
end: function(id) {
if(parent.steps.has(id)) {
var logtext = "[" + parent.type + "] - [INFO] - [STEP] - " + parent.steps.get(id) + " - Terminé !"
logStream.write("[" + getDate() + "] - " + logtext + "\n")
console.log(logtext)
parent.steps.delete(id)
} else {
var logtext = "[" + parent.type + "] - [WARN] - [STEP] - '" + id + "' n'est pas enregistré en tant qu'étape !"
logStream.write("[" + getDate() + "] - " + logtext + "\n")
console.log(logtext)
}
},
error: function(id, errorDesc) {
if(parent.steps.has(id)) {
var logtext = "[" + parent.type + "] - [ERROR] - [STEP] - " + parent.steps.get(id) + " - Une erreur a été rencontré dans l'étape ! : #E = " + errorDesc
logStream.write("[" + getDate() + "] - " + logtext + "\n")
console.log(logtext)
parent.steps.delete(id)
} else {
var logtext = "[" + parent.type + "] - [WARN] - [STEP] - '" + id + "' n'est pas enregistré en tant qu'étape !"
logStream.write("[" + getDate() + "] - " + logtext + "\n")
console.log(logtext)
}
}
}
}
}

305
src/modules/sub-player.js Normal file
View File

@ -0,0 +1,305 @@
const { SlashCommandBuilder, EmbedBuilder, DefaultWebSocketManagerOptions } = require("discord.js");
const { __glob } = require("../modules/global-variables");
const { LogType } = require("./sub-log");
const { List } = require("./sub-list")
var ytfps = require("ytfps")
const list = new List()
module.exports.play = async function (client, interaction) {
if(interaction) {
let player = client.manager.players.get(interaction.guild.id)
const song_name = interaction.options.getString("nom_ou_lien")
if(!player) {
player = client.manager.create({
guild: interaction.guild.id,
voiceChannel: interaction.member.voice.channel.id,
textChannel: interaction.channel.id,
});
player.connect();
}
// CHECK OF PLAYLIST
var playlist = await checkPlaylist(song_name)
if(playlist) {
list.playlistAdd(playlist, interaction)
} else {
const songs = await client.manager.search(song_name)
if(!player.playing) {
player.play(songs.tracks[0])
const embed = await new EmbedBuilder()
.setColor(0x15e6ed)
.setTitle('**Lecture de : **' + songs.tracks[0].title)
.setDescription('**Demandé par **' + interaction.member.user.username)
.addFields({name: "Auteur", value: songs.tracks[0].author},
{name: "URL", value:songs.tracks[0].uri})
.setThumbnail(songs.tracks[0].thumbnail)
.setTimestamp();
interaction.reply({embeds: [embed]})
} else {
list.add(songs.tracks[0], interaction)
}
}
} else {
// [A FINIR POUR WEB]
}
}
module.exports.pause = function (client, interaction) {
if(interaction) {
let player = client.manager.players.get(interaction.guild.id)
if(player) {
if(player.playing) {
const embed = new EmbedBuilder()
.setColor(0x03ff2d)
.setTitle('Pause !')
.setDescription("**Ok, une entracte est demandée par " + interaction.member.user.username + "**")
.setTimestamp();
interaction.reply({embeds: [embed]})
player.pause(true)
} else {
const embed = new EmbedBuilder()
.setColor(0x03ff2d)
.setTitle('C\'est reparti !')
.setDescription("**Ok, Fin de l'entracte, c'est reparti et c'est demandée par " + interaction.member.user.username + "**")
.setTimestamp();
interaction.reply({embeds: [embed]})
player.pause(false)
}
} else {
interaction.reply("**Aucune musique n'est actuellement jouée !**")
}
} else {
// [A FINIR POUR WEB]
}
}
module.exports.getState = function(client, interaction) {
if(interaction) {
let player = client.manager.players.get(interaction.guild.id)
if(player) {
let embed = new EmbedBuilder()
.setColor(0x32a875)
.setTitle('Information sur la musique')
.addFields({name:"Titre", value: player.queue.current.title},
{name:"Auteur", value: player.queue.current.author},
{name:"URL", value: player.queue.current.uri})
.setTimestamp()
.setThumbnail(player.queue.current.thumbnail);
interaction.reply({embeds: [embed]})
} else {
interaction.reply("**Aucune musique n'est actuellement jouée !**")
}
} else {
// [A FINIR POUR WEB]
}
}
module.exports.skip = function (client, interaction) {
if(interaction) {
let player = client.manager.players.get(interaction.guild.id)
if(player) {
if(!list.haveSongs()) {
var embed = new EmbedBuilder()
.setColor(0xff0303)
.setTitle('Erreur : Skip')
.setTimestamp();
const song_show = {name: "Aucune chanson n'est dans la queue", value: "Changement impossible !"}
embed.addFields(song_show)
interaction.reply({embeds: [embed]})
} else {
var embed = new EmbedBuilder()
.setColor(0x03ff2d)
.setTitle('On change de morceau !!!')
.setDescription("**Ok, On est reparti avec "+ list.getNextSong().title + " et c'est demandée par " + interaction.member.user.username + "**")
.setThumbnail(list.getNextSong().thumbnail)
.setTimestamp();
player.stop()
interaction.reply({embeds: [embed]})
}
} else {
interaction.reply("**Aucune musique n'est actuellement jouée !**")
}
} else {
// [A FINIR POUR WEB]
}
}
module.exports.previous = function (client, interaction) {
if(interaction) {
let player = client.manager.players.get(interaction.guild.id)
if(player) {
if(!list.havePreviousSongs()){
embed = new EmbedBuilder()
.setColor(0xff0303)
.setTitle('Erreur : Back')
.setTimestamp();
const song_show = {name: "Aucune chanson n'a été joué précédemment !", value: "Changement impossible !"}
embed.addFields(song_show)
interaction.reply({embeds: [embed]})
} else {
list.__next_add(player.queue.current)
player.play(list.previous())
embed = new EmbedBuilder()
.setColor(0x03ff2d)
.setTitle('Retour vers le passé !!!')
.setDescription("**Ok, On est reparti avec "+ player.queue.current.title +" et c'est demandée par " + interaction.member.user.username + "**")
.setTimestamp();
interaction.reply({embeds: [embed]})
}
}
}
}
// FINI
module.exports.leave = function (client, interaction) {
var player = null
if(interaction) {
player = client.manager.players.get(interaction.guild.id)
}
if(player) {
player.destroy()
if(interaction) {
const embed = new EmbedBuilder()
.setColor(0xff0000)
.setTitle('C\'est tout pour nous !')
.setDescription("**Le meilleur groupe du monde est parti ... !**")
.setTimestamp();
interaction.reply({embeds: [embed]})
}
} else {
if(interaction) {
interaction.reply("**Aucune musique n'est actuellement jouée !**")
}
}
}
// FINI
async function checkPlaylist (song_name) {
try {
return await ytfps(song_name)
} catch(err) {
return false
}
}