Compare commits

..

24 Commits

Author SHA1 Message Date
96cd60912b Version 1.2.0 - Ajout des suggestions et de paramètres
All checks were successful
Deployment Pipeline / deploy (push) Successful in 35s
2025-09-07 18:18:52 +02:00
b2aadc7c3c Version 1.1.4 - Correction de Bug
All checks were successful
Deployment Pipeline / deploy (push) Successful in 36s
2025-09-06 15:47:06 +02:00
dcc056455e Version 1.1.3 - Modfication du Proxy
All checks were successful
Deployment Pipeline / deploy (push) Successful in 36s
2025-08-31 16:57:13 +02:00
f777fb821a Version 1.1.2-rc4 - Modif erreur
All checks were successful
Deployment Pipeline / deploy (push) Successful in 37s
2025-08-29 19:44:38 +02:00
ecbda838d3 Merge branch 'main' of https://git.raphix.fr/subsonics/chopin
All checks were successful
Deployment Pipeline / deploy (push) Successful in 35s
2025-08-29 19:41:32 +02:00
5ac195fd46 Version 1.1.2-rc4 - Ajout de agent Cookie 2025-08-29 19:41:08 +02:00
914edbbf13 Version 1.1.2-rc3 - Modification Cookie
All checks were successful
Deployment Pipeline / deploy (push) Successful in 40s
2025-08-29 15:25:22 +02:00
c376e3259c Version 1.1.2-rc2 - Ajout Cookies
All checks were successful
Deployment Pipeline / deploy (push) Successful in 35s
2025-08-29 12:44:25 +02:00
f3b237b74f Version 1.1.2-rc1 - Modif Ytdl
All checks were successful
Deployment Pipeline / deploy (push) Successful in 34s
2025-08-29 12:29:02 +02:00
83e11f3341 Merge branch 'main' of https://git.raphix.fr/subsonics/chopin
All checks were successful
Deployment Pipeline / deploy (push) Successful in 35s
2025-08-29 12:26:32 +02:00
b132041d16 Version 1.1.2 - Modification User-Agent 2025-08-29 12:26:25 +02:00
33da8e8527 Version 1.1.1 - Edit Cookies
All checks were successful
Deployment Pipeline / deploy (push) Successful in 36s
2025-08-29 12:01:13 +02:00
c613d67c60 Version 1.1.1 - Modification du Changelog
All checks were successful
Deployment Pipeline / deploy (push) Successful in 35s
2025-08-29 00:23:21 +02:00
48e5bfad60 Version 1.1.1.rc8 - Changement permissions
All checks were successful
Deployment Pipeline / deploy (push) Successful in 33s
2025-08-29 00:07:11 +02:00
54fd731ab9 Version 1.1.1-rc7.1 - Modif Pipeline
All checks were successful
Deployment Pipeline / deploy (push) Successful in 35s
2025-08-28 23:58:19 +02:00
5b8f591216 Version 1.1.1-rc7 - Modification des valeurs Path
Some checks failed
Deployment Pipeline / deploy (push) Failing after 35s
2025-08-28 23:55:51 +02:00
2fe5d35efe Version 1.1.1-rc6.1 - Modif Erreur Pipeline
Some checks failed
Deployment Pipeline / deploy (push) Failing after 40s
2025-08-28 23:47:34 +02:00
b0f5ccbe5a Version 1.1.1-rc6 - Modif Pipeline
Some checks failed
Deployment Pipeline / deploy (push) Failing after 9s
2025-08-28 23:46:27 +02:00
98e5c41fa5 Version 1.1.1-rc5 - Modif Pipeline Final
Some checks failed
Deployment Pipeline / deploy (push) Failing after 27s
2025-08-28 23:32:40 +02:00
f41eddf1dc Version 1.1.1-rc4.1 - Modif Pipeline
Some checks failed
Deployment Pipeline / deploy (push) Failing after 8s
2025-08-28 23:29:50 +02:00
f12bbe8ad2 Version 1.1.1-rc4 - Modif Pipeline
Some checks failed
Deployment Pipeline / deploy (push) Failing after 8s
2025-08-28 23:28:12 +02:00
0c50874644 Version 1.1.1-rc3 - Modification Pipeline
Some checks failed
Deployment Pipeline / deploy (push) Failing after 7s
2025-08-28 23:27:30 +02:00
59ea576181 Version 1.1.1-rc2 - Premier deploy
Some checks failed
Deployment Pipeline / deploy (push) Failing after 7s
2025-08-28 23:20:57 +02:00
8b2728622c Version 1.1.1 - Premier Deploy 2025-08-28 23:15:27 +02:00
20 changed files with 2058 additions and 1310 deletions

View File

@@ -0,0 +1,86 @@
name: Deployment Pipeline
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Setup SSH
env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
run: |
mkdir -p ~/.ssh
echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_rsa
ssh-keyscan git.raphix.fr >> ~/.ssh/known_hosts
- name: Deploy Subsonics as gitlab-ci
run: |
ssh -A -o StrictHostKeyChecking=no raphix@alpha.raphix.fr << 'EOF'
sudo su - gitlab-ci -c '
set -e
# Variables PM2 et npm
export PM2_HOME=/home/gitlab-ci/.pm2
export NPM_CONFIG_CACHE=/home/gitlab-ci/.npm
mkdir -p $PM2_HOME $NPM_CONFIG_CACHE
chown -R gitlab-ci:gitlab-ci $PM2_HOME $NPM_CONFIG_CACHE
echo "[Subsonics-Deploy] - Stage - Déploiement - START"
echo "[Subsonics-Deploy] - Arrêt de Subsonics : Processing"
cd /home/gitlab-ci
pm2 stop "Subsonics - Backend" || true
pm2 delete "Subsonics - Backend" || true
echo "[Subsonics-Deploy] - Arrêt de Subsonics : Success"
# Préparer tempdata
if [ ! -d "/home/gitlab-ci/backend/data" ]; then
mkdir -p /home/gitlab-ci/backend/data
fi
mv /home/gitlab-ci/backend/data/ /home/gitlab-ci/tempdata || true
echo "[Subsonics-Deploy] - Suppression de Subsonics : Processing"
rm -rf ./backend
echo "[Subsonics-Deploy] - Suppression de Subsonics : Success"
echo "[Subsonics-Deploy] - Installation de Subsonics : Processing"
git clone https://git.raphix.fr/subsonics/chopin backend
echo "[Subsonics-Deploy] - Installation de Subsonics : Success"
echo "[Subsonics-Deploy] - Installation des dépendances : Processing"
cd /home/gitlab-ci/backend
# Nettoyage node_modules et tempdata
rm -rf node_modules
if [ -d "/home/gitlab-ci/tempdata" ]; then
mv /home/gitlab-ci/tempdata/ ./data
fi
# Assurer la propriété gitlab-ci
chown -R gitlab-ci:gitlab-ci /home/gitlab-ci/backend
mkdir -p $NPM_CONFIG_CACHE
chown -R gitlab-ci:gitlab-ci $NPM_CONFIG_CACHE
npm install --omit=dev
echo "[Subsonics-Deploy] - Installation des dépendances : Success"
echo "[Subsonics-Deploy] - Démarrage de Subsonics : Processing"
cd /home/gitlab-ci
pm2 start subsonic.config.js
echo "[Subsonics-Deploy] - Démarrage de Subsonics : Success"
echo "[Subsonics-Deploy] - Stage - Déploiement - END"
'
EOF

29
CHANGELOG.html Normal file
View File

@@ -0,0 +1,29 @@
<div class="changelog-version changelog-actual">
<h2>Chopin - Version /*1.2.0*/</h2>
<p class="changelog-date">*_Date de sortie_*: *-07/09/2025-*</p>
<ul>
<li>/#[AJOUT][FRONTEND]#/ Suggestion de recherche, depuis Youtube</li>
<li>/#[FIX][FRONTEND]#/ Optimisation des bouttons, avec une modification du composant</li>
<li>/#[AJOUT][DISCORD]#/ Ajout de la sécurité, permettant de restreindre l'utilisation du Bot au Rôle paramétré</li>
</ul>
</div>
<div class="changelog-version">
<h2>Chopin - Version /*1.1.0*/</h2>
<p class="changelog-date">*_Date de sortie_*: *-06/09/2025-*</p>
<ul>
<li>/#[AJOUT][FRONTEND]#/ Désormais, lorsque l'utilisateur n'est pas connecté, les bouttons de lecture sont désactivés au lieu d'être masqués</li>
<li>/#[AJOUT][FRONTEND] #/ Ajout de la prise en charge de plusieurs fichiers en même temps (Impossibilité sur Discord à cause de restriction) !</li>
<li>/#[FIX][BACKEND]#/ La source Youtube est désormais fonctionnelle</li>
<li>/#[FIX][BACKEND]#/ Le changement de channel est désormais fonctionnel.</li>
<li>/#[FIX][FRONTEND]#/ Résolution d'un problème d'affichage du changelog et de la liste de lecture</li>
<li>/#[FIX][FRONTEND]#/ Le "En ligne" est désormais resynchronisé sur le Bot</li>
</ul>
</div>
<div class="changelog-version">
<h2>Chopin - Version /*1.0.0*/</h2>
<p class="changelog-date">*_Date de sortie_*: *-29/08/2025-*</p>
<ul>
<li>/#[FRONTEND]#/ Sortie de la version 1.0.0 de Chopin, le bot Discord pour SubSonics. Refonte graphique et passage sur Vue JS</li>
<li>/#[BACKEND]#/ Refonte de toute la gestion de la musique et ajout de nouvelles fonctionnalités</li>
</ul>
</div>

2210
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "chopin-backend", "name": "chopin-backend",
"version": "1.1.1", "version": "1.2.0",
"description": "Discord Bot for music - Fetching everywhere !", "description": "Discord Bot for music - Fetching everywhere !",
"main": "src/main.js", "main": "src/main.js",
"nodemonConfig": { "nodemonConfig": {
@@ -21,12 +21,10 @@
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@discordjs/voice": "^0.18.0", "@discordjs/voice": "^0.18.0",
"@distube/ytdl-core": "^4.16.10", "@distube/ytdl-core": "^4.16.12",
"@distube/ytsr": "2.0.4",
"cors": "^2.8.5", "cors": "^2.8.5",
"discord-player": "^7.1.0", "discord-player": "^7.1.0",
"discord.js": "^14.18.0", "discord.js": "^14.18.0",
"express": "^4.21.2",
"ffmpeg-static": "^5.2.0", "ffmpeg-static": "^5.2.0",
"ffprobe": "^1.1.2", "ffprobe": "^1.1.2",
"ffprobe-static": "^3.1.0", "ffprobe-static": "^3.1.0",
@@ -42,6 +40,7 @@
"spotify-web-api-node": "^5.0.2", "spotify-web-api-node": "^5.0.2",
"uuid": "^11.1.0", "uuid": "^11.1.0",
"webmetrik": "^0.1.4", "webmetrik": "^0.1.4",
"yt-search": "^2.13.1",
"ytfps": "^1.2.0" "ytfps": "^1.2.0"
} }
} }

View File

@@ -7,6 +7,8 @@ const config = require("../utils/Database/Configuration")
const metric = require("webmetrik") const metric = require("webmetrik")
const { Player } = require("../player/Player") const { Player } = require("../player/Player")
const {refreshAllUserInformation} = require("../server/auth/User") const {refreshAllUserInformation} = require("../server/auth/User")
const serverSettings = require("./ServerSettings")
const { Embed, EmbedError } = require("./Embed")
const dlog = new LogType("Discord") const dlog = new LogType("Discord")
const glog = new LogType("GuildUpdater") const glog = new LogType("GuildUpdater")
@@ -49,6 +51,29 @@ function getGuildMembers(guildId) {
return guild.members.cache.map(member => member.user.id) return guild.members.cache.map(member => member.user.id)
} }
function getGuildMember(guildId, memberId) {
const guild = client.guilds.cache.get(guildId)
if(!guild) {
dlog.error("Guild not found: " + guildId)
return null
}
return guild.members.cache.get(memberId) || null
}
function getGuildRoles(guildId) {
const guild = client.guilds.cache.get(guildId)
if(!guild) {
dlog.error("Guild not found: " + guildId)
return []
}
return guild.roles.cache.map(role => ({
id: role.id,
name: role.name,
color: role.color,
position: role.position
}))
}
function getChannel(guildId, channelId) { function getChannel(guildId, channelId) {
return client.guilds.cache.get(guildId).channels.cache.get(channelId) return client.guilds.cache.get(guildId).channels.cache.get(channelId)
} }
@@ -77,22 +102,37 @@ function init() {
operational = true operational = true
}) })
client.on("interactionCreate", (interaction) => { client.on("interactionCreate", async (interaction) => {
if(!interaction.isCommand()) return; if(!interaction.isCommand()) return;
var numberOfCommands = new metric.Metric("numberOfCommands", "Nombre de commandes éxécutées") var numberOfCommands = new metric.Metric("numberOfCommands", "Nombre de commandes éxécutées")
numberOfCommands.setValue(numberOfCommands.getValue() + 1) numberOfCommands.setValue(numberOfCommands.getValue() + 1)
var numberOfCommandsServer = new metric.Metric("numberOfCommands_" + interaction.guild.id, "Nombre de commandes éxécutées sur le serveur : " + interaction.guild.name)
numberOfCommandsServer.setValue(numberOfCommandsServer.getValue() + 1)
const command = client.commands.get(interaction.commandName) const command = client.commands.get(interaction.commandName)
const roleProtected = await serverSettings.getSecureRole(interaction.guild.id) || false
var havePermission = true;
if(roleProtected) {
await interaction.member.fetch()
if(!interaction.member.roles.cache.has(roleProtected.id)) {
havePermission = false;
}
}
try { try {
// Create a metric to count the number of commands executed by each user // Create a metric to count the number of commands executed by each user
const userCommand = new metric.Metric("userCommand_" + interaction.member.user.username, "Nombre de commandes éxécutées par l'utilisateur : " + interaction.member.user.username) const userCommand = new metric.Metric("userCommand_" + interaction.member.user.username, "Nombre de commandes éxécutées par l'utilisateur : " + interaction.member.user.username)
userCommand.setValue(userCommand.getValue() + 1) userCommand.setValue(userCommand.getValue() + 1)
dlog.log(interaction.member.user.username + "-> /" + interaction.commandName) dlog.log(interaction.member.user.username + "-> /" + interaction.commandName)
if(havePermission) {
command.execute(client, interaction) command.execute(client, interaction)
} else {
const embed = new EmbedError(`L'utilisation du Bot est réservée aux membres ayant le rôle "${roleProtected.name}"`, interaction, true)
embed.setTitle("Accès refusé")
embed.send()
}
} catch(error) { } catch(error) {
dlog.error(interaction.member.user.username + "-> /" + interaction.commandName + " : ERREUR RENCONTRE") dlog.error(interaction.member.user.username + "-> /" + interaction.commandName + " : ERREUR RENCONTRE")
@@ -169,7 +209,7 @@ function init() {
} }
process.emit("VOCAL_UPDATE")
}) })
@@ -209,23 +249,15 @@ async function refreshGuilds() {
function checkRequiredPermission(guildMember) { function checkRequiredPermission(guildMember) {
const requiredPermissions = [ const requiredPermissions = [
'CreateInstantInvite', 'AddReactions', 'ViewChannel', // Voir les salons
'Stream', 'ViewChannel', 'SendMessages', // Envoyer des messages texte
'SendMessages', 'SendTTSMessages', 'ReadMessageHistory', // Lire lhistorique des messages
'EmbedLinks', 'AttachFiles', 'Connect', // Se connecter aux salons vocaux
'ReadMessageHistory', 'UseExternalEmojis', 'Speak' // Parler dans les salons vocaux
'Connect', 'Speak',
'UseVAD', 'ChangeNickname',
'UseApplicationCommands', 'RequestToSpeak',
'CreatePublicThreads', 'CreatePrivateThreads',
'UseExternalStickers', 'SendMessagesInThreads',
'UseEmbeddedActivities', 'UseSoundboard',
'UseExternalSounds', 'SendVoiceMessages',
'SendPolls', 'UseExternalApps'
] ]
return requiredPermissions.filter(permission => !guildMember.permissions.has(permission)); return requiredPermissions.filter(permission => !guildMember.permissions.has(permission));
} }
module.exports = {init, getClient, getGuilds, getMembersVoices, getChannel, getGuildMembers, isReady} module.exports = {init, getClient, getGuilds, getMembersVoices, getChannel, getGuildMembers, getGuildMember, isReady, getGuildRoles}

View File

@@ -4,6 +4,8 @@ const wlog = new LogType("MediaBase")
const { Database } = require("../utils/Database/Database") const { Database } = require("../utils/Database/Database")
const {__glob} = require("../utils/GlobalVars") const {__glob} = require("../utils/GlobalVars")
const { AttachmentBuilder } = require("discord.js") const { AttachmentBuilder } = require("discord.js")
const { Song } = require("../player/Song")
const { getMediaInformationFromUrl } = require("../media/MediaInformation")
const discordBot = require("./Bot") const discordBot = require("./Bot")
@@ -54,7 +56,7 @@ discordBot.getClient().on("ready", () => {
// SEND FILE TO DISCORD AND GET THE URL ID // SEND FILE TO DISCORD AND GET THE URL ID
async function postMedia(file) { async function postMedia(file, userId) {
if(!connected) { if(!connected) {
wlog.error("La base de données multimédia n'est pas connectée, impossible d'envoyer le fichier.") wlog.error("La base de données multimédia n'est pas connectée, impossible d'envoyer le fichier.")
return null return null
@@ -73,7 +75,8 @@ async function postMedia(file) {
url: url, url: url,
name: file.name, name: file.name,
size: file.size, size: file.size,
createdAt: new Date().toISOString() createdAt: new Date().toISOString(),
userId: userId
}) })
mediaDB.save() mediaDB.save()
return url return url
@@ -83,6 +86,33 @@ async function postMedia(file) {
} }
} }
async function getAllMedia(userId) {
if(!connected) {
wlog.error("La base de données multimédia n'est pas connectée, impossible de récupérer les fichiers.")
return []
}
const allSongs = mediaDB.data.filter(m => m.userId === userId)
const songs = []
for(const songDB of allSongs) {
const song = new Song()
const information = await getMediaInformationFromUrl(song, songDB.url)
if(!information) {
mediaDB.data = mediaDB.data.filter(m => m.id !== songDB.id)
mediaDB.save()
continue
}
song.type = "attachment"
song.author = songDB.author
song.createdAt = songDB.createdAt
song.author = songDB.userId
song.title = songDB.name
song.id = songDB.id
song.url = songDB.url
songs.push(song)
}
return songs
}
async function getMedia(id) { async function getMedia(id) {
if(!connected) { if(!connected) {
wlog.error("La base de données multimédia n'est pas connectée, impossible de récupérer le fichier.") wlog.error("La base de données multimédia n'est pas connectée, impossible de récupérer le fichier.")
@@ -103,7 +133,26 @@ async function getMedia(id) {
} }
} }
function deleteMedia(data, userId) {
if(!connected) {
wlog.error("La base de données multimédia n'est pas connectée, impossible de supprimer le fichier.")
return false
}
const mediaIndex = mediaDB.data.findIndex(m => m.id === data.id && m.userId === userId)
if(mediaIndex === -1) {
wlog.error(`Aucun média trouvé avec l'ID : ${data.id} pour l'utilisateur : ${userId}`)
return false
}
mediaDB.data.splice(mediaIndex, 1)
mediaDB.save()
return true
}
module.exports = { module.exports = {
postMedia, postMedia,
getMedia, getMedia,
deleteMedia,
getAllMedia
} }

View File

@@ -0,0 +1,38 @@
const { LogType } = require('loguix');
const clog = new LogType('ServerSettings');
const { __glob } = require('../utils/GlobalVars');
const { Database } = require('../utils/Database/Database');
const ServerDB = new Database("server_settings", __glob.SERVER_DB, {});
function getSecureRole(guildId) {
checkKey(guildId);
var role = ServerDB.getData()[guildId].secureRole || null;
if(role) {
if(role.name === "@everyone") {
return null;
}
}
return role;
}
function setSecureRole(guildId, roleId) {
checkKey(guildId);
ServerDB.getData()[guildId].secureRole = roleId;
ServerDB.save();
process.emit("USERS_UPDATE")
return true;
}
async function checkKey(guildId) {
const data = ServerDB.getData();
if (!data[guildId]) {
data[guildId] = { secureRole: null };
ServerDB.save();
}
}
module.exports = {
getSecureRole,
setSecureRole
};

View File

@@ -10,8 +10,7 @@ require("loguix").setup(__glob.LOGS, __glob.PACKAGEINFO)
const config = require("./utils/Database/Configuration") const config = require("./utils/Database/Configuration")
const metric = require("webmetrik") const metric = require("webmetrik")
metric.setMetricFile(__glob.METRIC_FILE) metric.setMetricFile(__glob.METRIC_FILE)
metric.publishMetrics("8001", "raphraph") metric.publishMetrics("8001", "subsonicsMetricsRaph")
// SETUP // SETUP

View File

@@ -21,7 +21,7 @@ async function getMediaInformation(instance, media, provider) {
// Obtenir l'auteur (s'il existe) // Obtenir l'auteur (s'il existe)
instance.author = info.streams?.[0]?.tags?.artist ?? instance.author; instance.author = info.streams?.[0]?.tags?.artist ?? instance.author;
return true;
} catch (err) { } catch (err) {
clog.error("Impossible de récupérer les informations de la musique : " + media.attachment.name) clog.error("Impossible de récupérer les informations de la musique : " + media.attachment.name)
clog.error(err) clog.error(err)
@@ -46,7 +46,7 @@ async function getMediaInformationFromUrl(instance, url) {
// Obtenir l'auteur (s'il existe) // Obtenir l'auteur (s'il existe)
instance.author = info.streams?.[0]?.tags?.artist ?? "Auteur inconnu"; instance.author = info.streams?.[0]?.tags?.artist ?? "Auteur inconnu";
return true;
} catch (err) { } catch (err) {
clog.error("Impossible de récupérer les informations de la musique depuis l'URL : " + url); clog.error("Impossible de récupérer les informations de la musique depuis l'URL : " + url);
console.log(err) console.log(err)

View File

@@ -3,7 +3,7 @@ const clog = new LogType("YoutubeInformation");
const { Song } = require('../player/Song'); const { Song } = require('../player/Song');
const { Playlist } = require('../playlists/Playlist'); const { Playlist } = require('../playlists/Playlist');
const { getReadableDuration, getSecondsDuration } = require('../utils/TimeConverter'); const { getReadableDuration, getSecondsDuration } = require('../utils/TimeConverter');
const ytsr = require('@distube/ytsr'); const yts = require("yt-search")
const ytfps = require('ytfps'); const ytfps = require('ytfps');
async function getQuery(query, multiple) { async function getQuery(query, multiple) {
@@ -14,15 +14,15 @@ async function getQuery(query, multiple) {
try { try {
const limit = multiple ? 25 : 1; const limit = multiple ? 25 : 1;
const searchResults = await ytsr(query, { limit }); const searchResults = await yts({ query: query, limit: limit });
const videos = searchResults.items.filter(item => item.type === 'video'); const videos = searchResults.videos;
if (videos.length === 0) { if (videos.length === 0) {
clog.error("Impossible de récupérer le lien de la vidéo YouTube à partir de la requête"); clog.error("Impossible de récupérer le lien de la vidéo YouTube à partir de la requête");
return null; return null;
} }
const songs = await Promise.all(videos.map(video => getVideo(video.url))); const songs = await Promise.all(videos.map(video => new Song().processYoutubeVideo(video)));
return multiple ? songs.filter(song => song !== null) : songs[0]; return multiple ? songs.filter(song => song !== null) : songs[0];
} catch (error) { } catch (error) {
clog.error('Erreur lors de la recherche YouTube: ' + error); clog.error('Erreur lors de la recherche YouTube: ' + error);
@@ -38,9 +38,7 @@ async function getVideo(url) {
} }
try { try {
const searchResults = await ytsr(videoId[1], { limit: 1 }); const video = await yts({videoId: videoId[1]});
const video = searchResults.items.find(item => item.type === 'video');
if (video) { if (video) {
const songReturn = new Song(); const songReturn = new Song();
await songReturn.processYoutubeVideo(video); await songReturn.processYoutubeVideo(video);
@@ -72,14 +70,14 @@ async function getPlaylist(url) {
playlistId = url.match(/(list=)([a-zA-Z0-9_-]+)/); playlistId = url.match(/(list=)([a-zA-Z0-9_-]+)/);
} }
console.log(playlistId);
if (playlistId === null) { if (playlistId === null) {
clog.error("Impossible de récupérer l'identifiant de la playlist YouTube à partir de l'URL"); clog.error("Impossible de récupérer l'identifiant de la playlist YouTube à partir de l'URL");
return null; return null;
} }
const playlistInfo = await ytfps(playlistId[2]); const playlistInfo = await yts({ listId: playlistId[2] });
if (!playlistInfo) { if (!playlistInfo) {
clog.error("Impossible de récupérer la playlist YouTube à partir de l'identifiant"); clog.error("Impossible de récupérer la playlist YouTube à partir de l'identifiant");
@@ -90,15 +88,16 @@ async function getPlaylist(url) {
playlist.type = "youtube"; playlist.type = "youtube";
playlist.author = playlistInfo.author.name; playlist.author = playlistInfo.author.name;
playlist.authorId = playlistInfo.author.url; playlist.authorId = playlistInfo.author.url;
playlist.authorAvatar = await getYouTubeProfilePicture(playlistInfo.author.url);
playlist.title = playlistInfo.title; playlist.title = playlistInfo.title;
playlist.thumbnail = playlistInfo.thumbnail_url; playlist.thumbnail = playlistInfo.thumbnail;
playlist.description = playlistInfo.description;
playlist.url = `https://www.youtube.com/playlist?list=${playlistId[2]}`; playlist.url = `https://www.youtube.com/playlist?list=${playlistId[2]}`;
playlist.id = playlistId[2]; playlist.id = playlistInfo.listId;
playlist.views = playlistInfo.views;
for (const video of playlistInfo.videos) { for (const video of playlistInfo.videos) {
const song = new Song(); const song = new Song();
await song.processYoutubeVideo(video, true); await song.processYoutubeVideo(video);
playlist.duration += song.duration; playlist.duration += song.duration;
playlist.songs.push(song); playlist.songs.push(song);
} }
@@ -117,11 +116,9 @@ async function getSecondsFromUrl(url) {
return null; return null;
} }
try { try {
const searchResults = await ytsr(videoId[1], { limit: 1 }); const video = await yts({ videoId: videoId[1] });
const video = searchResults.items.find(item => item.type === 'video');
console.log(video);
if (video) { if (video) {
return getSecondsDuration(video.duration); // Convert seconds to milliseconds return video.duration.seconds;
} else { } else {
clog.error("Impossible de récupérer la vidéo YouTube à partir de l'identifiant"); clog.error("Impossible de récupérer la vidéo YouTube à partir de l'identifiant");
return null; return null;
@@ -132,4 +129,36 @@ async function getSecondsFromUrl(url) {
} }
} }
async function getYouTubeProfilePicture(channelUrl) {
try {
const res = await fetch(channelUrl, {
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
}
});
const html = await res.text();
// Match img with yt-spec-avatar-shape__image in class list
const imgRegex = /<img[^>]*(?:class="[^"]*\byt-spec-avatar-shape__image\b[^"]*"[^>]*|[^>]*class="[^"]*\byt-spec-avatar-shape__image\b[^"]*")[^>]*src="([^"]+)"/i;
const imgMatch = html.match(imgRegex);
if (imgMatch && imgMatch[1]) {
return imgMatch[1];
}
// Fallback: look for avatar in embedded JSON
const jsonRegex = /"avatar":\{"thumbnails":\[\{"url":"(.*?)"/;
const match = html.match(jsonRegex);
if (match && match[1]) {
return match[1].replace(/\\u0026/g, "&"); // Decode \u0026 to &
}
console.warn("Photo non trouvée pour :", channelUrl);
return null;
} catch (err) {
console.error("Erreur :", err);
return null;
}
}
module.exports = { getQuery, getVideo, getPlaylist, getSecondsFromUrl }; module.exports = { getQuery, getVideo, getPlaylist, getSecondsFromUrl };

View File

@@ -60,6 +60,7 @@ class List {
} }
this.setCurrent(song) this.setCurrent(song)
process.emit("PLAYERS_UPDATE") process.emit("PLAYERS_UPDATE")
//TODO: Check History and continuity
return song return song
} }

View File

@@ -2,19 +2,35 @@ const {createAudioResource, VoiceConnectionStatus, createAudioPlayer, StreamType
const {LogType} = require('loguix') const {LogType} = require('loguix')
const clog = new LogType("Youtube-Stream") const clog = new LogType("Youtube-Stream")
const ytdl = require('@distube/ytdl-core') const ytdl = require('@distube/ytdl-core')
const ffmpeg = require('fluent-ffmpeg') const { __glob } = require('../../utils/GlobalVars');
const { getRandomIPv6 } = require("@distube/ytdl-core/lib/utils"); const fs = require('fs');
async function getStream(song) { async function getStream(song) {
// FIXME: Change youtube provider
try { try {
const headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' +
'AppleWebKit/537.36 (KHTML, like Gecko) ' +
'Chrome/116.0.5845.97 Safari/537.36',
'Accept-Language': 'en-US,en;q=0.9'
};
var cookies = await JSON.parse(await fs.readFileSync(__glob.COOKIES, 'utf-8'));
const proxy = await JSON.parse(await fs.readFileSync(__glob.PROXY, 'utf-8'));
const agent = ytdl.createProxyAgent(proxy, cookies)
let stream = ytdl(song.url, { let stream = ytdl(song.url, {
quality: 'highestaudio', quality: 'highestaudio',
highWaterMark: 1 << 30, highWaterMark: 1 << 30,
liveBuffer: 20000, liveBuffer: 20000,
dlChunkSize: 0, dlChunkSize: 0,
bitrate: 128, bitrate: 128,
requestOptions: {
headers: headers,
},
agent: agent,
}); });
return stream return stream

View File

@@ -5,6 +5,7 @@ const songCheck = require('./SongCheck')
const ffmpeg = require('fluent-ffmpeg') const ffmpeg = require('fluent-ffmpeg')
const fs = require('fs') const fs = require('fs')
const { PassThrough } = require('stream'); const { PassThrough } = require('stream');
const { Metric } = require('webmetrik')
const plog = new LogType("Player") const plog = new LogType("Player")
const clog = new LogType("Signal") const clog = new LogType("Signal")
@@ -21,6 +22,7 @@ class Player {
player; player;
guildId; guildId;
channelId; channelId;
channelName;
queue; queue;
currentResource; currentResource;
loop = false; loop = false;
@@ -29,7 +31,9 @@ class Player {
clog.error("Impossible de créer un Player, car guildId est null") clog.error("Impossible de créer un Player, car guildId est null")
return return
} }
if(AllPlayers.has(guildId)) { if(AllPlayers.has(guildId)) {
return AllPlayers.get(guildId) return AllPlayers.get(guildId)
} }
this.connection = null this.connection = null
@@ -37,6 +41,7 @@ class Player {
this.guildId = guildId this.guildId = guildId
this.queue = new List(guildId) this.queue = new List(guildId)
AllPlayers.set(guildId, this) AllPlayers.set(guildId, this)
} }
async join(channel) { async join(channel) {
@@ -59,6 +64,7 @@ class Player {
joinChannel(channel) { joinChannel(channel) {
this.channelId = channel.id this.channelId = channel.id
this.channelName = channel.name
this.connection = joinVoiceChannel({ this.connection = joinVoiceChannel({
channelId: channel.id, channelId: channel.id,
guildId: channel.guild.id, guildId: channel.guild.id,
@@ -77,6 +83,8 @@ class Player {
} }
}); });
this.connected = true this.connected = true
AllPlayers.set(this.guildId, this)
process.emit("PLAYERS_UPDATE") process.emit("PLAYERS_UPDATE")
} }
@@ -119,7 +127,7 @@ class Player {
checkConnection() { checkConnection() {
if(this.connection === null) { if(this.connection === null) {
clog.error(`GUILD : ${this.guildId} - La connection n'est pas définie`) // clog.error(`GUILD : ${this.guildId} - La connection n'est pas définie`)
return true return true
} }
if(this.player === null) { if(this.player === null) {
@@ -134,7 +142,7 @@ class Player {
const state = { const state = {
current: this.queue.current, current: this.queue.current,
next: this.queue.next, next: this.queue.next,
previous: this.queue.previous, previous: this.queue.getPrevious(),
loop: this.loop, loop: this.loop,
shuffle: this.queue.shuffle, shuffle: this.queue.shuffle,
paused: playerStatus === AudioPlayerStatus.Paused, paused: playerStatus === AudioPlayerStatus.Paused,
@@ -143,6 +151,7 @@ class Player {
playerState: playerStatus, playerState: playerStatus,
connectionState: connectionStatus, connectionState: connectionStatus,
channelId: this.channelId, channelId: this.channelId,
channelName: this.channelName,
guildId: this.guildId, guildId: this.guildId,
} }
return state return state
@@ -171,12 +180,19 @@ class Player {
} }
async play(song) { async play(song) {
if(!songCheck.checkSong(song)) return if(!songCheck.checkSong(song)) return
if(this.checkConnection()) return if(this.checkConnection()) return
if(this.queue.current != null) { if(this.queue.current != null) {
this.player.stop() this.player.stop()
} }
var numberOfMusicPlayedPerServer = new Metric("numberOfMusicPlayed_" + this.guildId, "Nombre de musiques jouées sur le serveur : " + this.guildId)
numberOfMusicPlayedPerServer.setValue(numberOfMusicPlayedPerServer.getValue() + 1)
var numberOfSecondsPlayedPerServer = new Metric("numberOfSecondsPlayed_" + this.guildId, "Temps jouée sur le serveur : " + this.guildId)
numberOfSecondsPlayedPerServer.setValue(numberOfSecondsPlayedPerServer.getValue() + song.duration)
this.queue.setCurrent(song) this.queue.setCurrent(song)
this.stream = await this.getStream(song) this.stream = await this.getStream(song)
@@ -240,7 +256,6 @@ class Player {
process.emit("PLAYERS_UPDATE") process.emit("PLAYERS_UPDATE")
return true return true
} }
const { LogType } = require('loguix')
} }
@@ -257,6 +272,7 @@ class Player {
this.player = null this.player = null
this.connection = null this.connection = null
this.channelId = null this.channelId = null
this.channelName = null
this.connected = false this.connected = false
Activity.idleActivity() Activity.idleActivity()
this.queue.destroy() this.queue.destroy()
@@ -291,6 +307,7 @@ class Player {
} }
const passThroughStream = new PassThrough(); const passThroughStream = new PassThrough();
duration = Math.floor(duration.time);
ffmpeg(this.stream) ffmpeg(this.stream)
.setStartTime(duration) // Démarrer à la position demandée (en secondes) .setStartTime(duration) // Démarrer à la position demandée (en secondes)
.outputOptions('-f', 'mp3') // Specify output format if needed .outputOptions('-f', 'mp3') // Specify output format if needed
@@ -398,6 +415,7 @@ function getAllPlayers() {
AllPlayers.forEach((player) => { AllPlayers.forEach((player) => {
players.push(player) players.push(player)
}) })
return players
} }
function isPlayer(guildId) { function isPlayer(guildId) {

View File

@@ -51,31 +51,17 @@ class Song {
} }
async processYoutubeVideo(video, playlist) { async processYoutubeVideo(video) {
if(playlist) {
this.title = video.title this.title = video.title
this.author = video.author.name this.author = video.author.name
this.authorId = video.author.channel_url
this.thumbnail = video.thumbnail_url
this.url = video.url
this.type = "youtube"
this.id = video.id
this.duration = video.milis_length / 1000
this.readduration = getReadableDuration(this.duration)
} else {
this.title = video.name
this.author = video.author.name
this.authorId = video.author.url this.authorId = video.author.url
this.thumbnail = video.thumbnail this.thumbnail = video.thumbnail
this.url = video.url this.url = "https://www.youtube.com/watch?v=" + video.videoId
this.type = "youtube" this.type = "youtube"
this.id = video.id this.id = video.videoId
this.duration = getSecondsDuration(video.duration) this.duration = video.duration.seconds
this.readduration = getReadableDuration(this.duration) this.readduration = getReadableDuration(this.duration)
}
return this return this
} }

View File

@@ -26,11 +26,11 @@ function checkSong(song) {
slog.error("La musique n'a pas d'auteur") slog.error("La musique n'a pas d'auteur")
return false return false
} }
if(!song.duration) { if(song.duration == null) {
slog.error("La musique n'a pas de durée") slog.error("La musique n'a pas de durée")
return false return false
} }
if(!song.readduration) { if(song.readduration == null) {
slog.error("La musique n'a pas de durée lisible") slog.error("La musique n'a pas de durée lisible")
return false return false
} }

View File

@@ -3,6 +3,7 @@ const { getReadableDuration } = require("../utils/TimeConverter");
class Playlist { class Playlist {
title = "Aucun titre"; title = "Aucun titre";
id; id;
playlistId;
url; url;
author = "Auteur inconnu"; author = "Auteur inconnu";
authorId; authorId;

View File

@@ -35,27 +35,28 @@ function getPlaylistsOfUser(id) {
/** /**
* @param {string} id * @param {string} id
* @param {string} name * @param {string} playlistId
* @returns {Playlist} * @returns {Playlist}
*/ */
function getPlaylistOfUser(id, name) { function getPlaylistOfUser(id, playlistId) {
const playlists = getPlaylistsOfUser(id); const playlists = getPlaylistsOfUser(id);
const playlist = playlists.find(p => p.title === name); const playlist = playlists.find(p => p.playlistId === playlistId);
if (!playlist) { if (!playlist) {
clog.warn(`La playlist ${name} n'existe pas pour l'utilisateur ${id}`); clog.warn(`La playlist ${playlistId} n'existe pas pour l'utilisateur ${id}`);
return null; return null;
} }
return playlist; return playlist;
} }
async function addPlaylist(id, name, url) { async function addPlaylist(id, name, url, authorName, authorId, authorAvatar) {
const playlists = getPlaylistsOfUser(id); const playlists = getPlaylistsOfUser(id);
var playlist = new Playlist(name, url); var playlist = new Playlist(name, url);
if (playlists.find(p => p.title === name)) { let failed = false;
clog.warn(`La playlist ${name} existe déjà pour l'utilisateur ${id}`); playlist.thumbnail = null
return; playlist.author = authorName;
} playlist.authorAvatar = `https://cdn.discordapp.com/avatars/${authorId}/${authorAvatar}`;
var failed; playlist.views = null;
if(url) { if(url) {
await Finder.search(url, false, "PLAYLIST").then(async (playlistFounded) => { await Finder.search(url, false, "PLAYLIST").then(async (playlistFounded) => {
if(!playlistFounded) { if(!playlistFounded) {
@@ -69,7 +70,7 @@ async function addPlaylist(id, name, url) {
} }
}) })
} }
playlist.playlistId = new String(Date.now());
if(failed) { if(failed) {
clog.error(`Impossible de trouver la playlist ${name} pour l'utilisateur ${id}`); clog.error(`Impossible de trouver la playlist ${name} pour l'utilisateur ${id}`);
return null; return null;
@@ -81,65 +82,60 @@ async function addPlaylist(id, name, url) {
return playlist; return playlist;
} }
function removePlaylist(id, name) { function removePlaylist(id, playlistId) {
const playlists = getPlaylistsOfUser(id); const playlists = getPlaylistsOfUser(id);
const index = playlists.findIndex(p => p.title === name); const index = playlists.findIndex(p => p.playlistId === playlistId);
if (index === -1) { if (index === -1) {
clog.warn(`La playlist ${name} n'existe pas pour l'utilisateur ${id}`); clog.warn(`La playlist ${playlistId} n'existe pas pour l'utilisateur ${id}`);
return; return;
} }
playlists.splice(index, 1); playlists.splice(index, 1);
playlistDB.save(); playlistDB.save();
clog.log(`Suppression de la playlist ${name} pour l'utilisateur ${id}`); clog.log(`Suppression de la playlist ${playlistId} pour l'utilisateur ${id}`);
} }
function getPlaylist(id, name) { function getPlaylist(id, playlistId) {
const playlists = getPlaylistsOfUser(id); const playlists = getPlaylistsOfUser(id);
const playlist = playlists.find(p => p.title === name); const playlist = playlists.find(p => p.playlistId === playlistId);
if (!playlist) { if (!playlist) {
clog.warn(`La playlist ${name} n'existe pas pour l'utilisateur ${id}`); clog.warn(`La playlist ${playlistId} n'existe pas pour l'utilisateur ${id}`);
return null; return null;
} }
return playlist; return playlist;
} }
function copyPlaylist(fromId, toId, name) { function copyPlaylist(fromId, toId, playlistId) {
const playlists = getPlaylistsOfUser(fromId); const playlists = getPlaylistsOfUser(fromId);
const playlist = playlists.find(p => p.title === name); const playlist = playlists.find(p => p.playlistId === playlistId);
if (!playlist) { if (!playlist) {
clog.warn(`La playlist ${name} n'existe pas pour l'utilisateur ${fromId}`); clog.warn(`La playlist ${playlistId} n'existe pas pour l'utilisateur ${fromId}`);
return null; return null;
} }
const toPlaylists = getPlaylistsOfUser(toId); const toPlaylists = getPlaylistsOfUser(toId);
// Check if the playlist already exists in the target user // Check if the playlist already exists in the target user
if (toPlaylists.find(p => p.title === name)) { if (toPlaylists.find(p => p.title === playlist.title)) {
clog.warn(`La playlist ${name} existe déjà pour l'utilisateur ${toId}`); clog.warn(`La playlist ${playlist.title} existe déjà pour l'utilisateur ${toId}`);
return null; return null;
} }
toPlaylists.push(playlist); toPlaylists.push(playlist);
playlistDB.save(); playlistDB.save();
clog.log(`Copie de la playlist ${name} de l'utilisateur ${fromId} vers l'utilisateur ${toId}`); clog.log(`Copie de la playlist ${playlist.title} de l'utilisateur ${fromId} vers l'utilisateur ${toId}`);
return false; return false;
} }
function renamePlaylist(id, oldName, newName) { function renamePlaylist(id, playlistId, newName) {
const playlists = getPlaylistsOfUser(id); const playlists = getPlaylistsOfUser(id);
const playlist = playlists.find(p => p.title === oldName); const playlist = playlists.find(p => p.playlistId === playlistId);
if (!playlist) { if (!playlist) {
clog.warn(`La playlist ${oldName} n'existe pas pour l'utilisateur ${id}`); clog.warn(`La playlist ${playlistId} n'existe pas pour l'utilisateur ${id}`);
return null;
}
// Check if the new name already exists
if (playlists.find(p => p.title === newName)) {
clog.warn(`La playlist ${newName} existe déjà pour l'utilisateur ${id}`);
return null; return null;
} }
playlist.title = newName; playlist.title = newName;
playlistDB.save(); playlistDB.save();
clog.log(`Renommage de la playlist ${oldName} en ${newName} pour l'utilisateur ${id}`); clog.log(`Renommage de la playlist ${playlistId} en ${newName} pour l'utilisateur ${id}`);
} }
function addSong(id, playlistName, song) { function addSong(id, playlistId, song) {
if(typeof song === "string") { if(typeof song === "string") {
try { try {
song = JSON.parse(song) song = JSON.parse(song)
@@ -154,9 +150,9 @@ function addSong(id, playlistName, song) {
return null; return null;
} }
const playlists = getPlaylistsOfUser(id); const playlists = getPlaylistsOfUser(id);
const playlist = playlists.find(p => p.title === playlistName); const playlist = playlists.find(p => p.playlistId === playlistId);
if (!playlist) { if (!playlist) {
clog.warn(`La playlist ${playlistName} n'existe pas pour l'utilisateur ${id}`); clog.warn(`La playlist ${playlistId} n'existe pas pour l'utilisateur ${id}`);
return null; return null;
} }
// Check the integrity of the song // Check the integrity of the song
@@ -165,25 +161,31 @@ function addSong(id, playlistName, song) {
return null; return null;
} }
playlist.songs.push(song); playlist.songs.push(song);
// Recalculate the songs duration and readduration
playlist.duration += song.duration;
playlist.readduration = getReadableDuration(playlist.duration);
playlistDB.save(); playlistDB.save();
clog.log(`Ajout de la chanson ${song.title} à la playlist ${playlistName} pour l'utilisateur ${id}`); clog.log(`Ajout de la chanson ${song.title} à la playlist ${playlistId} pour l'utilisateur ${id}`);
} }
function removeSong(id, playlistName, songId) { function removeSong(id, playlistId, songId) {
const playlists = getPlaylistsOfUser(id); const playlists = getPlaylistsOfUser(id);
const playlist = playlists.find(p => p.title === playlistName); const playlist = playlists.find(p => p.playlistId === playlistId);
if (!playlist) { if (!playlist) {
clog.warn(`La playlist ${playlistName} n'existe pas pour l'utilisateur ${id}`); clog.warn(`La playlist ${playlistId} n'existe pas pour l'utilisateur ${id}`);
return null; return null;
} }
const index = playlist.songs.findIndex(s => s.id === songId); const index = playlist.songs.findIndex(s => s.id === songId);
if (index === -1) { if (index === -1) {
clog.warn(`La chanson ${songId} n'existe pas dans la playlist ${playlistName} pour l'utilisateur ${id}`); clog.warn(`La chanson ${songId} n'existe pas dans la playlist ${playlistId} pour l'utilisateur ${id}`);
return null; return null;
} }
playlist.duration -= playlist.songs[index].duration;
playlist.songs.splice(index, 1); playlist.songs.splice(index, 1);
playlist.readduration = getReadableDuration(playlist.duration);
playlistDB.save(); playlistDB.save();
clog.log(`Suppression de la chanson ${songId} de la playlist ${playlistName} pour l'utilisateur ${id}`); clog.log(`Suppression de la chanson ${songId} de la playlist ${playlistId} pour l'utilisateur ${id}`);
} }
async function processYoutubeData(userId, data) { async function processYoutubeData(userId, data) {
@@ -264,6 +266,55 @@ async function processYoutubeData(userId, data) {
return playlists; return playlists;
} }
async function deleteUserPlaylists(userId) {
// Delete all playlists of the user and the keys
if (!playlistDB.data[userId]) {
clog.warn(`Aucune playlist trouvée pour l'utilisateur ${userId}`);
return;
}
delete playlistDB.data[userId];
playlistDB.save();
}
async function refreshPlaylist(userId, playlistId) {
var playlist = getPlaylistOfUser(userId, playlistId);
if (!playlist) {
clog.warn(`Aucune playlist trouvée pour l'utilisateur ${userId} avec l'ID ${playlistId}`);
return null;
}
let failed = false;
// If playlistHasUrl, refresh the playlist
if (playlist.url) {
await Finder.search(playlist.url, false, "PLAYLIST").then(async (playlistFounded) => {
if(!playlistFounded) {
failed = true;
}
if(playlistFounded instanceof Playlist) {
playlist = playlistFounded;
}
if(playlist.type === "spotify") {
playlist.songs = await spotify.getTracks(playlist);
}
})
}
if(failed) {
clog.warn(`Échec de la mise à jour de la playlist ${playlistId} pour l'utilisateur ${userId}`);
return null;
}
playlist.playlistId = playlistId;
const playlists = getPlaylistsOfUser(userId);
// Remove the older one
// Push at the same index
const existingIndex = playlists.findIndex(p => p.playlistId === playlistId);
playlists.splice(existingIndex, 1, playlist);
playlistDB.save();
return playlist;
}
module.exports = { module.exports = {
getPlaylistsOfUser, getPlaylistsOfUser,
getPlaylistOfUser, getPlaylistOfUser,
@@ -274,5 +325,7 @@ module.exports = {
renamePlaylist, renamePlaylist,
addSong, addSong,
removeSong, removeSong,
processYoutubeData processYoutubeData,
deleteUserPlaylists,
refreshPlaylist
} }

View File

@@ -1,5 +1,6 @@
const {LogType} = require('loguix') const {LogType} = require('loguix')
const wlog = new LogType("Server") const wlog = new LogType("Server")
const metrics = require("webmetrik")
const fs = require("fs") const fs = require("fs")
const path = require("path") const path = require("path")
@@ -18,6 +19,7 @@ const {__glob} = require("../utils/GlobalVars")
const playlists = require("../playlists/PlaylistManager") const playlists = require("../playlists/PlaylistManager")
const history = require("../playlists/History") const history = require("../playlists/History")
const lyrics = require("../lyrics/Lyrics") const lyrics = require("../lyrics/Lyrics")
const serverSettings = require("../discord/ServerSettings")
const mediaBase = require("../discord/MediaBase") const mediaBase = require("../discord/MediaBase")
const googleApis = require("../playlists/Google/OAuth2") const googleApis = require("../playlists/Google/OAuth2")
const youtubeApi = require("../playlists/Google/YoutubeList") const youtubeApi = require("../playlists/Google/YoutubeList")
@@ -33,8 +35,6 @@ const allConnectedUsers = new Array()
const guildConnectedUsers = new Map() const guildConnectedUsers = new Map()
const UsersBySocket = new Map() const UsersBySocket = new Map()
//TODO: Refactor this file to implement the fact that server can be joined and leaved and all the events are now handled, so guildId is not required for every event
function init() { function init() {
wlog.step.init("server_init", "Initialisation du serveur Socket.IO") wlog.step.init("server_init", "Initialisation du serveur Socket.IO")
@@ -44,16 +44,17 @@ function init() {
cors: { cors: {
origin: "*" origin: "*"
}, },
maxHttpBufferSize: 300 * 1024 * 1024
}) })
process.on("PLAYERS_UPDATE", () => { process.on("PLAYERS_UPDATE", () => {
if(io) { if(io) {
io.sockets.emit("/GUILD/UPDATE")
// Get all players and send them to client subscribed to the guild // Get all players and send them to client subscribed to the guild
for(var guild of discordBot.getGuilds().keys()) { for(var guild of discordBot.getGuilds().keys()) {
const player = players.getPlayer(guild) const player = players.getPlayer(guild)
if(player) { if(player) {
if(!player.isConnected()) continue;
io.to(player.guildId).emit("/PLAYER/UPDATE", player.getState()) io.to(player.guildId).emit("/PLAYER/UPDATE", player.getState())
wlog.log("Envoi de l'état du player de la guilde : " + player.guildId + " à tous les utilisateurs connectés") wlog.log("Envoi de l'état du player de la guilde : " + player.guildId + " à tous les utilisateurs connectés")
} }
@@ -61,7 +62,7 @@ function init() {
} }
}) })
process.on("USERS_UPDATE", () => { process.on("USERS_UPDATE", async () => {
if(io) { if(io) {
// Get all players and send them to client subscribed to the guild // Get all players and send them to client subscribed to the guild
for(var guild of discordBot.getGuilds().keys()) { for(var guild of discordBot.getGuilds().keys()) {
@@ -75,6 +76,12 @@ function init() {
} }
}) })
process.on("VOCAL_UPDATE", async () => {
if(io) {
io.sockets.emit("/CHANNEL/UPDATE")
}
})
io.on("connection", async (socket) => { io.on("connection", async (socket) => {
var socketUser; var socketUser;
@@ -86,9 +93,9 @@ function init() {
wlog.log(`Connexion d'un client : ${socket.id}`) wlog.log(`Connexion d'un client : ${socket.id}`)
socket.on("disconnect", () => { socket.on("disconnect", (info) => {
handleDisconnect() handleDisconnect()
wlog.log("Déconnexion du client : " + socket.id) wlog.log("Déconnexion du client : " + socket.id + " - Raison : " + info)
}) })
socket.on("error", (error) => { socket.on("error", (error) => {
@@ -123,11 +130,6 @@ function init() {
return return
} else { } else {
const loggedUser = await users.addUser(discordUser.auth, discordUser.identity) const loggedUser = await users.addUser(discordUser.auth, discordUser.identity)
for(var guild of discordUser.guilds) {
if(guild.owner) {
users.setGuildOwner(loggedUser.identity.id, guild.id, true)
}
}
const newToken = await loggedUser.createToken() const newToken = await loggedUser.createToken()
socket.emit("NEW_TOKEN", newToken) socket.emit("NEW_TOKEN", newToken)
token = newToken token = newToken
@@ -220,56 +222,22 @@ function init() {
// CHECKED : 24/04/2025 // CHECKED : 24/04/2025
IORequest("/USER/INFO", () => { IORequest("/USER/INFO", () => {
var guildPresents = new Array(); socketUser = users.getUserById(socketUser.identity.id)
var guildsOfBot = discordBot.getGuilds() socketUser.identity['isAdmin'] = socketUser.isAdmin()
for(var guild of guildsOfBot) { var guildPresents = getUserGuilds();
if(guild[1].allMembers.includes(socketUser.identity.id)) {
const guildData = guild[1]
guildData['members'] = new Array()
guildData.serverMember = guild[1].allMembers.length
for(var user of guildConnectedUsers.get(guild[0]) || []) {
const userData = users.getUserById(user.id)
if(userData && userData.identity.id != socketUser.identity.id) {
let infos = {
id: userData.identity.id,
username: userData.identity.username,
avatar: userData.identity.avatar,
isAdmin: userData.isAdmin(),
isOwner: userData.isOwner(guild[0]),
isMod: userData.isMod(guild[0]),
}
guildData.members.push(infos)
}
}
// Send if the bot is connected to the guild
if(players.getPlayer(guild[0]) && players.getPlayer(guild[0]).isConnected()) {
guildData.connected = true
} else {
guildData.connected = false
}
// Leave the room if the user is not in the guild
if(socket.rooms.has(guild[0]) && !checkUserGuild(socketUser, guild[0])) {
socket.leave(guild[0])
removeGuildConnectedUser(socketUser.identity)
wlog.warn("L'utilisateur '" + socketUser.identity.username + "' quitte la room de la guilde : " + guild[0] + " car il n'est pas dans la guilde) /!\\")
}
guildPresents.push(guildData)
}
}
IOAnswer("/USER/INFO", { IOAnswer("/USER/INFO", {
identity: socketUser.identity, identity: socketUser.identity,
guilds: guildPresents, guilds: guildPresents,
labels: socketUser.labels, labels: socketUser.labels,
history: history.getPersonalHistory(socketUser.identity.id), history: history.getPersonalHistory(socketUser.identity.id),
}) }, true)
wlog.log("Envoi des informations Discord de '" + socketUser.identity.id + "' à '" + socket.id + "'" ) wlog.log("[USUAL] - Envoi des informations Discord de '" + socketUser.identity.id + "' à '" + socket.id + "'" )
}) }, true)
IORequest("/USER/HISTORY", () => { IORequest("/USER/HISTORY", () => {
IOAnswer("/USER/HISTORY", history.getPersonalHistory(socketUser.identity.id)) IOAnswer("/USER/HISTORY", history.getPersonalHistory(socketUser.identity.id), true)
}) }, true)
//CHECKED : 24/04/2025 //CHECKED : 24/04/2025
IORequest("/USER/SIGNOUT", () => { IORequest("/USER/SIGNOUT", () => {
@@ -278,6 +246,15 @@ function init() {
socket.disconnect() socket.disconnect()
}) })
IORequest("/USER/DELETE", async () => {
socketUser.removeToken(token)
await users.deleteAccount(socketUser.identity.id)
await playlists.deleteUserPlaylists(socketUser.identity.id)
await history.clearPersonalHistory(socketUser.identity.id)
await IOAnswer("/USER/DELETE", true)
socket.disconnect()
})
// CHECKED : 24/04/2025 // CHECKED : 24/04/2025
IORequest("/USERS/LIST", () => { IORequest("/USERS/LIST", () => {
if(!checkUserGuild(socketUser, actualGuildId)) return if(!checkUserGuild(socketUser, actualGuildId)) return
@@ -285,6 +262,20 @@ function init() {
IOAnswer("/USERS/LIST", guildConnectedUsers.get(actualGuildId)) IOAnswer("/USERS/LIST", guildConnectedUsers.get(actualGuildId))
}) })
IORequest("/VERSION", () => {
IOAnswer("/VERSION", __glob.VERSION)
})
IORequest("/CHANGELOG", () => {
const changelogPath = __glob.CHANGELOG_PATH
if(!fs.existsSync(changelogPath)) {
wlog.warn("Aucun changelog trouvé à l'emplacement : " + changelogPath)
return IOAnswer("/CHANGELOG", false)
}
const changelogContent = fs.readFileSync(changelogPath, "utf-8")
IOAnswer("/CHANGELOG", changelogContent, true)
}, true)
// PLAYERS // PLAYERS
IORequest("/PLAYER/LYRICS", async () => { IORequest("/PLAYER/LYRICS", async () => {
@@ -317,38 +308,81 @@ function init() {
// ChECKED : 03/05/2025 // ChECKED : 03/05/2025
IORequest("/GUILD/JOIN", async (guildId) => { IORequest("/GUILD/JOIN", async (guildId) => {
if(!checkUserGuild(socketUser, guildId)) return IOAnswer("/GUILD/JOIN", "No guild found or not in the guild") if(!checkUserGuild(socketUser, guildId)) return IOAnswer("/GUILD/JOIN", false)
if(socket.rooms.has(guildId)) { if(socket.rooms.has(guildId)) {
wlog.warn("L'utilisateur '" + socketUser.identity.username + "' est déjà dans la room de la guilde : " + guildId) wlog.warn("[USUAL] - L'utilisateur '" + socketUser.identity.username + "' est déjà dans la room de la guilde : " + guildId)
IOAnswer("/GUILD/JOIN", true, true)
} else { } else {
// Make him to leave all the other rooms except the ADMIN room if he is admin // Make him to leave all the other rooms except the ADMIN room if he is admin
await socket.rooms.forEach((room) => { await socket.rooms.forEach((room) => {
if(room != "ADMIN" && room != guildId && room != socket.id) { if(room != "ADMIN" && room != guildId && room != socket.id) {
socket.leave(room) socket.leave(room)
wlog.log("L'utilisateur '" + socketUser.identity.username + "' quitte la room de la guilde: " + room) wlog.log("L'utilisateur '" + socketUser.identity.username + "' quitte la room de la guilde: " + room)
removeGuildConnectedUser(socketUser.identity) removeGuildConnectedUser(socketUser.identity.id)
} }
}) })
socket.join(guildId) socket.join(guildId)
wlog.log("L'utilisateur '" + socketUser.identity.username + "' rejoint la room de la guilde : " + guildId) wlog.log("L'utilisateur '" + socketUser.identity.username + "' rejoint la room de la guilde : " + guildId)
addGuildConnectedUser(socketUser.identity, guildId) addGuildConnectedUser(socketUser.identity, guildId)
actualGuildId = guildId actualGuildId = guildId
IOAnswer("/GUILD/JOIN", true) IOAnswer("/GUILD/JOIN", true, true)
process.emit("PLAYERS_UPDATE") process.emit("PLAYERS_UPDATE")
process.emit("USERS_UPDATE") process.emit("USERS_UPDATE")
} }
}) }, true)
IORequest("/GUILD/INFO", () => {
if(!checkUserGuild(socketUser, actualGuildId)) return IOAnswer("/GUILD/INFO", false)
const guild = discordBot.getGuilds().get(actualGuildId)
if(!guild) {
wlog.warn("Aucune guilde trouvée pour l'id : " + actualGuildId)
return IOAnswer("/GUILD/INFO", false)
}
const guildData = {
id: guild.id,
name: guild.name,
icon: guild.icon,
owner: guild.owner,
members: new Array(),
serverMember: guild.allMembers.length,
connected: players.getPlayer(actualGuildId) && players.getPlayer(actualGuildId).isConnected(),
labels: socketUser.labels,
isOwner: socketUser.identity.id === guild.owner,
isMod: socketUser.isMod(actualGuildId),
isAdmin: socketUser.isAdmin(),
}
for(var user of guildConnectedUsers.get(actualGuildId) || []) {
const userData = users.getUserById(user.id)
if(userData && userData.identity.id != socketUser.identity.id) {
let infos = {
id: userData.identity.id,
username: userData.identity.username,
global_name: userData.identity.global_name,
avatar: userData.identity.avatar,
isAdmin: userData.isAdmin(),
isOwner: userData.identity.id == guild.owner,
isMod: userData.isMod(actualGuildId),
}
if(guildData.members.find(m => m.id == infos.id)) continue
guildData.members.push(infos)
}
}
IOAnswer("/GUILD/INFO", guildData, true)
}, true)
IORequest("/GUILD/LIST", () => {
const guildData = getUserGuilds()
IOAnswer("/GUILD/LIST", guildData, true)
}, true)
// CHECKED : 03/05/2025 // CHECKED : 03/05/2025
IORequest("/PLAYER/STATE", async () => { IORequest("/PLAYER/STATE", async () => {
const plaryer = await verifyPlayerAction(actualGuildId) const player = await verifyPlayerAction(actualGuildId)
if(!player) return IOAnswer("/PLAYER/STATE", false) if(!player) return IOAnswer("/PLAYER/STATE", false)
IOAnswer("/PLAYER/STATE", await player.getState()) IOAnswer("/PLAYER/STATE", await player.getState(), true)
}) }, true)
// CHECKED : 03/05/2025 // CHECKED : 03/05/2025
@@ -384,7 +418,7 @@ function init() {
// CHECKED : 03/05/2025 // CHECKED : 03/05/2025
IORequest("/PLAYER/CHANNEL/CHANGE", () => { IORequest("/PLAYER/CHANNEL/CHANGE", () => {
handlePlayerAction(actualGuildId, (player) => { handlePlayerAction(actualGuildId, (player) => {
const channel = getUserChannel() const channel = getUserChannel(false, true)
if(!channel) { if(!channel) {
IOAnswer("/PLAYER/CHANNEL/CHANGE", false) IOAnswer("/PLAYER/CHANNEL/CHANGE", false)
return return
@@ -409,10 +443,10 @@ function init() {
// CHECKED : 04/05/2025 // CHECKED : 04/05/2025
IORequest("/QUEUE/PLAY", (data) => { IORequest("/QUEUE/PLAY", (data) => {
if(!data) return IOAnswer("/QUEUE/PLAY/NOW", false) if(!data) return IOAnswer("/QUEUE/PLAY", false)
const {index, listType, now} = data const {index, listType, now} = data
if(!index) return IOAnswer("/QUEUE/PLAY/NOW", false) if(index == null) return IOAnswer("/QUEUE/PLAY", false)
if(!listType) return IOAnswer("/QUEUE/PLAY/NOW", false) if(listType == null) return IOAnswer("/QUEUE/PLAY", false)
if(!checkUserGuild(socketUser, actualGuildId)) return if(!checkUserGuild(socketUser, actualGuildId)) return
const player = new Player(actualGuildId) const player = new Player(actualGuildId)
if(!connectToPlayer(actualGuildId, player)) return IOAnswer("/QUEUE/PLAY", false) if(!connectToPlayer(actualGuildId, player)) return IOAnswer("/QUEUE/PLAY", false)
@@ -424,7 +458,7 @@ function init() {
const next = player.queue.getNext() const next = player.queue.getNext()
song = next[index] song = next[index]
} }
if(!song) return IOAnswer("/QUEUE/PLAY/NOW", false) if(!song) return IOAnswer("/QUEUE/PLAY", false)
if(listType == "next") player.queue.removeNextByIndex(index) if(listType == "next") player.queue.removeNextByIndex(index)
if(now) { if(now) {
player.play(song) player.play(song)
@@ -432,12 +466,12 @@ function init() {
player.add(song) player.add(song)
} }
history.addToPersonalHistory(socketUser.identity.id, song) history.addToPersonalHistory(socketUser.identity.id, song)
IOAnswer("/QUEUE/PLAY/NOW", true) IOAnswer("/QUEUE/PLAY", true)
}) })
// CHECKED : 04/05/2025 // CHECKED : 04/05/2025
IORequest("/QUEUE/NEXT/DELETE", (index) => { IORequest("/QUEUE/NEXT/DELETE", (index) => {
if(!index) return IOAnswer("/QUEUE/NEXT/DELETE", false) if(index == null) return IOAnswer("/QUEUE/NEXT/DELETE", false)
handlePlayerAction(actualGuildId, (player) => { handlePlayerAction(actualGuildId, (player) => {
const next = player.queue.getNext() const next = player.queue.getNext()
if(!next[index]) return IOAnswer("/QUEUE/NEXT/DELETE", false); if(!next[index]) return IOAnswer("/QUEUE/NEXT/DELETE", false);
@@ -454,8 +488,8 @@ function init() {
IORequest("/QUEUE/NEXT/MOVE", (data) => { IORequest("/QUEUE/NEXT/MOVE", (data) => {
if(!data) return IOAnswer("/QUEUE/NEXT/MOVE", false) if(!data) return IOAnswer("/QUEUE/NEXT/MOVE", false)
const {index, newIndex} = data const {index, newIndex} = data
if(!index) return IOAnswer("/QUEUE/NEXT/MOVE", false) if(index == null) return IOAnswer("/QUEUE/NEXT/MOVE", false)
if(!newIndex) return IOAnswer("/QUEUE/NEXT/MOVE", false) if(newIndex == null) return IOAnswer("/QUEUE/NEXT/MOVE", false)
handlePlayerAction(actualGuildId, (player) => { handlePlayerAction(actualGuildId, (player) => {
const next = player.queue.getNext() const next = player.queue.getNext()
if(!next[index]) return IOAnswer("/QUEUE/NEXT/MOVE", false); if(!next[index]) return IOAnswer("/QUEUE/NEXT/MOVE", false);
@@ -478,6 +512,7 @@ function init() {
if(typeof song == "string") { if(typeof song == "string") {
song = JSON.parse(song) song = JSON.parse(song)
} }
song = new Song(song)
if(!checkUserGuild(socketUser, actualGuildId)) return if(!checkUserGuild(socketUser, actualGuildId)) return
const player = new Player(actualGuildId) const player = new Player(actualGuildId)
if(!connectToPlayer(actualGuildId, player)) return IOAnswer("/SEARCH/PLAY", false) if(!connectToPlayer(actualGuildId, player)) return IOAnswer("/SEARCH/PLAY", false)
@@ -523,26 +558,20 @@ function init() {
wlog.warn("Le fichier envoyé n'est pas un fichier audio valide (MP3/WAV)") wlog.warn("Le fichier envoyé n'est pas un fichier audio valide (MP3/WAV)")
return IOAnswer("/UPLOAD/FILE", false) return IOAnswer("/UPLOAD/FILE", false)
} }
const url = await mediaBase.postMedia(data) const url = await mediaBase.postMedia(data, socketUser.identity.id)
if(!url) return IOAnswer("/UPLOAD/FILE", false) if(!url) return IOAnswer("/UPLOAD/FILE", "TOOHIGH")
IOAnswer("/UPLOAD/FILE", {"url": url, "name": data.name}) IOAnswer("/UPLOAD/FILE", {"url": url, "name": data.name})
}) })
// CHECKED : 29/05/2025 IORequest("/UPLOAD/FILES", async () => {
IORequest("/UPLOAD/FILE/GET_SONG", async (data) => { const files = await mediaBase.getAllMedia(socketUser.identity.id)
if(!data) return IOAnswer("/UPLOAD/FILE/GET_SONG", false) IOAnswer("/UPLOAD/FILES", files)
const {name, url} = data })
if(!url) return IOAnswer("/UPLOAD/FILE/GET_SONG", false)
if(!name) return IOAnswer("/UPLOAD/FILE/GET_SONG", false) IORequest("/UPLOAD/FILE/DELETE", (data) => {
const song = new Song() if(!data) return IOAnswer("/UPLOAD/FILE/DELETE", false)
if(!song) return IOAnswer("/UPLOAD/FILE/GET_SONG", false) mediaBase.deleteMedia(data, socketUser.identity.id)
await getMediaInformationFromUrl(song, url) IOAnswer("/UPLOAD/FILE/DELETE", true)
song.type = "attachment"
song.author = socketUser.identity.username
song.authorId = socketUser.identity.id
song.title = name
song.url = url
IOAnswer("/UPLOAD/FILE/GET_SONG", song)
}) })
// GOOGLE API // GOOGLE API
@@ -567,24 +596,27 @@ function init() {
// PLAYLISTS // PLAYLISTS
IORequest("/CHANNEL", () => {
// Get the channel of the bot, in actualGuildId if he is connected
const channel = getUserChannel(true)
if(!channel) return IOAnswer("/CHANNEL", false, true)
IOAnswer("/CHANNEL", channel, true)
}, true)
// CHECKED : 30/04/2025 // CHECKED : 30/04/2025
IORequest("/PLAYLISTS/CREATE", async (data) => { IORequest("/PLAYLISTS/CREATE", async (data) => {
if(!data) return IOAnswer("/PLAYLISTS/CREATE", false) if(!data) return IOAnswer("/PLAYLISTS/CREATE", false)
const {name, url} = data const {name, url} = data
if(!name) return IOAnswer("/PLAYLISTS/CREATE", false) if(!name) return IOAnswer("/PLAYLISTS/CREATE", false)
const playlist = await playlists.addPlaylist(socketUser.identity.id, name, url) const playlist = await playlists.addPlaylist(socketUser.identity.id, name, url, socketUser.identity.username, socketUser.identity.id, socketUser.identity.avatar)
if(!playlist) return IOAnswer("/PLAYLISTS/CREATE", false) if(!playlist) return IOAnswer("/PLAYLISTS/CREATE", false)
IOAnswer("/PLAYLISTS/CREATE", true) IOAnswer("/PLAYLISTS/CREATE", playlist)
}) })
// CHECKED : 30/04/2025 // CHECKED : 30/04/2025
IORequest("/PLAYLISTS/DELETE", (data) => { IORequest("/PLAYLISTS/DELETE", (data) => {
if(!data) return IOAnswer("/PLAYLISTS/DELETE", false) if(!data) return IOAnswer("/PLAYLISTS/DELETE", false)
const {name} = data playlists.removePlaylist(socketUser.identity.id, data)
if(!name) return IOAnswer("/PLAYLISTS/DELETE", false)
playlists.removePlaylist(socketUser.identity.id, name)
IOAnswer("/PLAYLISTS/DELETE", true) IOAnswer("/PLAYLISTS/DELETE", true)
}) })
@@ -614,34 +646,39 @@ function init() {
// CHECKED : 30/04/2025 // CHECKED : 30/04/2025
IORequest("/PLAYLISTS/RENAME", (data) => { IORequest("/PLAYLISTS/RENAME", (data) => {
if(!data) return IOAnswer("/PLAYLISTS/RENAME", false) if(!data) return IOAnswer("/PLAYLISTS/RENAME", false)
const {name, newName} = data const {id, newName} = data
if(!name || !newName) return IOAnswer("/PLAYLISTS/RENAME", false) if(!id || !newName) return IOAnswer("/PLAYLISTS/RENAME", false)
const playlist = playlists.getPlaylistOfUser(socketUser.identity.id, name) var playlist = playlists.getPlaylist(socketUser.identity.id, id)
if(!playlist) return IOAnswer("/PLAYLISTS/RENAME", false) if(!playlist) return IOAnswer("/PLAYLISTS/RENAME", false)
playlists.renamePlaylist(socketUser.identity.id, name, newName) playlists.renamePlaylist(socketUser.identity.id, id, newName)
IOAnswer("/PLAYLISTS/RENAME", true) playlist = playlists.getPlaylist(socketUser.identity.id, id)
IOAnswer("/PLAYLISTS/RENAME", playlist)
}) })
// CHECKED : 30/04/2025 // CHECKED : 30/04/2025
IORequest("/PLAYLISTS/ADD_SONG", (data) => { IORequest("/PLAYLISTS/ADD_SONG", (data) => {
if(!data) return IOAnswer("/PLAYLISTS/ADD_SONG", false) if(!data) return IOAnswer("/PLAYLISTS/ADD_SONG", false)
const {name, song} = data const {id, song} = data
if(!name || !song) return IOAnswer("/PLAYLISTS/ADD_SONG", false) if(!id || !song) return IOAnswer("/PLAYLISTS/ADD_SONG", false)
const playlist = playlists.getPlaylistOfUser(socketUser.identity.id, name) var playlist = playlists.getPlaylistOfUser(socketUser.identity.id, id)
if(!playlist) return IOAnswer("/PLAYLISTS/ADD_SONG", false) if(!playlist) return IOAnswer("/PLAYLISTS/ADD_SONG", false)
playlists.addSong(socketUser.identity.id, name, song) playlists.addSong(socketUser.identity.id, id, song)
IOAnswer("/PLAYLISTS/ADD_SONG", true) playlist = playlists.getPlaylistOfUser(socketUser.identity.id, id)
IOAnswer("/PLAYLISTS/ADD_SONG", playlist)
}) })
// CHECKED : 30/04/2025 // CHECKED : 30/04/2025
IORequest("/PLAYLISTS/REMOVE_SONG", (data) => { IORequest("/PLAYLISTS/REMOVE_SONG", (data) => {
if(!data) return IOAnswer("/PLAYLISTS/REMOVE_SONG", false) if(!data) return IOAnswer("/PLAYLISTS/REMOVE_SONG", false)
const {name, songId} = data const {id, songId} = data
if(!name || !songId) return IOAnswer("/PLAYLISTS/REMOVE_SONG", false) if(!id || !songId) return IOAnswer("/PLAYLISTS/REMOVE_SONG", false)
const playlist = playlists.getPlaylistOfUser(socketUser.identity.id, name) var playlist = playlists.getPlaylistOfUser(socketUser.identity.id, id)
if(!playlist) return IOAnswer("/PLAYLISTS/REMOVE_SONG", false) if(!playlist) return IOAnswer("/PLAYLISTS/REMOVE_SONG", false)
playlists.removeSong(socketUser.identity.id, name, songId)
IOAnswer("/PLAYLISTS/REMOVE_SONG", true) playlists.removeSong(socketUser.identity.id, id, songId)
playlist = playlists.getPlaylistOfUser(socketUser.identity.id, id)
IOAnswer("/PLAYLISTS/REMOVE_SONG", playlist)
}) })
// CHECKED : 05/05/2025 // CHECKED : 05/05/2025
@@ -658,6 +695,13 @@ function init() {
IOAnswer("/PLAYLISTS/PLAY", true) IOAnswer("/PLAYLISTS/PLAY", true)
}) })
IORequest("/PLAYLISTS/REFRESH", async (data) => {
if(!data) return IOAnswer("/PLAYLISTS/REFRESH", false)
const playlist = await playlists.refreshPlaylist(socketUser.identity.id, data)
if(!playlist) return IOAnswer("/PLAYLISTS/REFRESH", false)
IOAnswer("/PLAYLISTS/REFRESH", playlist)
})
// ADMIN // ADMIN
@@ -686,6 +730,7 @@ function init() {
if(socketUser.identity.id == userId) return IOAnswer("/ADMIN/USERS/SWITCH_ADMIN", false) if(socketUser.identity.id == userId) return IOAnswer("/ADMIN/USERS/SWITCH_ADMIN", false)
if(!users.getUserById(userId)) return IOAnswer("/ADMIN/USERS/SWITCH_ADMIN", false) if(!users.getUserById(userId)) return IOAnswer("/ADMIN/USERS/SWITCH_ADMIN", false)
users.setAdmin(userId) users.setAdmin(userId)
process.emit("USERS_UPDATE")
IOAnswer("/ADMIN/USERS/SWITCH_ADMIN", true) IOAnswer("/ADMIN/USERS/SWITCH_ADMIN", true)
}) })
@@ -696,6 +741,7 @@ function init() {
if(!users.getUserById(userId)) return IOAnswer("/ADMIN/USERS/FULL_BAN", false) if(!users.getUserById(userId)) return IOAnswer("/ADMIN/USERS/FULL_BAN", false)
if(users.getUserById(userId).isAdmin()) return IOAnswer("/ADMIN/USERS/FULL_BAN", false) if(users.getUserById(userId).isAdmin()) return IOAnswer("/ADMIN/USERS/FULL_BAN", false)
users.setFullBan(userId) users.setFullBan(userId)
process.emit("USERS_UPDATE")
IOAnswer("/ADMIN/USERS/FULL_BAN", true) IOAnswer("/ADMIN/USERS/FULL_BAN", true)
}) })
@@ -735,45 +781,140 @@ function init() {
// CHECKED : 24/04/2025 // CHECKED : 24/04/2025
IORequest("/OWNER/USERS/SWITCH_MOD", (userId) => { IORequest("/OWNER/USERS/SWITCH_MOD", async (userId) => {
if(userId || actualGuildId) return IOAnswer("/OWNER/USERS/SWITCH_MOD", false) if(!userId || !actualGuildId) return IOAnswer("/OWNER/USERS/SWITCH_MOD", false)
if(socketUser.identity.id == userId) return IOAnswer("/OWNER/USERS/SWITCH_MOD", false) if(socketUser.identity.id == userId) return IOAnswer("/OWNER/USERS/SWITCH_MOD", false)
if(!socketUser.isOwner(actualGuildId)) return IOAnswer("/OWNER/USERS/SWITCH_MOD", false) const guild = discordBot.getGuilds().get(actualGuildId)
users.setGuildMod(userId, actualGuildId) if(!socketUser.identity.id === guild.owner) return IOAnswer("/OWNER/USERS/SWITCH_MOD", false)
if(userId == guild.owner) return IOAnswer("/OWNER/USERS/SWITCH_MOD", false)
await users.setGuildMod(userId, actualGuildId)
await process.emit("USERS_UPDATE")
IOAnswer("/OWNER/USERS/SWITCH_MOD", true) IOAnswer("/OWNER/USERS/SWITCH_MOD", true)
}) })
IORequest("/OWNER/ROLES/GET", async () => {
if(!actualGuildId) return IOAnswer("/OWNER/ROLES/GET", false)
const guild = discordBot.getGuilds().get(actualGuildId)
if(!socketUser.identity.id === guild.owner) return IOAnswer("/OWNER/ROLES/GET", false)
const rolesSecure = discordBot.getGuildRoles(actualGuildId)
const actualRole = await serverSettings.getSecureRole(actualGuildId)
// Move the actual role at the start of the array
if(actualRole) {
const index = rolesSecure.findIndex(r => r.id === actualRole.id)
if(index > -1) {
rolesSecure.unshift(rolesSecure.splice(index, 1)[0])
}
} else {
// Find the @everyone role and put it at the start of the array
const index = rolesSecure.findIndex(r => r.name === "@everyone")
if(index > -1) {
rolesSecure.unshift(rolesSecure.splice(index, 1)[0])
}
}
IOAnswer("/OWNER/ROLES/GET", await rolesSecure)
})
IORequest("/OWNER/ROLES/SET", async (data) => {
if(!actualGuildId) return IOAnswer("/OWNER/ROLES/SET", false)
const guild = discordBot.getGuilds().get(actualGuildId)
if(!socketUser.identity.id === guild.owner) return IOAnswer("/OWNER/ROLES/SET", false)
await serverSettings.setSecureRole(actualGuildId, data)
IOAnswer("/OWNER/ROLES/SET", true)
})
// CHECKED : 24/04/2025 // CHECKED : 24/04/2025
IORequest("/MOD/USERS/BAN", (userId) => { IORequest("/MOD/USERS/BAN", async (userId) => {
if(userId || actualGuildId) return IOAnswer("/MOD/USERS/BAN", false) if(!userId || !actualGuildId) return IOAnswer("/MOD/USERS/BAN", false)
const guild = discordBot.getGuilds().get(actualGuildId)
if(!guild) return IOAnswer("/MOD/USERS/BAN", false)
if(!socketUser.isMod(actualGuildId) && socketUser.identity.id !== guild.owner) return IOAnswer("/MOD/USERS/BAN", false)
if(socketUser.identity.id == userId) return IOAnswer("/MOD/USERS/BAN", false) if(socketUser.identity.id == userId) return IOAnswer("/MOD/USERS/BAN", false)
if(!socketUser.isMod(actualGuildId)) return IOAnswer("/MOD/USERS/BAN", false) const user = users.getUserById(userId)
users.setGuildBan(userId, actualGuildId) if(user.isMod(actualGuildId) || user.identity.id == guild.owner) return IOAnswer("/MOD/USERS/BAN", false)
await users.setGuildBan(userId, actualGuildId)
// If user is connected, disconnect him
const userSocket = UsersBySocket.get(userId)
if(userSocket) {
const socket = io.sockets.sockets.get(userSocket)
if(socket) {
removeGuildConnectedUser(userId)
if(socket.rooms.has(actualGuildId)) {
socket.leave(actualGuildId)
}
}
}
await process.emit("USERS_UPDATE")
IOAnswer("/MOD/USERS/BAN", true) IOAnswer("/MOD/USERS/BAN", true)
}) })
IORequest("/MOD/USERS/LIST", () => {
if(!actualGuildId) return IOAnswer("/MOD/USERS/LIST", false)
const guild = discordBot.getGuilds().get(actualGuildId)
if(!guild) return IOAnswer("/MOD/USERS/LIST", false)
if(!socketUser.isMod(actualGuildId) && socketUser.identity.id !== guild.owner) return IOAnswer("/MOD/USERS/LIST", false)
if(!checkUserGuild(socketUser, actualGuildId)) return IOAnswer("/MOD/USERS/LIST", false)
const guildUserList = new Array()
for(var user of guild.allMembers) {
const userData = users.getUserById(user)
if(!userData) continue
if(userData.labels.includes("DELETED")) continue
userData.identity['isAdmin'] = userData.isAdmin()
userData.identity['isMod'] = userData.isMod(actualGuildId)
userData.identity['isOwner'] = userData.identity.id == guild.owner
guildUserList.push({
identity: userData.identity,
isBanned: userData.isBanned(actualGuildId),
})
}
IOAnswer("/MOD/USERS/LIST", guildUserList, true)
}, true)
// UTILS // UTILS
// CHECKED : 24/04/2025 // CHECKED : 24/04/2025
IORequest("/REPORT", (data) => { IORequest("/REPORT", (data) => {
if(data.length < 2) return IOAnswer("/REPORT", false) if(!data) return IOAnswer("/REPORT", false)
if(!data["level"] || !data["desc"]) return IOAnswer("/REPORT", false) if(!data["level"] || !data["desc"]) return IOAnswer("/REPORT", false)
const report = new Report(socketUser.identity.username, data["level"], data["desc"]).send() const report = new Report(socketUser.identity.username, data["level"], data["desc"]).send()
IOAnswer("/REPORT", true) IOAnswer("/REPORT", true)
}) })
IORequest("/MOD/STATS", () => {
if(!actualGuildId) return IOAnswer("/MOD/USERS/LIST", false)
const guild = discordBot.getGuilds().get(actualGuildId)
if(!guild) return IOAnswer("/MOD/USERS/LIST", false)
if(!socketUser.isMod(actualGuildId) && socketUser.identity.id !== guild.owner) return IOAnswer("/MOD/USERS/LIST", false)
if(!checkUserGuild(socketUser, actualGuildId)) return IOAnswer("/MOD/USERS/LIST", false)
const metrics = JSON.parse(fs.readFileSync(__glob.METRIC_FILE))
const metricsToSend = new Array()
for(var metric of metrics) {
if(metric.name.includes(actualGuildId)) {
metricsToSend.push(metric)
}
}
IOAnswer("/MOD/STATS", metricsToSend, true)
}, true)
// Functions // Functions
function getUserChannel() { function getUserChannel(usual, force) {
const botChannel = getBotChannel()
if(botChannel && !force) {
return botChannel
}
const membersVoices = discordBot.getMembersVoices() const membersVoices = discordBot.getMembersVoices()
const member = membersVoices.get(socketUser.identity.id) const member = membersVoices.get(socketUser.identity.id)
if(member) { if(member) {
const channelId = member.channelId const channelId = member.channelId
const guildId = member.guildId const guildId = member.guildId
if(guildId != actualGuildId) {
if(!usual) wlog.warn("La guilde active : " + actualGuildId + " ne correspond pas à la guilde du channel vocal : " + guildId + " de l'utilisateur '" + socketUser.identity.username + "'")
return null
}
const channel = discordBot.getChannel(guildId, channelId) const channel = discordBot.getChannel(guildId, channelId)
if(!channel) { if(!channel) {
wlog.warn("Le channel vocal n'existe pas : " + channelId) wlog.warn("Le channel vocal n'existe pas : " + channelId)
@@ -781,11 +922,81 @@ function init() {
} }
return channel return channel
} else { } else {
wlog.warn("L'utilisateur '" + socketUser.identity.username + "' n'est pas dans un channel vocal") if(!usual) wlog.warn("L'utilisateur '" + socketUser.identity.username + "' n'est pas dans un channel vocal")
return null return null
} }
} }
function getUserGuilds() {
var guildPresents = new Array();
var guildsOfBot = discordBot.getGuilds()
for(var guild of guildsOfBot) {
if(guild[1].allMembers.includes(socketUser.identity.id)) {
const guildData = guild[1]
guildData['members'] = new Array()
guildData.serverMember = guild[1].allMembers.length
guildData.restricted = false
const secureRole = serverSettings.getSecureRole(guild[0])
if(secureRole && socketUser.identity.id !== discordBot.getGuilds().get(guild[0]).owner) {
const member = discordBot.getGuildMember(guild[0], socketUser.identity.id)
if(!member.roles.cache.has(secureRole.id) && socketUser.identity.id !== guild[1].owner && !socketUser.isAdmin()) {
guildData.restricted = true
} else {
guildData.restricted = false
}
}
guildData.allowed = true
for(var user of guildConnectedUsers.get(guild[0]) || []) {
const userData = users.getUserById(user.id)
if(userData && userData.identity.id != socketUser.identity.id) {
let infos = {
id: userData.identity.id,
global_name: userData.identity.global_name,
username: userData.identity.username,
avatar: userData.identity.avatar,
isAdmin: userData.isAdmin(),
isOwner: userData.identity.id == guild[1].owner,
isMod: userData.isMod(guild[0]),
}
// If it's already in guildData.members, skip
if(guildData.members.find(m => m.id == infos.id)) continue
guildData.members.push(infos)
}
}
// Send if the bot is connected to the guild
if(players.getPlayer(guild[0]) && players.getPlayer(guild[0]).isConnected()) {
guildData.connected = true
} else {
guildData.connected = false
}
// Leave the room if the user is not in the guild
if(socket.rooms.has(guild[0]) && !checkUserGuild(socketUser, guild[0])) {
socket.leave(guild[0])
removeGuildConnectedUser(socketUser.identity.id)
wlog.warn("L'utilisateur '" + socketUser.identity.username + "' quitte la room de la guilde : " + guild[0] + " car il n'est pas dans la guilde) /!\\")
}
guildPresents.push(guildData)
}
}
return guildPresents
}
function getBotChannel() {
const playersList = players.getAllPlayers()
if(!playersList || playersList.length == 0) return null
const player = playersList.find(p => p.isConnected() && p.guildId == actualGuildId)
if(player) {
const channel = discordBot.getChannel(actualGuildId, player.channelId)
if(channel) {
return channel
}
}
return null
}
/** /**
* @param {Player} player * @param {Player} player
*/ */
@@ -815,6 +1026,15 @@ function init() {
wlog.warn("Aucun guildId n'est actif pour l'utilisateur : " + socketUser.identity.username) wlog.warn("Aucun guildId n'est actif pour l'utilisateur : " + socketUser.identity.username)
return false return false
} }
// Check role if secure role is set
const secureRole = serverSettings.getSecureRole(guildId)
if(secureRole && socketUser.identity.id !== discordBot.getGuilds().get(guildId).owner) {
const member = discordBot.getGuildMember(guildId, socketUser.identity.id)
if(member.roles.cache.has(secureRole.id) == false) {
wlog.warn("L'utilisateur '" + socketUser.identity.username + "' n'a pas le rôle requis pour accéder à la guilde : " + guildId)
return false
}
}
// Check if the guildId is referenced in the bot guilds // Check if the guildId is referenced in the bot guilds
if(!discordBot.getGuilds().has(guildId)) { if(!discordBot.getGuilds().has(guildId)) {
wlog.warn("La guilde : " + guildId + " n'est pas référencée dans le bot") wlog.warn("La guilde : " + guildId + " n'est pas référencée dans le bot")
@@ -864,7 +1084,7 @@ function init() {
if(socketUser) { if(socketUser) {
wlog.log("Déconnexion de l'utilisateur : " + socketUser.identity.username + " (" + socketUser.identity.id + ") - Socket : " + socket.id) wlog.log("Déconnexion de l'utilisateur : " + socketUser.identity.username + " (" + socketUser.identity.id + ") - Socket : " + socket.id)
allConnectedUsers.splice(allConnectedUsers.indexOf(socketUser.identity), 1) allConnectedUsers.splice(allConnectedUsers.indexOf(socketUser.identity), 1)
removeGuildConnectedUser(socketUser.identity) removeGuildConnectedUser(socketUser.identity.id)
process.emit("USERS_UPDATE") process.emit("USERS_UPDATE")
// Remove every rooms include admin // Remove every rooms include admin
socket.rooms.forEach((room) => { socket.rooms.forEach((room) => {
@@ -882,16 +1102,19 @@ function init() {
} }
function IORequest(RequestName, RequestCallback) { function IORequest(RequestName, RequestCallback, usual) {
socket.on(RequestName, (value) => { socket.on(RequestName, (value) => {
if(!usual) {
wlog.log(socketUser.identity.username + " - Socket : " + socket.id + " - " + RequestName + " - [RECIEVED]") wlog.log(socketUser.identity.username + " - Socket : " + socket.id + " - " + RequestName + " - [RECIEVED]")
}
RequestCallback(value) RequestCallback(value)
}) })
} }
function IOAnswer(AnswerName, AnswerValue) { function IOAnswer(AnswerName, AnswerValue, usual) {
if(!usual) {
wlog.log(socketUser.identity.username + " - Socket : " + socket.id + " - " + AnswerName + " - [ANSWERED]") wlog.log(socketUser.identity.username + " - Socket : " + socket.id + " - " + AnswerName + " - [ANSWERED]")
}
socket.emit(AnswerName, AnswerValue) socket.emit(AnswerName, AnswerValue)
} }
@@ -907,27 +1130,29 @@ function init() {
if(!guildConnectedUsers.has(guildId)) { if(!guildConnectedUsers.has(guildId)) {
guildConnectedUsers.set(guildId, new Array()) guildConnectedUsers.set(guildId, new Array())
} }
const users = guildConnectedUsers.get(guildId)
if(users.includes(user)) { if(guildConnectedUsers.get(guildId).includes(user)) {
wlog.warn("L'utilisateur '" + user.username + "' est déjà connecté à la guilde : " + guildId) wlog.warn("L'utilisateur '" + user.username + "' est déjà connecté à la guilde : " + guildId)
return return
} } else {
guildConnectedUsers.get(guildId).push(user) guildConnectedUsers.get(guildId).push(user)
} }
}
function removeGuildConnectedUser(user) {
function removeGuildConnectedUser(userId) {
for(var guild of guildConnectedUsers.keys()) { for(var guild of guildConnectedUsers.keys()) {
const users = guildConnectedUsers.get(guild) const users = guildConnectedUsers.get(guild)
if(users.includes(user)) { const userIndex = users.findIndex(u => u.id == userId)
users.splice(users.indexOf(user), 1) if(userIndex != -1) {
if(users.length == 0) { users.splice(userIndex, 1)
guildConnectedUsers.delete(guild)
}
} }
} }
} }
} }

View File

@@ -145,15 +145,8 @@ class User {
return this.labels.includes("ADMIN"); return this.labels.includes("ADMIN");
} }
isMod(guildId) { isMod(guildId) {
if(this.isOwner(guildId)) return true;
const modLabel = `MOD_${guildId}`; const modLabel = `MOD_${guildId}`;
return this.labels.includes(modLabel); return this.labels.includes(modLabel) || this.isAdmin();
}
isOwner(guildId) {
const ownerLabel = `OWNER_${guildId}`;
if(this.isAdmin()) return true;
return this.labels.includes(ownerLabel);
} }
justUpdated() { justUpdated() {
@@ -241,6 +234,10 @@ async function updateIdentity(id) {
clog.warn(`Utilisateur ${id} non trouvé.`); clog.warn(`Utilisateur ${id} non trouvé.`);
return null; return null;
} }
if(user.labels.includes("DELETED")) {
clog.warn(`L'utilisateur ${user.identity.username} (${user.identity.id}) est marqué comme supprimé, il ne peut pas être mis à jour.`);
return null;
}
clog.log(`Mise à jour de l'identité de l'utilisateur ${user.identity.username} (${user.identity.id})...`); clog.log(`Mise à jour de l'identité de l'utilisateur ${user.identity.username} (${user.identity.id})...`);
if (user.auth) { if (user.auth) {
const identity = await discordAuth.getUserIdentity(user.auth); const identity = await discordAuth.getUserIdentity(user.auth);
@@ -284,6 +281,10 @@ async function addUser(auth, identity) {
const existingUser = userList.find(user => user.identity.id === identity.id); const existingUser = userList.find(user => user.identity.id === identity.id);
if (existingUser) { if (existingUser) {
clog.warn(`L'utilisateur ${identity.username} existe déjà.`); clog.warn(`L'utilisateur ${identity.username} existe déjà.`);
if(existingUser.labels.includes("DELETED")) {
clog.warn(`L'utilisateur ${identity.username} est marqué comme supprimé, il sera réactivé.`);
existingUser.labels = existingUser.labels.filter(label => label !== "DELETED");
}
// Update the existing user with new information // Update the existing user with new information
existingUser.auth = auth; existingUser.auth = auth;
existingUser.identity = identity; existingUser.identity = identity;
@@ -391,88 +392,88 @@ function getSimpleUser(id) {
// SET LABELS // SET LABELS
function setAdmin(id) { async function setAdmin(id) {
const user = getUserById(id); const user = getUserById(id);
if (user) { if (user) {
user.setAdmin(); await user.setAdmin();
saveUsers(); await saveUsers();
} else { } else {
clog.warn(`Utilisateur ${id} non trouvé.`); clog.warn(`Utilisateur ${id} non trouvé.`);
} }
} }
function setGuildMod(id, guildId) { async function setGuildMod(id, guildId) {
const user = getUserById(id); const user = getUserById(id);
if (user) { if (user) {
const modLabel = `MOD_${guildId}`; const modLabel = `MOD_${guildId}`;
if (user.labels.includes(modLabel)) { if (user.labels.includes(modLabel)) {
user.labels.splice(user.labels.indexOf(modLabel), 1); await user.labels.splice(user.labels.indexOf(modLabel), 1);
clog.log(`L'utilisateur ${user.identity.username} n'est plus modérateur du serveur ${guildId}.`); clog.log(`L'utilisateur ${user.identity.username} n'est plus modérateur du serveur ${guildId}.`);
} else { } else {
user.labels.push(modLabel); await user.labels.push(modLabel);
clog.log(`L'utilisateur ${user.identity.username} est maintenant modérateur du serveur ${guildId}.`); clog.log(`L'utilisateur ${user.identity.username} est maintenant modérateur du serveur ${guildId}.`);
} }
saveUsers(); await saveUsers();
} else { } else {
clog.warn(`Utilisateur ${id} non trouvé.`); clog.warn(`Utilisateur ${id} non trouvé.`);
} }
} }
function setGuildBan(id, guildId) { async function setGuildBan(id, guildId) {
const user = getUserById(id); const user = getUserById(id);
if (user) { if (user) {
if(user.isAdmin()) { if(user.isAdmin()) {
clog.warn(`L'utilisateur ${user.identity.username} est admin, il ne peut pas être banni.`); clog.warn(`L'utilisateur ${user.identity.username} est admin, il ne peut pas être banni.`);
return; return;
} }
user.setBan(guildId); await user.setBan(guildId);
saveUsers(); await saveUsers();
} else { } else {
clog.warn(`Utilisateur ${id} non trouvé.`); clog.warn(`Utilisateur ${id} non trouvé.`);
} }
} }
function setFullBan(id) { async function setFullBan(id) {
const user = getUserById(id); const user = getUserById(id);
if (user) { if (user) {
if(user.isAdmin()) { if(user.isAdmin()) {
clog.warn(`L'utilisateur ${user.identity.username} est admin, il ne peut pas être banni.`); clog.warn(`L'utilisateur ${user.identity.username} est admin, il ne peut pas être banni.`);
return; return;
} }
user.setFullBan(); await user.setFullBan();
saveUsers(); await saveUsers();
} else { } else {
clog.warn(`Utilisateur ${id} non trouvé.`); clog.warn(`Utilisateur ${id} non trouvé.`);
} }
} }
function setGuildOwner(id, guildId, force) {
function deleteAccount(id) {
const user = getUserById(id); const user = getUserById(id);
if (user) { if (user) {
const ownerLabel = `OWNER_${guildId}`; user.labels = user.labels.filter(label => label.includes('BAN'));
if (user.labels.includes(ownerLabel) && !force) { user.labels.push('DELETED'); // Add a deleted label
user.labels.splice(user.labels.indexOf(ownerLabel), 1); user.tokens = []; // Clear tokens
clog.log(`L'utilisateur ${user.identity.username} n'est plus propriétaire du serveur ${guildId}.`); user.auth = null; // Clear authentication
} else { user.identity = { id: user.identity.id, username: user.identity.username }; // Keep only identity information
if(force && user.labels.includes(ownerLabel)) {
return;
}
user.labels.push(ownerLabel);
clog.log(`L'utilisateur ${user.identity.username} est maintenant propriétaire du serveur ${guildId}.`);
}
saveUsers(); saveUsers();
clog.log(`Suppression du compte de l'utilisateur ${user.identity.username}.`);
} else { } else {
clog.warn(`Utilisateur ${id} non trouvé.`); clog.warn(`Utilisateur ${id} non trouvé.`);
} }
} }
// USERS DB // USERS DB
function loadUsers() { function loadUsers() {
UserDB.load() UserDB.load()
userList = new Array(); userList = new Array();
for (const user of UserDB.getData()) { for (const user of UserDB.getData()) {
if(user?.labels?.includes("DELETED")) {
clog.log(`Utilisateur ${user.identity.id} marqué comme supprimé, ignoré.`);
userList.push(new User(null, user.identity, [], user.labels));
continue; // Skip deleted users
}
userList.push(new User(user.auth, user.identity, user.tokens, user.labels)); userList.push(new User(user.auth, user.identity, user.tokens, user.labels));
} }
clog.log(`Chargement de ${userList.length} utilisateurs.`); clog.log(`Chargement de ${userList.length} utilisateurs.`);
@@ -506,7 +507,6 @@ function clearNeedUpdateForUsers() {
module.exports = {User} module.exports = {User}
module.exports = { module.exports = {
addUser, addUser,
setGuildOwner,
setFullBan, setFullBan,
removeUser, removeUser,
getUserByToken, getUserByToken,
@@ -522,5 +522,6 @@ module.exports = {
updateCredientials, updateCredientials,
refreshAllUserInformation, refreshAllUserInformation,
updateIdentity, updateIdentity,
clearNeedUpdateForUsers clearNeedUpdateForUsers,
deleteAccount
}; };

View File

@@ -1,5 +1,6 @@
const path = require("path") const path = require("path")
const root = path.resolve(__dirname, '../../') const root = path.resolve(__dirname, '../../')
const version = JSON.parse(require('fs').readFileSync(root + path.sep + "package.json", "utf-8")).version
const __glob = { const __glob = {
PACKAGEINFO: root + path.sep + "package.json", PACKAGEINFO: root + path.sep + "package.json",
@@ -7,13 +8,18 @@ const __glob = {
SRC: root + path.sep + "src", SRC: root + path.sep + "src",
LOGS: root + path.sep + "logs", LOGS: root + path.sep + "logs",
DATA: root + path.sep + "data", DATA: root + path.sep + "data",
COMMANDS: root + path.sep + "src" + path.sep + "discord" + path.sep + "commands", COMMANDS: root + path.sep + "src" + path.sep + "discord" + path.sep + "Commands",
METRIC_FILE: root + path.sep + "data" + path.sep + "metrics.json", METRIC_FILE: root + path.sep + "data" + path.sep + "metrics.json",
PREVIOUSFILE: root + path.sep + "data" + path.sep + "previous.json", PREVIOUSFILE: root + path.sep + "data" + path.sep + "previous.json",
USERFILE: root + path.sep + "data" + path.sep + "users.json", USERFILE: root + path.sep + "data" + path.sep + "users.json",
PLAYLISTFILE: root + path.sep + "data" + path.sep + "playlists.json", PLAYLISTFILE: root + path.sep + "data" + path.sep + "playlists.json",
HISTORY_DB: root + path.sep + "data" + path.sep + "history.json", HISTORY_DB: root + path.sep + "data" + path.sep + "history.json",
MEDIA_DB: root + path.sep + "data" + path.sep + "media.json", MEDIA_DB: root + path.sep + "data" + path.sep + "media.json",
SERVER_DB: root + path.sep + "data" + path.sep + "servers.json",
VERSION: version,
CHANGELOG_PATH: root + path.sep + "CHANGELOG.html",
COOKIES: root + path.sep + "data" + path.sep + "cookies.json",
PROXY: root + path.sep + "data" + path.sep + "proxy.json"
} }
module.exports = {__glob} module.exports = {__glob}