Compare commits

...

2 Commits

Author SHA1 Message Date
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
18 changed files with 1852 additions and 1282 deletions

View File

@@ -0,0 +1,61 @@
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
ssh-keyscan raphix.fr >> ~/.ssh/known_hosts
- name: Deploy Subsonics
run: |
ssh -o StrictHostKeyChecking=no raphix@raphix.fr << 'EOF'
sudo su - gitlab-ci << 'INNER_EOF'
set -e
echo "[Subsonics-Deploy] - Stage - Déploiement - START "
echo "[Subsonics-Deploy] - Arrêt de Subsonics : Processing"
cd /home/gitlab-ci
whoami
pm2 stop "Subsonics" || true
pm2 delete "Subsonics" || true
echo "[Subsonics-Deploy] - Arrêt de Subsonics : Success"
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.git
echo "[Subsonics-Deploy] - Installation de Subsonics : Success"
echo "[Subsonics-Deploy] - Installation des dépendances : Processing"
cd /home/gitlab-ci/backend
rm -r /home/gitlab-ci/backend/data || true
mv /home/gitlab-ci/tempdata/ /home/gitlab-ci/backend/data || true
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"
INNER_EOF
EOF

13
CHANGELOG.html Normal file
View File

@@ -0,0 +1,13 @@
<div class="changelog-version changelog-actual">
<h2>Chopin - Version /*1.0.0*/</h2>
<p class="changelog-date">*_Date de sortie_*: *-XX/XX/XXXX-*</p>
<ul>
<li>[FRONTEND] Sortie de la version 1.0.0 de Chopin, le bot Discord pour SubSonics.</li>
<li>[BACKEND] Ajout de la fonctionnalité de gestion des utilisateurs avec un fichier JSON.</li>
<li>[DISCORD] Ajout de la fonctionnalité de gestion des utilisateurs avec un fichier JSON.</li>
<li>[PLAYER] Ajout de la fonctionnalité de gestion des utilisateurs avec un fichier JSON.</li>
<li>[AUTRE] Ajout de la fonctionnalité de gestion des utilisateurs avec un fichier JSON.</li>
<li>[FIX] Ajout de la fonctionnalité de gestion des utilisateurs avec un fichier JSON.</li>
<li>[AJOUT] Ajout de la fonctionnalité de gestion des utilisateurs avec un fichier JSON.</li>
</ul>
</div>

2210
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -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

@@ -83,11 +83,12 @@ function init() {
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)
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)
@@ -169,7 +170,7 @@ function init() {
} }
process.emit("VOCAL_UPDATE")
}) })

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

@@ -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

@@ -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")
} }
@@ -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")
@@ -33,7 +34,7 @@ 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 //TODO: Separate all request in separate files
function init() { function init() {
@@ -44,16 +45,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 +63,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 +77,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 +94,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 +131,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 +223,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 +247,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 +263,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 +309,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
@@ -409,10 +444,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 +459,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 +467,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 +489,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 +513,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 +559,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 +597,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 +647,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 +696,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 +731,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 +742,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 +782,111 @@ 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)
}) })
// 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) {
const botChannel = getBotChannel()
if(botChannel) {
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 +894,69 @@ 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
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
*/ */
@@ -864,7 +1035,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 +1053,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 +1081,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",
@@ -14,6 +15,8 @@ const __glob = {
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",
VERSION: version,
CHANGELOG_PATH: root + path.sep + "CHANGELOG.html",
} }
module.exports = {__glob} module.exports = {__glob}