Version 1.1.0 - Refactor + Intergration Backend

This commit is contained in:
2025-07-25 17:56:30 +02:00
parent a59d7a66db
commit 98cdae97c0
58 changed files with 244 additions and 70 deletions

View File

@@ -24,7 +24,7 @@
"libsodium-wrappers": "^0.7.15",
"loguix": "^1.4.2",
"mime-types": "^3.0.1",
"nodemon": "^3.1.9",
"nodemon": "^3.1.10",
"pm2": "^5.4.3",
"socket.io": "^4.8.1",
"soundcloud.ts": "^0.6.3",
@@ -4544,9 +4544,9 @@
}
},
"node_modules/nodemon": {
"version": "3.1.9",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz",
"integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==",
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz",
"integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==",
"license": "MIT",
"dependencies": {
"chokidar": "^3.5.2",

View File

@@ -35,7 +35,7 @@
"libsodium-wrappers": "^0.7.15",
"loguix": "^1.4.2",
"mime-types": "^3.0.1",
"nodemon": "^3.1.9",
"nodemon": "^3.1.10",
"pm2": "^5.4.3",
"socket.io": "^4.8.1",
"soundcloud.ts": "^0.6.3",

View File

@@ -6,15 +6,18 @@ const { LogType } = require("loguix")
const config = require("../utils/Database/Configuration")
const metric = require("webmetrik")
const { Player } = require("../player/Player")
const {refreshAllUserInformation} = require("../server/auth/User")
const {refreshAllUserInformation, clearNeedUpdateForUsers} = require("../server/auth/User")
const dlog = new LogType("Discord")
const glog = new LogType("GuildUpdater")
dlog.log("Initialisation du Bot Discord")
const membersVoices = new Map()
const timers = new Map()
const guilds = new Map()
var operational = false
const client = new Client({
intents:[GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildMembers],
})
@@ -33,6 +36,19 @@ function getMembersVoices() {
return membersVoices
}
function isReady() {
return operational
}
function getGuildMembers(guildId) {
const guild = client.guilds.cache.get(guildId)
if(!guild) {
dlog.error("Guild not found: " + guildId)
return []
}
return guild.members.cache.map(member => member.user.id)
}
function getChannel(guildId, channelId) {
return client.guilds.cache.get(guildId).channels.cache.get(channelId)
}
@@ -40,40 +56,45 @@ function getChannel(guildId, channelId) {
function init() {
client.once('ready', async () => {
dlog.log("Connexion au Bot Discord réussi ! Connecté en tant que : " + client.user.tag)
dlog.log("Connexion au Bot Discord réussi ! Connecté en tant que : " + client.user.tag)
// Add all guilds to the guilds map
await client.guilds.cache.forEach(async guild => {
var guildMember = await guild.members.fetch()
guildMember = guildMember.map(member => member.user.id)
await guilds.set(guild.id, {
id: guild.id,
name: guild.name,
members: guildMember,
})
})
refreshAllUserInformation()
const Activity = require("./Activity")
Activity.idleActivity()
const CommandUpdater = require("./CommandUpdater")
CommandUpdater.init()
const commandManager = client.application.commands;
if (!commandManager) {
dlog.error('Command manager not available.');
} else {
commandManager.set([]);
for (const guild of client.guilds.cache.values()) {
const missingPermissions = checkRequiredPermission(guild.members.me)
if (missingPermissions.length > 0) {
dlog.error("Le bot n'a pas les permissions nécessaires pour rejoindre la guilde : " + guild.name)
dlog.error("Permissions manquantes : " + missingPermissions.join(", "))
await guild.leave()
continue
}
dlog.step.end("d_init")
});
var guildMember = await guild.members.fetch()
guildMember = guildMember.map(member => member.user.id)
guilds.set(guild.id, {
id: guild.id,
name: guild.name,
members: guildMember,
})
glog.log("Guilde instanciée (démarrage) : " + guild.name + " (" + guild.id + ")")
}
await refreshAllUserInformation()
const Activity = require("./Activity")
Activity.idleActivity()
const CommandUpdater = require("./CommandUpdater")
CommandUpdater.init()
const commandManager = client.application.commands
if (!commandManager) {
dlog.error('Command manager not available.')
} else {
commandManager.set([])
}
dlog.step.end("d_init")
operational = true
})
client.on("interactionCreate", (interaction) => {
@@ -100,13 +121,36 @@ function init() {
})
// If a new guild is added, we will add it to the guilds map
client.on("guildCreate", (guild) => {
dlog.log("Nouvelle guilde ajoutée : " + guild.name)
guilds.set(guild.id, {
id: guild.id,
name: guild.name,
members: guild.members.cache.map(member => member.user.username),
})
client.on("guildCreate", async (guild) => {
const guildMember = guild.members.cache.get(client.user.id);
if (guildMember) {
const missingPermissions = checkRequiredPermission(guildMember)
if(missingPermissions.length > 0) {
dlog.error("Le bot n'a pas les permissions nécessaires pour rejoindre la guilde : " + guild.name)
guild.leave()
return
}
dlog.log("Nouvelle guilde ajoutée : " + guild.name)
var allMembersOfGuild = await guild.members.fetch()
allMembersOfGuild = allMembersOfGuild.map(member => member.user.id)
guilds.set(guild.id, {
id: guild.id,
name: guild.name,
members: allMembersOfGuild,
})
glog.log("Guilde ajoutée : " + guild.name + " (" + guild.id + ")")
clearNeedUpdateForUsers()
process.emit("USERS_UPDATE")
}
})
client.on("guildDelete", (guild) => {
dlog.log("Guilde supprimée : " + guild.name)
guilds.delete(guild.id)
glog.log("Guilde supprimée : " + guild.name + " (" + guild.id + ")")
clearNeedUpdateForUsers()
process.emit("USERS_UPDATE")
})
client.on("voiceStateUpdate", (oldMember, newMember) => {
@@ -152,6 +196,25 @@ function init() {
client.login(config.getToken())
}
module.exports = {init, getClient, getGuilds, getMembersVoices, getChannel}
function checkRequiredPermission(guildMember) {
const requiredPermissions = [
'CreateInstantInvite', 'AddReactions',
'Stream', 'ViewChannel',
'SendMessages', 'SendTTSMessages',
'EmbedLinks', 'AttachFiles',
'ReadMessageHistory', 'UseExternalEmojis',
'Connect', 'Speak',
'UseVAD', 'ChangeNickname',
'UseApplicationCommands', 'RequestToSpeak',
'CreatePublicThreads', 'CreatePrivateThreads',
'UseExternalStickers', 'SendMessagesInThreads',
'UseEmbeddedActivities', 'UseSoundboard',
'UseExternalSounds', 'SendVoiceMessages',
'SendPolls', 'UseExternalApps'
]
return requiredPermissions.filter(permission => !guildMember.permissions.has(permission));
}
module.exports = {init, getClient, getGuilds, getMembersVoices, getChannel, getGuildMembers, isReady}

View File

@@ -22,7 +22,12 @@ if(!config.getMediaGuildId() || !config.getMediaChannelId()) {
var channel = null
discordBot.getClient().on("ready", () => {
channel = discordBot.getChannel(config.getMediaGuildId(), config.getMediaChannelId())
try {
channel = discordBot.getChannel(config.getMediaGuildId(), config.getMediaChannelId())
} catch (e) {
}
if(!channel) {
wlog.warn("Le canal multimédia n'existe pas, vérifiez le fichier de configuration.")
wlog.step.error("init_db","Impossible d'initialiser la base de données multimédia, vérifiez le fichier de configuration.")

View File

@@ -240,7 +240,9 @@ class Player {
process.emit("PLAYERS_UPDATE")
return true
}
const { LogType } = require('loguix')
}
async leave() {

View File

@@ -1,6 +1,7 @@
const {LogType} = require('loguix')
const wlog = new LogType("Server")
const fs = require("fs")
const path = require("path")
const {Server} = require('socket.io')
const {createServer} = require('http')
@@ -12,7 +13,7 @@ const discordBot = require("../discord/Bot")
const discordAuth = require("../server/auth/DiscordAuth")
const {Report} = require("../discord/ReportSender")
const Finder = require("../player/Finder")
const fs = require("fs")
const {__glob} = require("../utils/GlobalVars")
const playlists = require("../playlists/PlaylistManager")
const history = require("../playlists/History")
@@ -32,6 +33,8 @@ const allConnectedUsers = new Array()
const guildConnectedUsers = 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() {
wlog.step.init("server_init", "Initialisation du serveur Socket.IO")
@@ -60,6 +63,7 @@ function init() {
process.on("USERS_UPDATE", () => {
if(io) {
updateGuildConnectedUsers()
// Get all players and send them to client subscribed to the guild
for(var guild of discordBot.getGuilds().keys()) {
if(guildConnectedUsers.has(guild)) {
@@ -68,17 +72,17 @@ function init() {
wlog.log("Envoi de la liste des utilisateurs connectés (" + guildConnectedUsers.get(guild).length +") à la guilde : " + guild + " à tous les utilisateurs connectés")
}
}
io.sockets.emit("/USER/READY")
}
})
io.on("connection", async (socket) => {
var socketUser;
// Make sure Discord Bot is loaded and make an interruption until it is loaded
while(!discordBot.getClient().isReady()) {
wlog.warn("Attente de traitement : "+ socket.id + " : Le bot Discord n'est pas encore chargé, attente de 3 seconde... (Avoid Rate Limit)")
await new Promise(resolve => setTimeout(resolve, 3000))
while(!await discordBot.isReady()) {
wlog.warn("Attente de traitement : "+ socket.id + " : Le bot Discord n'est pas encore chargé, attente de 0.5 seconde... (Avoid Rate Limit)")
await new Promise(resolve => setTimeout(resolve, 500))
}
wlog.log(`Connexion d'un client : ${socket.id}`)
@@ -102,6 +106,7 @@ function init() {
var token = socket.handshake.auth.token
var sessionId = socket.handshake.auth.sessionId
var auth_code = socket.handshake.auth.auth_code
var inLogin = false
if(sessionId) {
if(!session.checkSession(sessionId)) {
@@ -126,7 +131,8 @@ function init() {
}
const newToken = await loggedUser.createToken()
socket.emit("NEW_TOKEN", newToken)
socket.disconnect()
token = newToken
inLogin = true
wlog.log("Utilisateur Discord associé à la session : " + sessionId + " récupéré avec succès")
}
@@ -134,6 +140,7 @@ function init() {
} else {
wlog.warn("Code d'authentification manquant pour le client :" + socket.id)
socket.emit("AUTH_ERROR", "Code manquant invalide")
socket.disconnect()
return
}
@@ -142,6 +149,7 @@ function init() {
if(!token) {
wlog.warn("Token manquant pour le client :" + socket.id)
socket.emit("AUTH_ERROR", "Token invalide")
sendSession()
return
}
@@ -150,31 +158,42 @@ function init() {
if(!socketUser) {
wlog.warn("Token invalide pour le client :" + socket.id)
socket.emit("AUTH_ERROR", "Token invalide")
sendSession()
return
} else {
if(!socketUser.auth) {
wlog.warn("L'utilisateur '" + socketUser.identity.username + "' n'a pas d'authentification Discord Valide")
socketUser.clearToken()
socket.emit("AUTH_ERROR", "AUTH_ERROR")
socket.emit("AUTH_ERROR", "L'authentification Discord de l'utilisateur n'est pas valide")
socket.disconnect()
return
}
if (!(await users.updateGuilds(socketUser.identity.id)) || !(await users.updateIdentity(socketUser.identity.id))) {
wlog.error("Erreur lors de la mise à jour des informations de l'utilisateur : " + socketUser.identity.id);
socket.emit("UPDATE_ERROR", "Error updating user information");
wlog.log("Déconnexion de l'utilisateur : " + socketUser.identity.username + " (" + socketUser.identity.id + ") - Socket : " + socket.id)
socket.disconnect();
return;
if(!inLogin) {
if(socketUser.needUpdate()) {
if (!(await users.updateGuilds(socketUser.identity.id)) || !(await users.updateIdentity(socketUser.identity.id))) {
wlog.error("Erreur lors de la mise à jour des informations de l'utilisateur : " + socketUser.identity.id);
socket.emit("AUTH_ERROR", "Mise à jour des informations de l'utilisateur impossible");
wlog.log("Déconnexion de l'utilisateur : " + socketUser.identity.username + " (" + socketUser.identity.id + ") - Socket : " + socket.id)
socket.disconnect();
return;
}
socketUser.justUpdated()
} else {
wlog.log("Pas de mise à jour des informations de l'utilisateur : " + socketUser.identity.id + " car l'utilisateur vient de se connecter")
}
} else {
wlog.log("L'utilisateur '" + socketUser.identity.username + "' s'est connecté via la session : " + sessionId)
}
}
socketUser = users.getUserByToken(token)
if(socketUser) {
if(allConnectedUsers.includes(socketUser.identity.id)) {
if(allConnectedUsers.includes(socketUser.identity)) {
wlog.warn("L'utilisateur '" + socketUser.identity.username + "' est déjà connecté sur un autre appareil")
return
} else {
@@ -189,7 +208,7 @@ function init() {
if(socketUser.isFullBanned()) {
wlog.warn("Utilisateur banni : " + socketUser.identity.username + " (" + socketUser.identity.id + ") - Socket : " + socket.id)
socket.emit("BANNED")
socket.emit("AUTH_ERROR", "Vous êtes banni du serveur")
socket.disconnect()
}
if(socketUser.isAdmin()) {
@@ -197,6 +216,7 @@ function init() {
wlog.log("Utilisateur admin identifié : " + socketUser.identity.username + " (" + socketUser.identity.id + ")")
}
IOAnswer("/USER/READY", true)
// USERS
// CHECKED : 24/04/2025
@@ -204,9 +224,31 @@ function init() {
var guildPresents = new Array();
var guildsOfBot = discordBot.getGuilds()
for(var guild of guildsOfBot) {
if(guild[1].members.includes(socketUser.identity.id)) {
guildPresents.push(guild[1].id)
const guildData = socketUser.guilds.find(g => g.id == guild[0])
guildData['members'] = new Array()
guildData.serverMember = guild[1].members.length
for(var user of guild[1].members) {
const userData = users.getUserById(user)
if(userData && userData.identity.id != socketUser.identity.id && allConnectedUsers.includes(userData.identity)) {
guildData.members.push({
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]),
})
}
}
// 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
}
guildPresents.push(guildData)
}
}
IOAnswer("/USER/INFO", {
@@ -225,6 +267,7 @@ function init() {
//CHECKED : 24/04/2025
IORequest("/USER/SIGNOUT", () => {
socketUser.removeToken(token)
IOAnswer("/USER/SIGNOUT", true)
socket.disconnect()
})
@@ -660,7 +703,7 @@ function init() {
if(userSocket) {
const socket = io.sockets.sockets.get(userSocket)
if(socket) {
socket.emit("DELETED")
socket.emit("AUTH_ERROR", "Votre compte a été supprimé")
socket.disconnect()
}
}
@@ -677,7 +720,10 @@ function init() {
}
IOAnswer("/ADMIN/PLAYER/GETALLSTATE", states)
})
}
// CHECKED : 24/04/2025
IORequest("/OWNER/USERS/SWITCH_MOD", (data) => {
@@ -801,7 +847,7 @@ function init() {
}
function handleDisconnect() {
if(socketUser) {
@@ -871,6 +917,25 @@ function init() {
}
}
function updateGuildConnectedUsers() {
guildConnectedUsers.clear()
// Get from discordBot
const guilds = discordBot.getGuilds()
for(var guild of guilds) {
const members = discordBot.getGuildMembers(guild[0])
if(!members) continue
for(var member of members) {
const user = users.getUserById(member)
if(user && allConnectedUsers.includes(user.identity)) {
if(!guildConnectedUsers.has(guild[0])) {
guildConnectedUsers.set(guild[0], new Array())
}
guildConnectedUsers.get(guild[0]).push(user)
}
}
}
}
}

View File

@@ -17,7 +17,7 @@ async function getDiscordUser(sessionId, auth_code) {
params.append("client_secret", getClientSecret());
params.append("grant_type", "authorization_code");
params.append("code", auth_code);
params.append("redirect_uri", getWebsiteLink() + "/callback");
params.append("redirect_uri", getWebsiteLink() + "/redirect");
params.append("scope", "identify guilds");
fetch("https://discord.com/api/oauth2/token", {
@@ -112,6 +112,7 @@ function getUserGuilds(accessToken) {
}).then(guildsResp => guildsResp.json()).then(guilds => {
if (guilds.error) {
dlog.error("Erreur lors de la récupération des guildes de l'utilisateur Discord : " + guilds.error + " : " + guilds.error_description);
dlog.log(accessToken.token_type + " " + accessToken.access_token )
resolve(null);
return;
}

View File

@@ -158,8 +158,36 @@ class User {
return this.labels.includes(ownerLabel);
}
justUpdated() {
const userInUserList = userList.find(user => user.identity.id === this.identity.id);
if (!userInUserList) {
clog.warn(`Utilisateur ${this.identity.username} non trouvé dans la liste des utilisateurs.`);
return false;
}
userInUserList.labels = userInUserList.labels.filter(label => !label.startsWith("UPDATED["));
userInUserList.labels.push("UPDATED[" + new Date().toISOString() + "]");
saveUsers();
}
needUpdate() {
const userInUserList = userList.find(user => user.identity.id === this.identity.id);
if (!userInUserList) {
clog.warn(`Utilisateur ${this.identity.username} non trouvé dans la liste des utilisateurs.`);
return false;
}
const lastUpdate = userInUserList.labels.find(label => label.startsWith("UPDATED["));
if (lastUpdate) {
const date = new Date(lastUpdate.replace("UPDATED[", "").replace("]", ""));
const now = new Date();
const diff = now - date;
// Check for 30 seconds
clog.log(`Dernière mise à jour de l'utilisateur ${this.identity.username} : ${date.toISOString()} (${diff} ms) - Besoin de mise à jour : ${diff > 30000}`);
// If the difference is greater than 30 seconds, we need to update
return diff > 30000; // 30 seconds
}
clog.log(`Aucune mise à jour n'a été effectuée pour l'utilisateur ${this.identity.username}.`);
return true;
}
}
@@ -241,6 +269,7 @@ async function updateGuilds(id) {
return null;
}
saveUsers();
return user.guilds;
}
@@ -508,6 +537,14 @@ function saveUsers() {
return loadUsers();
}
function clearNeedUpdateForUsers() {
userList.forEach(user => {
user.labels = user.labels.filter(label => !label.startsWith("UPDATED["));
});
saveUsers();
clog.log("Nettoyage des mises à jour nécessaires pour tous les utilisateurs.");
}
module.exports = {User}
module.exports = {
@@ -528,5 +565,6 @@ module.exports = {
updateCredientials,
refreshAllUserInformation,
updateGuilds,
updateIdentity
updateIdentity,
clearNeedUpdateForUsers
};