diff --git a/.env.example b/.env.example index 47387c5..05ff73c 100644 --- a/.env.example +++ b/.env.example @@ -1,12 +1,13 @@ TOKEN="BOT_TOKEN" GUILD_ID="1234567890" -DB_DIALECT="mysql" + +DB_DIALECT="postgres" DB_HOST="localhost" DB_NAME="gachamelia" -DB_USERNAME="gachamelia" +DB_USERNAME="postgres" DB_PASSWORD="gachamelia" -DB_PORT=3310 -SSR_ROLE="" -SR_ROLE="" -R_ROLE="" -WELCOME_CHANNEL="" \ No newline at end of file +DB_PORT=5433 + +WELCOME_CHANNEL="" + +INIT_DB="true" \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index a832151..e721dbb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,15 +1,13 @@ services: - mysql: - image: mysql:8 + postgres: + image: postgres:17 environment: - MYSQL_ROOT_PASSWORD: root - MYSQL_DATABASE: gachamelia - MYSQL_USER: gachamelia - MYSQL_PASSWORD: gachamelia + POSTGRES_PASSWORD: gachamelia + POSTGRES_DB: gachamelia ports: - - "3310:3306" + - "5433:5432" volumes: - - mysql_gachamelia_data:/var/lib/mysql + - postgres_gachamelia_data:/var/lib/postgresql/data volumes: - mysql_gachamelia_data: \ No newline at end of file + postgres_gachamelia_data: \ No newline at end of file diff --git a/src/base/classes/CustomClient.ts b/src/base/classes/CustomClient.ts index 213c194..40e2673 100644 --- a/src/base/classes/CustomClient.ts +++ b/src/base/classes/CustomClient.ts @@ -9,6 +9,7 @@ import {Database} from "./Database"; import {IDatabaseConfig} from "../interfaces/IDatabaseConfig"; import {Dialect} from "sequelize"; import {User} from "../models/User"; +import {Rank} from "../models/Rank"; export class CustomClient extends Client implements ICustomClient { config: IConfig; @@ -47,7 +48,20 @@ export class CustomClient extends Client implements ICustomClient { } async init(): Promise { await this.database.connect(); + + Rank.initModel(this.database.getSequelize()); User.initModel(this.database.getSequelize()); + + + Rank.hasMany(User, { + foreignKey: 'rankId', + as: 'users' + }); + User.belongsTo(Rank, { + foreignKey: 'rankId', + as: 'rank' + }); + await this.database.sync({ alter: true }); diff --git a/src/base/enums/Rank.ts b/src/base/enums/Rank.ts deleted file mode 100644 index 3c1bf8d..0000000 --- a/src/base/enums/Rank.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum Rank { - SSR = 'SSR', - SR = 'SR', - R = 'R', -} - diff --git a/src/base/enums/RankChance.ts b/src/base/enums/RankChance.ts deleted file mode 100644 index 5909a24..0000000 --- a/src/base/enums/RankChance.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum RankChance { - SSR = 3, - SR = 17, - R = 80, -} - diff --git a/src/base/models/Rank.ts b/src/base/models/Rank.ts new file mode 100644 index 0000000..b423cde --- /dev/null +++ b/src/base/models/Rank.ts @@ -0,0 +1,40 @@ +import { Model, DataTypes, Sequelize } from 'sequelize'; +import {User} from "./User"; + +export class Rank extends Model { + declare id: number; + declare name: string; + declare discordId: string; + declare percentage: number; + declare createdAt: Date; + declare updatedAt: Date; + declare users: User[]; + + public static initModel(sequelize: Sequelize): void { + Rank.init({ + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true + }, + name: { + type: DataTypes.STRING, + allowNull: false + }, + discordId: { + type: DataTypes.STRING, + allowNull: false, + unique: true + }, + percentage: { + type: DataTypes.INTEGER, + allowNull: false + }, + createdAt: DataTypes.DATE, + updatedAt: DataTypes.DATE, + }, { + sequelize, + tableName: 'ranks' + }); + } +} \ No newline at end of file diff --git a/src/base/models/User.ts b/src/base/models/User.ts index 627f5d6..5bacc56 100644 --- a/src/base/models/User.ts +++ b/src/base/models/User.ts @@ -1,12 +1,22 @@ -import { Model, DataTypes, Sequelize } from 'sequelize'; -import { Rank } from '../enums/Rank'; +import { + Model, + DataTypes, + Sequelize, + ForeignKey, + NonAttribute, + InferAttributes, InferCreationAttributes, CreationOptional, Association +} from 'sequelize'; +import {Rank} from "./Rank"; -export class User extends Model { - declare id: number; +export class User extends Model< + InferAttributes, + InferCreationAttributes> { + declare id: CreationOptional; declare discordId: string; - declare rank: Rank; - declare createdAt: Date; - declare updatedAt: Date; + declare rankId: ForeignKey; + declare rank: NonAttribute; + declare createdAt: CreationOptional; + declare updatedAt: CreationOptional; public static initModel(sequelize: Sequelize): void { User.init({ @@ -20,9 +30,13 @@ export class User extends Model { allowNull: false, unique: true }, - rank: { - type: DataTypes.STRING, - allowNull: false + rankId: { + type: DataTypes.INTEGER, + allowNull: false, + references: { + model: Rank, + key: 'id' + } }, createdAt: DataTypes.DATE, updatedAt: DataTypes.DATE @@ -31,4 +45,10 @@ export class User extends Model { tableName: 'users' }); } + + + + declare static associations: { + rank: Association; + }; } \ No newline at end of file diff --git a/src/base/utils/GachaUtils.ts b/src/base/utils/GachaUtils.ts index 6c6a8a9..b298e9c 100644 --- a/src/base/utils/GachaUtils.ts +++ b/src/base/utils/GachaUtils.ts @@ -1,33 +1,24 @@ import {CustomClient} from "../classes/CustomClient"; import {User} from "../models/User"; import {getRandomRank} from "./RandomUtils"; -import {Rank} from "../enums/Rank"; import {GuildMember} from "discord.js"; +import {Rank} from "../models/Rank"; -export async function addRole(member: GuildMember, user: User, client: CustomClient): Promise { +export async function addRole(member: GuildMember, user: User): Promise { try { - let rRoleId = client.config.rRole; - let srRoleId = client.config.srRole; - let ssrRoleId = client.config.ssrRole; - - if (user.rank === Rank.R) { - const rRole = await member.guild.roles.fetch(rRoleId); - - if (rRole) { - await member.roles.add(rRole); + let userRank = await Rank.findOne({ + where: { + id: user.rankId } - } else if (user.rank === Rank.SR) { - const srRole = await member.guild.roles.fetch(srRoleId); + }); - if (srRole) { - await member.roles.add(srRole); - } - } else if (user.rank === Rank.SSR) { - const ssrRole = await member.guild.roles.fetch(ssrRoleId); + if (!userRank) { + return; + } - if (ssrRole) { - await member.roles.add(ssrRole); - } + const discordRank = await member.guild.roles.fetch(userRank.discordId); + if (discordRank) { + await member.roles.add(userRank.discordId); } } catch (e: Error | any) { console.log(`Impossible d'ajouter les rôles à ${member.displayName}`); @@ -43,16 +34,25 @@ export async function createAllUsers(client: CustomClient): Promise { let user = await User.findOne({ where: { discordId: member.id + }, + include: { + model: Rank, + as: 'rank' } }) if (!user) { user = await User.create({ discordId: member.id, - rank: getRandomRank() + rankId: (await getRandomRank()).id + }, { + include: [{ + model: Rank, + as: 'rank' + }] }); - await addRole(member, user, client); + await addRole(member, user); count++; } diff --git a/src/base/utils/RandomUtils.ts b/src/base/utils/RandomUtils.ts index 7ae4db1..67bfa64 100644 --- a/src/base/utils/RandomUtils.ts +++ b/src/base/utils/RandomUtils.ts @@ -1,16 +1,20 @@ -import {Rank} from "../enums/Rank"; -import {RankChance} from "../enums/RankChance"; +import {Rank} from "../models/Rank"; -export function getRandomRank(): Rank { +export async function getRandomRank(): Promise { const rand = Math.random() * 100; let cumulativeChance = 0; + const ranks = await Rank.findAll(); - for (const rank of Object.values(Rank)) { - cumulativeChance += RankChance[rank as keyof typeof RankChance]; + if (ranks.length === 0) { + throw new Error('No ranks found'); + } + + for (const rank of ranks) { + cumulativeChance += rank.percentage; if (rand <= cumulativeChance) { return rank as Rank; } } - return Rank.R; + return ranks[0] as Rank; } \ No newline at end of file diff --git a/src/commands/RankCommand.ts b/src/commands/RankCommand.ts index 6fe81cc..32f2f16 100644 --- a/src/commands/RankCommand.ts +++ b/src/commands/RankCommand.ts @@ -3,7 +3,7 @@ import {CustomClient} from "../base/classes/CustomClient"; import {Category} from "../base/enums/Category"; import {EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandUserOption} from "discord.js"; import {User} from "../base/models/User"; -import {Rank} from "../base/enums/Rank"; +import {Rank} from "../base/models/Rank"; import {getRandomRank} from "../base/utils/RandomUtils"; export class PingCommand extends Command { @@ -26,19 +26,23 @@ export class PingCommand extends Command { async execute(interaction: any): Promise { let discordUser = (interaction.options.getMember('user') || interaction.member) as GuildMember; - console.log(discordUser.id); + let user = await User.findOne( { where: { discordId: discordUser.id - } + }, + include: [{ + model: Rank, + as: 'rank' + }], } ); if (!user) { user = await User.create({ discordId: discordUser.id, - rank: getRandomRank() + rankId: (await getRandomRank()).id }); } @@ -46,7 +50,7 @@ export class PingCommand extends Command { let embed = new EmbedBuilder() .setTitle(`Rang de ${discordUser.displayName}`) .setThumbnail(discordUser.displayAvatarURL()) - .setDescription(`Cet utilisateur est de rang : ${Rank[user.rank]}`) + .setDescription(`Cet utilisateur est de rang : ${user.rank.name}`) .setTimestamp(new Date()) .setColor('#0078DE') ; diff --git a/src/events/client/Ready.ts b/src/events/client/Ready.ts index 5b68d4e..aac1f03 100644 --- a/src/events/client/Ready.ts +++ b/src/events/client/Ready.ts @@ -3,6 +3,7 @@ import {CustomClient} from "../../base/classes/CustomClient"; import {Collection, Events, REST, Routes} from "discord.js"; import {Command} from "../../base/classes/Command"; import {createAllUsers} from "../../base/utils/GachaUtils"; +import {Rank} from "../../base/models/Rank"; export class Ready extends Event { constructor(client: CustomClient) { super(client, { @@ -29,7 +30,11 @@ export class Ready extends Event { console.log(`${setCommands.length} Commandes mises à jours avec succès !`); - await createAllUsers(this.client); + if (process.env.INIT_DB === 'true') { + await this.initDb(); + } else { + await createAllUsers(this.client); + } } private getJson(commands: Collection): object[] { @@ -47,4 +52,38 @@ export class Ready extends Event { return data; } + + private async initDb() { + // On commence par vérifier si les rangs existent déjà + const ranks = await Rank.findAll(); + if (ranks.length > 0) { + return; + } + + await Rank.create({ + name: '1★', + discordId: '1234567890', + percentage: 1, + }); + await Rank.create({ + name: '2★', + discordId: '2345678901', + percentage: 48, + }); + await Rank.create({ + name: '3★', + discordId: '3456789012', + percentage: 41, + }); + await Rank.create({ + name: '4★', + discordId: '4567890123', + percentage: 2, + }); + await Rank.create({ + name: '5★', + discordId: '5678901234', + percentage: 3, + }); + } } \ No newline at end of file diff --git a/src/events/guild/GuildMemberJoin.ts b/src/events/guild/GuildMemberJoin.ts index d4b4336..6c8ec20 100644 --- a/src/events/guild/GuildMemberJoin.ts +++ b/src/events/guild/GuildMemberJoin.ts @@ -3,8 +3,8 @@ import {CustomClient} from "../../base/classes/CustomClient"; import {Event} from "../../base/classes/Event"; import {User} from "../../base/models/User"; import {getRandomRank} from "../../base/utils/RandomUtils"; -import {Rank} from "../../base/enums/Rank"; import {addRole} from "../../base/utils/GachaUtils"; +import {Rank} from "../../base/models/Rank"; export class GuildMemberJoin extends Event { constructor(client: CustomClient) { @@ -26,11 +26,16 @@ export class GuildMemberJoin extends Event { if (!user) { user = await User.create({ discordId: member.id, - rank: getRandomRank() + rankId: (await getRandomRank()).id + }, { + include: [{ + model: Rank, + as: 'rank' + }] }); } - await addRole(member, user, this.client); + await addRole(member, user); const channel = await member.guild.channels.fetch(this.client.config.welcomeChannel); @@ -39,7 +44,7 @@ export class GuildMemberJoin extends Event { let embed = new EmbedBuilder() .setTitle(`Bienvenue sur le serveur ${member.displayName} !`) .setThumbnail(member.displayAvatarURL()) - .setDescription(`Bien joué ! Tu as obtenu le rang : ${Rank[user.rank]}`) + .setDescription(`Bien joué ! Tu as obtenu le rang : ${user.rank.name}`) .setTimestamp(new Date()) .setColor('#0078DE') ;