Select Git revision

Sofiane Lasri authored
index.js 27.78 KiB
// Configuration
const config = require('./config');
// Driver SQL
const Sequelize = require('sequelize');
const { Op } = require("sequelize");
// API externe
const express = require('express')
const app = express()
var XMLHttpRequest = require('xhr2');
// Couleurs de la console
var colors = require('colors');
// API discord
const { REST } = require('@discordjs/rest');
const { Routes } = require('discord-api-types/v9');
// Moment JS
var moment = require('moment');
// Schedule
const schedule = require('node-schedule');
////////////////////////////////////////////////////////////////
// MODELES DES TABLES
////////////////////////////////////////////////////////////////
// Connexion à la base de données
const sequelize = new Sequelize(config.get("BDD_NAME"), config.get("BDD_USER"), config.get("BDD_PASSWORD"), {
host: config.get("BDD_HOST"),
dialect: 'mariadb',
logging: false,
});
// Paramètres du bot
const botSettings = sequelize.define('discord_settings', {
name: { type: Sequelize.STRING(128), primaryKey: true },
value: Sequelize.STRING(512),
}, {
timestamps: false
});
// Entrées et sorties des membres
const entries = sequelize.define('discord_entries', {
id: { type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true },
memberId: Sequelize.BIGINT(255),
isJoin: Sequelize.BOOLEAN,
date: Sequelize.DATE,
}, {
timestamps: false
});
// Paramètres des membres
const memberSettings = sequelize.define('discord_memberSettings', {
memberId: { type: Sequelize.BIGINT(255), primaryKey: true },
name: { type: Sequelize.STRING(128), primaryKey: true },
value: Sequelize.STRING(512),
}, {
timestamps: false
});
// Paramètres des joueurs MC
const minecraftPlayerSetting = sequelize.define('site_userSetting', {
uuid: { type: Sequelize.STRING(36), primaryKey: true },
name: { type: Sequelize.STRING(128), primaryKey: true },
value: Sequelize.TEXT,
}, {
timestamps: false,
freezeTableName: true
});
// Codes de liaison du serveur MC
const mcLinkCode = sequelize.define('site_linkCode', {
uuid: { type: Sequelize.STRING(36), primaryKey: true },
code: { type: Sequelize.STRING(8), unique: true },
time: { type: 'TIMESTAMP', defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), allowNull: false },
used: { type: Sequelize.BOOLEAN, defaultValue: false },
}, {
timestamps: false,
freezeTableName: true
});
////////////////////////////////////////////////////////////////
const commands = [{
name: 'ping',
description: 'Répond avec pong!'
},
{
name: 'setanniv',
description: 'Permet de définir ta date d\'anniversaire (usage unique).',
options: [{
name: "date", // no uppercase as well
description: "Date au format DD/MM/YYYY - 31/12/2001",
type: 3,
required: true
}]
},
{
name: 'delanniv',
description: '[Admin] Supprime la date d\'anniversaire d\'un membre.',
options: [{
name: "membre", // no uppercase as well
description: "Membre à supprimer la date d'anniversaire.",
type: 6,
required: true
}]
},
{
name: 'linkmc',
description: 'Te permet de lier ton compte Minecraft au serveur Discord.',
options: [{
name: "code", // no uppercase as well
description: "Code généré par la commande /getlinkcode.",
type: 3,
required: true
}]
}];
////////////////////////////////////////////////////////////////
// INITIALISATION DES COMMANDES ET CONNEXION À LA BASE DE DONNÉES
////////////////////////////////////////////////////////////////
const rest = new REST({ version: '9' }).setToken(config.get("DISCORD_BOT_TOKEN"));
(async () => {
console.log('[' + 'INFO'.yellow + '] Connexion à la base de donnée...'.brightWhite);
try {
await sequelize.authenticate();
console.log('[' + 'SUCCES'.brightGreen + '] Connexion à la base de donnée réussie.'.brightWhite);
await initialiseDatabaseTables(); // On va initialiser les tables de la base de donnée
try {
console.log('[' + 'INFO'.yellow + '] Actualisation des commandes...'.brightWhite);
//console.log(JSON.stringify(commands));
await rest.put(
Routes.applicationGuildCommands(config.get("CLIENT_ID"), config.get("GUILD_ID")),
{ body: commands },
);
console.log('[' + 'SUCCES'.brightGreen + '] Toutes les commandes ont été actualisées.');
} catch (error) {
console.error('[' + 'ERREUR'.brightRed + '] Erreur lors de l\'actualisation des commandes:'.brightWhite + error);
}
} catch (error) {
console.log('[' + 'ERREUR'.brightRed + '] Erreur lors de la connexion à la base de donnée:'.brightWhite);
console.log('[' + 'DEBUG'.yellow + '] '.brightWhite + config.get("BDD_USER") + ":" + config.get("BDD_PASSWORD") + "@" + config.get("BDD_HOST") + " db:" + config.get("BDD_NAME") + '\n');
console.error(error);
}
})();
async function initialiseDatabaseTables() {
console.log('[' + 'INFO'.yellow + '] Initialisation des tables...'.brightWhite);
try {
// On synchronise les modèles de sequlize
await botSettings.sync();
await entries.sync();
await memberSettings.sync();
await minecraftPlayerSetting.sync();
await mcLinkCode.sync();
// Basiquement on regarde si l'entrée existe, puis on agit en conséquence
let token = await botSettings.findOne({ where: { name: "token" } });
if (token == null) {
// INSERT si elle n'existe pas
console.log('[' + 'INSERT'.brightMagenta + '] Insertion de token'.brightWhite);
let token = botSettings.create({
name: "token",
value: config.get("DISCORD_BOT_TOKEN")
});
} else {
// UPDATE si différente
if (token.value != config.get("DISCORD_BOT_TOKEN")) {
token.update({ value: config.get("DISCORD_BOT_TOKEN") })
.then(updatedRecord => {
console.log('[' + 'UPDATE'.brightMagenta + '] Mise à jour du token dans la base de donnée'.brightWhite);
}).catch(err => {
console.log('[' + 'ERREUR'.brightRed + '] Erreur lors de la màj de token dans la base de donnée: '.brightWhite + '\n');
throw new Error(err);
});
}
}
// Et c'est pareil à chaque fois
let clientId = await botSettings.findOne({ where: { name: "clientId" } });
if (clientId == null) {
console.log('[' + 'INSERT'.brightMagenta + '] Insertion de clientId'.brightWhite);
let clientId = botSettings.create({
name: "clientId",
value: config.get("CLIENT_ID")
});
} else {
if (clientId.value != config.get("CLIENT_ID")) {
clientId.update({ value: config.get("CLIENT_ID") })
.then(updatedRecord => {
console.log('[' + 'UPDATE'.brightMagenta + '] Mise à jour du clientId dans la base de donnée'.brightWhite);
}).catch(err => {
console.log('[' + 'ERREUR'.brightRed + '] Erreur lors de la màj de clientId dans la base de donnée: '.brightWhite + '\n');
throw new Error(err);
});
}
}
let guildId = await botSettings.findOne({ where: { name: "guildId" } });
if (guildId == null) {
console.log('[' + 'INSERT'.brightMagenta + '] Insertion de guildId'.brightWhite);
let guildId = botSettings.create({
name: "guildId",
value: config.get("GUILD_ID")
});
} else {
if (guildId.value != config.get("GUILD_ID")) {
guildId.update({ value: config.get("GUILD_ID") })
.then(updatedRecord => {
console.log('[' + 'UPDATE'.brightMagenta + '] Mise à jour du guildId dans la base de donnée'.brightWhite);
}).catch(err => {
console.log('[' + 'ERREUR'.brightRed + '] Erreur lors de la màj de guildId dans la base de donnée: '.brightWhite + '\n');
throw new Error(err);
});
}
}
console.log('[' + 'SUCCES'.brightGreen + '] Tables initialisées avec succès.'.brightWhite);
} catch (error) {
console.error('[' + 'ERREUR'.brightRed + '] Erreur lors de l\'initialisation des tables:'.brightWhite + '\n', error);
}
}
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
// CONSOLE
////////////////////////////////////////////////////////////////
// Console Input/Output
var readline = require('readline');
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
// Ce code est a améliorer, je l'ai vulgairement recopié d'un ancien bot (lui même pas très bien conçu)
var recursiveAsyncReadLine = function () {
rl.question('Commande: ', function (answer) {
//if (answer == 'exit')
// return rl.close();
const args = answer.match(/(".*?"|[^"\s]+)+(?=\s*|\s*$)/g);
const command = args.shift().toLowerCase();
switch (command) {
case "say":
if (!args[0] || !args[1])
console.log('\n' + '[' + 'ERREUR'.brightRed + "] Tu n'as pas mis d'arguments :p" + '\n' + '[' + 'INFO'.yellow + "] Usage: say <Numéro du canal> <\"Texte\">");
else {
var message = args[1].substring(1, args[1].length - 1);
client.channels.cache.get(args[0]).send(message);
console.log('\n' + '[' + 'SUCCES'.brightGreen + '] Le message a été envoyé dans le canal n°' + args[0]);
}
break;
default:
console.log('\n' + "Commande inconnue. :p");
break;
}
recursiveAsyncReadLine(); //Calling this function again to ask new question
});
};
recursiveAsyncReadLine(); //we have to actually start our recursion somehow
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
// BOT DISCORD
////////////////////////////////////////////////////////////////
// require the needed discord.js classes
const { Client, Intents, MessageActionRow, MessageButton, WebhookClient } = require('discord.js');
const { verify } = require('crypto');
// create a new Discord client
const client = new Client({ intents: ["GUILDS", "GUILD_MESSAGES", "DIRECT_MESSAGES", "GUILD_MEMBERS"] });
var mcChatWebhook;
client.on("ready", async function () {
console.log('\n' + "SL-Projects Bot".brightCyan);
console.log("");
console.log("Bot démarré".brightGreen);
var guild = client.guilds.cache.get(config.get("GUILD_ID"));
var memberCount = guild.memberCount.toLocaleString();
console.log('\n' + "Nous sommes " + memberCount + " membres.");
client.user.setActivity("Regarde " + memberCount + " membres");
checkAnniv();
verifyMcChatWebhook();
});
// Actions lorsqu'un membre rejoins ou part
client.on('guildMemberAdd', async (member) => {
let guild = client.guilds.cache.get(config.get("GUILD_ID"));
var memberCount = guild.memberCount.toLocaleString();
console.log('\n' + "Nouveau membre - " + member.user.username + " - " + memberCount + " membres");
member.guild.channels.cache.get(config.get("CANAL_LOG")).send("Nouveau membre - " + member.user.username + " - " + memberCount + " membres");
client.user.setActivity("Regarde " + memberCount + " membres");
var currentdate = new Date();
var datetime = currentdate.getFullYear() + "-"
+ (currentdate.getMonth() + 1) + "-"
+ currentdate.getDate() + " "
+ (currentdate.getHours() + 1) + ":"
+ currentdate.getMinutes() + ":"
+ currentdate.getSeconds();
await entries.create({
id: null,
memberId: member.user.id,
isJoin: 1,
date: datetime,
});
});
client.on('guildMemberRemove', async (member) => {
let guild = client.guilds.cache.get(config.get("GUILD_ID"));
var memberCount = guild.memberCount.toLocaleString();
console.log('\n' + "Un membre a quitté le serveur - " + member.user.username + " - " + memberCount + " membres");
member.guild.channels.cache.get(config.get("CANAL_LOG")).send("Un membre a quitté le serveur - " + member.user.username + " - " + memberCount + " membres");
client.user.setActivity("Regarde " + memberCount + " membres");
var currentdate = new Date();
var datetime = currentdate.getFullYear() + "-"
+ (currentdate.getMonth() + 1) + "-"
+ currentdate.getDate() + " "
+ (currentdate.getHours() + 1) + ":"
+ currentdate.getMinutes() + ":"
+ currentdate.getSeconds();
await entries.create({
id: null,
memberId: member.user.id,
isJoin: 0,
date: datetime,
});
});
// Lorsqu'un message est envoyé
client.on('messageCreate', async message => {
// On va regarder si le message vient du canal de tchat du serveur MC
if (message.channel.id == config.get("CANAL_CHAT_MC") && !message.author.bot) {
var playerName = message.author.username;
var messageContent = message.content;
// On va vérifier si l'utilisateur a associé son compte discord avec le serveur MC
var memberAccountAssoc = await minecraftPlayerSetting.findOne({
where: {
name: 'discordAccountId',
value: message.author.id
}
});
if (memberAccountAssoc != null) {
// L'utilisateur a bien associé son compte discord avec le serveur MC
let userMcUsername = await minecraftPlayerSetting.findOne({
where: {
uuid: memberAccountAssoc.uuid,
name: 'playerName'
}
});
if(userMcUsername != null) {
playerName = userMcUsername.value;
}
}
// On encode ça en JSON
var jsonEncodedMessage = JSON.stringify({
"message": messageContent,
"playerName": playerName
});
// Maintenant on va appeler le serveur pour envoyer le message
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", "http://node.sl-projects.com:" + config.get("PORT_MSG_SRVMC") + "/discordMsg/" + encodeURIComponent(jsonEncodedMessage), true);
xmlHttp.send();
}
});
// Commandes
client.on('interactionCreate', async interaction => {
if (interaction.isCommand()) {
console.log('[' + 'COMMANDE'.brightMagenta + '] '.brightWhite + interaction.user.username.brightBlue + ' a lancé la commande '.brightWhite + interaction.commandName.yellow);
if (interaction.commandName === 'ping') {
await interaction.reply('Pong!');
} else if (interaction.commandName === 'setanniv') {
// Je check si le membre a déjà enregistré sa date d'anniversaire
let userAnniv = await memberSettings.findOne({
where: {
memberId: interaction.user.id,
name: 'birthday'
}
});
// S'il ne l'a pas déjà fait
if (userAnniv == null) {
// On va checker que ce qu'il a envoyé est bien une date valide
let memberBirthday = moment(interaction.options.getString('date'), "DD/MM/YYYY");
if (isNaN(memberBirthday)) {
console.log('\n' + '[' + 'ERREUR'.brightRed + "] Date illisible: " + interaction.options.getString('date'));
await interaction.reply('J\'ai du mal à lire la date que tu m\'as donné. Est-elle bien dans ce format **DD/MM/YYYY**? :thinking:');
} else {
try {
memberBirthday = memberBirthday.toDate();
console.log('[' + 'INSERT'.brightMagenta + '] '.brightWhite + interaction.user.username.brightBlue + " a renseigné sa date d'anniversaire. ".brightWhite + interaction.options.getString('date').yellow);
var dd = memberBirthday.getDate();
var mm = memberBirthday.getMonth() + 1;
var yyyy = memberBirthday.getFullYear();
if (dd < 10) {
dd = '0' + dd;
}
if (mm < 10) {
mm = '0' + mm;
}
let birthday = mm + '/' + dd + '/' + yyyy;
let insetMemberBirthday = memberSettings.create({
memberId: interaction.user.id,
name: "birthday",
value: birthday
});
await interaction.reply({ content: 'Je m\'en souviendrai. :thumbup:', ephemeral: true });
checkAnniv();
} catch (error) {
console.error('[' + 'ERREUR'.brightRed + '] Erreur lors de l\'insertion de la date d\'anniversaire: '.brightWhite + '\n', error);
await interaction.reply("J'ai eu un petit problème pour enregistrer ta date d'anniversaire, re-essaie plus-tard. :p");
}
}
} else {
await interaction.reply('Tu ne peux pas redéfinir ta date d\'anniversaire. Demande au staff si besoin. :p');
}
} else if (interaction.commandName === 'delanniv') {
// On check les perms
if (interaction.member.roles.cache.has(config.get("ROLE_ANNIV"))) {
try {
console.log('\n' + '[' + 'DELETE'.brightMagenta + "] Suppression de la date d'anniversaire de " + interaction.options.getMember('membre'));
await memberSettings.destroy({
where: {
name: "birthday",
memberId: interaction.options.getMember('membre').id
}
});
} catch (error) {
console.error('[' + 'ERREUR'.brightRed + '] Erreur lors de la supression de la date d\'anniversaire: '.brightWhite + '\n', error);
await interaction.reply("J'ai eu un petit problème pour supprimer la date d'anniversaire, re-essaie plus-tard. :p");
}
await interaction.reply({ content: 'La date d\'anniversaire de <@' + interaction.options.getMember('membre') + '> a été supprimée.', ephemeral: true });
checkAnniv();
} else {
await interaction.reply({ content: "Tu n'as pas le droit d'exécuter cette commande. :p", ephemeral: true });
}
} else if (interaction.commandName === 'linkmc') {
console.log('[' + 'INFO'.yellow + '] L\'utilisateur '.brightWhite + interaction.user.username.brightBlue + " a demandé une association de son compte Minecraft avec le code ".brightWhite + interaction.options.getString('code').yellow);
// On va vérifier si le membre a déjà associé son compte minecraft
let memberAccountAssoc = await minecraftPlayerSetting.findOne({
where: {
name: 'discordAccountId',
value: interaction.user.id
}
});
if (memberAccountAssoc == null) {
// Il ne l'a pas déjà associé
// On va donc checker la validité du code
let code = interaction.options.getString('code');
// On va le chercher dans la bdd
let isCodeValid = await mcLinkCode.findOne({
where: {
code: code,
used: false
}
});
if (isCodeValid != null) {
// Le code est valide
// On va le mettre dans la bdd
await minecraftPlayerSetting.create({
name: 'discordAccountId',
value: interaction.user.id,
uuid: isCodeValid.uuid
});
// On va mettre à jour le code
await mcLinkCode.update({
used: true
}, {
where: {
code: code
}
});
// On va récupérer le pseudo du joueur pour la réponse
let playerName = await minecraftPlayerSetting.findOne({
where: {
uuid: isCodeValid.uuid,
name: 'playerName'
}
});
// On va envoyer le message
await interaction.reply({ content: 'Ton compte a été associé avec le compte Minecraft de **' + playerName.value + '** !', ephemeral: true });
} else {
// Le code n'est pas valide
await interaction.reply({ content: 'Le code que tu as entré est invalide. :p', ephemeral: true });
}
} else {
// Il l'a déjà associé
await interaction.reply({ content: 'Tu as déjà associé ton compte minecraft avec le serveur Discord. :thinking:', ephemeral: true });
}
}
}
});
async function checkAnniv() {
console.log('[' + 'INFO'.yellow + '] Vérification des anniversaires.'.brightWhite);
let today = new Date();
var dd = today.getDate();
var mm = today.getMonth() + 1;
var yyyy = today.getFullYear();
if (dd < 10) {
dd = '0' + dd;
}
if (mm < 10) {
mm = '0' + mm;
}
today = mm + '/' + dd;
let { count, rows } = await memberSettings.findAndCountAll({
where: {
name: "birthday",
value: {
[Op.like]: today + '%'
}
}
});
const guild = client.guilds.cache.get(config.get("GUILD_ID"));
//console.log(guild);
var membersWithAnnivRole = await guild.roles.cache.get(config.get("ROLE_ANNIV")).members;
//console.log(membersWithAnnivRole);
// On va vérifier que c'est bien l'anniv des membres ayant le rôle. :p
for await (var memberWithAnnivRole of membersWithAnnivRole) {
var isMemberBirthday = false;
for await (const member of rows) {
if (memberWithAnnivRole[0] === member.memberId.toString()) {
isMemberBirthday = true;
}
}
if (!isMemberBirthday) {
console.log('[' + 'INFO'.yellow + '] Suppression du rôle anniversaire pour '.brightWhite + memberWithAnnivRole[1].user.username);
memberWithAnnivRole[1].roles.remove(config.get("ROLE_ANNIV")).catch(console.error);
}
}
console.log('[' + 'SUCCES'.brightGreen + '] C\'est l\'anniversaire de ' + count + ' personne(s).');
for await (var member of rows) {
let memberFetch = await guild.members.fetch(member.memberId.toString());
//console.log(memberFetch);
if (memberFetch) {
console.log(" 🎂 " + memberFetch.user.username);
if (!memberFetch.roles.cache.has(config.get("ROLE_ANNIV"))) {
let annivRole = await memberFetch.guild.roles.cache.find(role => role.id === config.get("ROLE_ANNIV"));
if (annivRole) {
memberFetch.roles.add(annivRole);
console.log('[' + 'INFO'.yellow + '] Le rôle '.brightWhite + annivRole.name.yellow + "a été donné à " + memberFetch.user.username.brightBlue);
client.channels.cache.get(config.get("CANAL_GENERAL")).send("Fêtons l'anniversaire de <@" + memberFetch.id + "> ! :partying_face:").catch(console.error);
}
}
}
}
}
async function verifyMcChatWebhook() {
let mcChatWebhookId = await botSettings.findOne({ where: { name: "mcChatWebhookId" } });
if (mcChatWebhookId != null) {
console.log("[" + 'INFO'.yellow + "] Le Webhook du serveur MC est déjà configuré.");
let mcChatWebhookToken = await botSettings.findOne({ where: { name: "mcChatWebhookToken" } });
if (mcChatWebhookToken) {
mcChatWebhook = new WebhookClient({ id: mcChatWebhookId.value, token: mcChatWebhookToken.value });
} else {
console.log("[" + 'ERREUR'.brightRed + "] Impossible de trouver le token du webhook dans la base de donnée.");
createMcChatWebhook();
}
} else {
createMcChatWebhook();
}
}
async function createMcChatWebhook() {
console.log("[" + 'INFO'.yellow + "] Création du Webhook du serveur MC.");
client.channels.cache.get(config.get("CANAL_CHAT_MC")).createWebhook('Chat Serveur Minecraft', {
avatar: 'https://sl-craft.fr/data/images/logo/short-color.png',
})
.then(webhook => {
botSettings.create({
name: "mcChatWebhookId",
value: webhook.id
});
botSettings.create({
name: "mcChatWebhookToken",
value: webhook.token
});
mcChatWebhook = new WebhookClient({ id: mcChatWebhookId.value, token: mcChatWebhookToken.value });
console.log("[" + 'INFO'.yellow + "] Le Webhook du serveur MC a été configuré avec succès.");
})
.catch(console.error);
}
function sendMessageFromMcChat(username, message) {
if(username == "SL-Craft"){
mcChatWebhook.send({
content: message,
username: username,
avatarURL: 'https://sl-craft.fr/data/images/logo/favicon-color.png',
});
}else{
// On va vérifier que le joueur ne fait pas de @everyone ou de @here
message = message.replace("<@everyone>", "**everyone**");
message = message.replace("<@here>", "**here**");
message = message.replace("@everyone", "**everyone**");
message = message.replace("@here", "**here**");
// On va regarder si le joueur souhaite tagger quelqu'un avec un @
var regex = /@(.*?\s)/g;
var match = regex.exec(message);
if (match){
var member = match[1].trim();
var memberFetch = client.users.cache.find(user => user.username == member);
if (memberFetch){
message = message.replace("@"+member, "<@" + memberFetch.id + ">");
}
}
mcChatWebhook.send({
content: message,
username: username,
avatarURL: 'https://live.mc.sl-projects.com/tiles/faces/32x32/' + username + '.png',
});
}
}
const job = schedule.scheduleJob('0 0 * * *', function () {
console.log('[' + 'INFO'.yellow + '] Éxecution des tâches de fonds quotidiennes...'.brightWhite);
checkAnniv();
});
// login to Discord with your app's token
client.login(config.get("DISCORD_BOT_TOKEN"));
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
// API
////////////////////////////////////////////////////////////////
app.get('/ping', (req, res) => {
res.send("Pong!")
})
app.get('/channels/:id', (req, res) => {
var id = req.params.id;
res.send(client.channels.cache.get(id));
})
app.get('/mc/chat/:detail', (req, res) => {
var detail = JSON.parse(decodeURI(req.params.detail));
sendMessageFromMcChat(detail.username, detail.message);
res.send("Envoyé!")
})
app.listen(27001, () => {
console.log('[' + 'INFO'.yellow + '] Écoute sur '.brightWhite + 'node.sl-projects.com:27001'.yellow);
});
////////////////////////////////////////////////////////////////