From 194ea203c76b5ee3c297da7749257b9c99dfb1ba Mon Sep 17 00:00:00 2001 From: Benjamin Kraft Date: Wed, 12 Jun 2024 15:08:05 +0200 Subject: [PATCH] add, remove, list works --- package-lock.json | 95 ++++++++++++++++++++++++++- package.json | 3 +- src/account.ts | 127 ++++++++++++++++++++++++++++++++++++ src/bot.ts | 145 ++++++++++++++++++++++------------------- src/commands/add.ts | 19 ++---- src/commands/list.ts | 29 ++++----- src/commands/ping.ts | 7 +- src/commands/remove.ts | 19 ++---- src/commands/update.ts | 9 +-- src/index.ts | 1 + src/player.ts | 144 ---------------------------------------- src/util.ts | 13 +++- 12 files changed, 343 insertions(+), 268 deletions(-) create mode 100644 src/account.ts delete mode 100644 src/player.ts diff --git a/package-lock.json b/package-lock.json index 00a3f5b..3e89eaf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,8 @@ "dependencies": { "@fightmegg/riot-api": "^0.0.18", "discord.js": "^14.14.1", - "dotenv": "^16.3.1" + "dotenv": "^16.3.1", + "mysql2": "^3.10.0" }, "devDependencies": { "@types/node": "^20.10.6", @@ -355,6 +356,25 @@ "node": ">= 6" } }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ioredis": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.4.1.tgz", @@ -378,6 +398,11 @@ "url": "https://opencollective.com/ioredis" } }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -398,6 +423,19 @@ "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, + "node_modules/lru-cache": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz", + "integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==", + "engines": { + "node": ">=16.14" + } + }, "node_modules/magic-bytes.js": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz", @@ -429,6 +467,43 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/mysql2": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.10.0.tgz", + "integrity": "sha512-qx0mfWYt1DpTPkw8mAcHW/OwqqyNqBLBHvY5IjN8+icIYTjt6znrgYJ+gxqNNRpVknb5Wc/gcCM4XjbCR0j5tw==", + "dependencies": { + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^5.2.1", + "lru-cache": "^8.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/named-placeholders/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "engines": { + "node": ">=12" + } + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -472,6 +547,24 @@ "node": ">=4" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/standard-as-callback": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", diff --git a/package.json b/package.json index ea36868..f482d1b 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "dependencies": { "@fightmegg/riot-api": "^0.0.18", "discord.js": "^14.14.1", - "dotenv": "^16.3.1" + "dotenv": "^16.3.1", + "mysql2": "^3.10.0" } } diff --git a/src/account.ts b/src/account.ts new file mode 100644 index 0000000..5f1bca7 --- /dev/null +++ b/src/account.ts @@ -0,0 +1,127 @@ +import {PlatformId, RiotAPI, RiotAPITypes} from "@fightmegg/riot-api" +import {RowDataPacket} from "mysql2/promise"; +import {getMySQLConnection} from "./util"; +import AccountDTO = RiotAPITypes.Account.AccountDTO; + +const riotAPI = new RiotAPI(process.env["RIOT_API_KEY"] ?? ""); + +export class Account { + + puuid: string + gameName?: string + tagLine?: string + + currentElo?: Elo + startElo?: Elo + + constructor(puuid: string) { + this.puuid = puuid; + } + + getProgress(){ + return eloToNumber(this.currentElo) - eloToNumber(this.startElo); + } + + async isAdded() { + const conn = await getMySQLConnection(); + const [rows] = await conn.execute(` + SELECT 1 + FROM accounts + WHERE puuid = ? + LIMIT 1 + `, [this.puuid]); + return rows.length == 1; + } + + async addToDB(){ + const conn = await getMySQLConnection(); + await conn.execute(` + INSERT INTO accounts + (puuid, gameName, tagLine) + VALUES (?, ?, ?) + `, [this.puuid, this.gameName, this.tagLine]); + } + + async removeFromDB(){ + const conn = await getMySQLConnection(); + await conn.execute(` + DELETE FROM accounts + WHERE puuid = ? + `, [this.puuid]); + } + + async update(){ + const conn = await getMySQLConnection(); + const fetchedAccount = await riotAPI.account.getByPUUID({ + region: PlatformId.EUROPE, + puuid: this.puuid + }); + this.gameName = fetchedAccount.gameName; + this.tagLine = fetchedAccount.tagLine; + await conn.execute(` + UPDATE accounts + SET gameName = ?, tagLine = ? + WHERE puuid = ? + `, [this.gameName, this.tagLine, this.puuid]); + // TODO try add new elo entry to db + } + + static async fetchFromRiotID(gameName: string, tagLine: string){ + let fetchedAccount: AccountDTO; + try { + fetchedAccount = await riotAPI.account.getByRiotId({ + region: PlatformId.EUROPE, + gameName: gameName, + tagLine: tagLine + }); + } catch (error) { + console.error(`Riot ID not found: ${gameName}#${tagLine}!`); + return; + } + let account = new Account(fetchedAccount.puuid); + account.gameName = fetchedAccount.gameName; + account.tagLine = fetchedAccount.tagLine; + + return account; + } + + toString() { + return `${this.gameName}#${this.tagLine}`; + } +} + +interface Elo { + tier: string, + rank: string, + points: number +} + +export function eloToNumber(elo?: Elo){ + if (!elo) + return 0; + let tiers: Record = { + "CHALLENGER": 2800, + "GRANDMASTER": 2800, + "MASTER": 2800, + "DIAMOND": 2400, + "EMERALD": 2000, + "PLATINUM": 1600, + "GOLD": 1200, + "SILVER": 800, + "BRONZE": 400, + "IRON": 0, + }; + + let ranks: Record = { + "I": 300, + "II": 200, + "III": 100, + "IV": 0, + }; + + let tier = elo.tier; + if (tier === "MASTER" || tier === "GRANDMASTER" || tier === "CHALLENGER") + return tiers[tier] + elo.points; + + return tiers[tier] + ranks[elo.rank] + elo.points; +} \ No newline at end of file diff --git a/src/bot.ts b/src/bot.ts index 1b7bab1..41b8e05 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -1,115 +1,128 @@ import {Client, Collection, Events, GatewayIntentBits, Interaction} from "discord.js" -import {Player} from "./player"; -import fs from "node:fs"; +import {Account} from "./account"; import {Command, loadCommands} from "./command"; +import {getMySQLConnection} from "./util"; +import {RowDataPacket} from "mysql2/promise"; require("./util.js"); export class UEMEloBot extends Client { - servers = new Collection(); - fileName = "players.json" commands = new Collection(); constructor() { super({intents: [GatewayIntentBits.Guilds]}); this.once(Events.ClientReady, this.onReady); this.on(Events.InteractionCreate, this.onInteractionCreate); - this.parsePlayersFromFile(); loadCommands().then(async commands => { this.commands = commands; const token = await this.login(); console.log(`Discord Token: ${token}`); - setInterval(this.updatePlayers.bind(this), 20 * 60 * 1000); - await this.updatePlayers(); + await this.addAccount("benjo", "tgm"); + await this.addAccount("ag infinity", "euw"); }); } async onInteractionCreate(interaction: Interaction) { if (!interaction.isChatInputCommand()) return; + + // TODO only continue to uem server + let commandName = interaction.commandName; let command = this.commands.get(commandName) as Command; await command.execute(interaction); } - parsePlayersFromFile(){ - // TODO get players from sql instead - if (fs.existsSync(this.fileName)){ - let fileContent = fs.readFileSync(this.fileName).toString(); - try { - this.servers.clear(); - for (let [serverID, players] of Object.entries(JSON.parse(fileContent))){ - console.log(`Server: ${serverID}`); - let playersList: Player[] = []; - for (let obj of (players as Object[])){ - let p = Player.Load(obj); - playersList.push(p); - console.log(`Parsed player: ${p}`); - } - this.servers.set(serverID, playersList); - } - } catch (error) { - console.error(`Failed to parse players: ${error}`); - } + async updateAccounts(){ + let accounts = await this.getProgressedAccountList(); + for (let account of accounts){ + await account.update(); } } - savePlayersToFile(){ - fs.writeFileSync(this.fileName, JSON.stringify(Object.fromEntries(this.servers), null, 4)); - } + async addAccount(gameName: string, tagLine: string) { + let account = await Account.fetchFromRiotID(gameName, tagLine); + if (!account) + return; - async updatePlayers(){ - for (let [_, players] of this.servers){ - for (let p of players){ - await p.updateFullName(); - await p.updateCurrentElo(); - if (!p.startElo && p.currentElo){ - p.startElo = p.currentElo; - console.log(`Updated start elo for ${p}`); - } - } + if (!await account.isAdded()){ + await account.addToDB(); + await account.update(); + return account; + } else { + console.error(`Account ${account} already added.`); + return; } - this.savePlayersToFile(); } - async addPlayer(gameName: string, tagLine: string, serverID: string){ - let player = await Player.TryCreateFrom(gameName, tagLine); - - if (!player) + async removeAccount(gameName: string, tagLine: string) { + let account = await Account.fetchFromRiotID(gameName, tagLine); + if (!account) return; - if (this.servers.ensure(serverID, () => []).find(p => p.puuid === player.puuid)) { - console.error(`${player} already registered!`); + if (await account.isAdded()){ + await account.removeFromDB(); + return account; + } else { + console.error(`Account ${account} was not added.`); return; } - - this.servers.ensure(serverID, () => []).push(player); - console.log(`Added ${player} on Server: ${serverID}!`); - this.savePlayersToFile(); - return player; } - async removePlayer(gameName: string, tagLine: string, serverID: string){ - let playerCopy = await Player.TryCreateFrom(gameName, tagLine); + async getAccountMap(){ + const conn = await getMySQLConnection(); - if (!playerCopy){ - console.error(`Tried to remove non-existent Riot ID ${gameName}#${tagLine}`) - return; + const [sqlAccounts] = await conn.query(`SELECT * FROM accounts`); + + let accounts = new Collection(); + + for (const sqlAccount of sqlAccounts){ + let account = new Account(sqlAccount["puuid"]); + account.gameName = sqlAccount["gameName"]; + account.tagLine = sqlAccount["tagLine"]; + accounts.set(account.puuid, account); } - let serverPlayers = this.servers.ensure(serverID, () => []); + return accounts; + } - let player = serverPlayers.find(p => p.puuid === playerCopy.puuid); - if (!player){ - console.error(`Tried to remove non-added player ${playerCopy}!`); - return; + async getProgressedAccountList() { + const conn = await getMySQLConnection(); + + const [[, starts, ends]] = await conn.query(` + CREATE TEMPORARY TABLE borders AS + SELECT MIN(date) AS start, MAX(date) AS end, accountId, gameName, tagLine, puuid + FROM accounts JOIN elo_entries ON id = accountId + GROUP BY accountId, gameName, tagLine, puuid; + + SELECT tier, \`rank\`, points, gameName, tagLine, puuid + FROM elo_entries JOIN borders ON elo_entries.date = start AND borders.accountId = elo_entries.accountId; + + SELECT tier, \`rank\`, points, gameName, tagLine, puuid + FROM elo_entries JOIN borders ON elo_entries.date = end AND borders.accountId = elo_entries.accountId; + `); + + let accounts = await this.getAccountMap(); + + for (const start of starts){ + let account = accounts.get(start["puuid"]) as Account; + account.startElo = { + rank: start["rank"], + tier: start["tier"], + points: start["points"] + }; + } + + for (const end of ends){ + let account = accounts.get(end["puuid"]) as Account; + account.currentElo = { + rank: end["rank"], + tier: end["tier"], + points: end["points"] + }; } - serverPlayers.splice(serverPlayers.indexOf(player), 1); - this.servers.set(serverID, serverPlayers); - console.log(`Removed ${player}!`); - this.savePlayersToFile(); - return player; + return Array.from(accounts.values()); } async onReady(readyClient: Client){ diff --git a/src/commands/add.ts b/src/commands/add.ts index c57353d..d1c36df 100644 --- a/src/commands/add.ts +++ b/src/commands/add.ts @@ -1,33 +1,26 @@ -import {Interaction, PermissionsBitField} from "discord.js"; +import {ChatInputCommandInteraction, PermissionsBitField} from "discord.js"; import {Command} from "../command"; import {UEMEloBot} from "../bot"; -import {checkServer} from "../util"; class Add extends Command { constructor() { super("add", "Spieler hinzufügen"); this.data.addStringOption(option => { - return option.setName("riot-id").setDescription("Riot ID: 'Name#Tag'").setRequired(true) + return option.setName("riot-id").setDescription("Riot ID: 'Name#Tag'").setRequired(true); }).setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator); } - async execute(interaction: Interaction) { - if (!interaction.isChatInputCommand()) - return; - + async execute(interaction: ChatInputCommandInteraction) { console.log("Add command used"); let client = interaction.client as UEMEloBot; const riotId = interaction.options.getString("riot-id", true); const [gameName, tagLine] = riotId.split("#"); - const serverID = await checkServer(interaction); - if (!serverID) - return; - const player = await client.addPlayer(gameName, tagLine, serverID); - if (player) - await interaction.reply({content: `${player} hinzugefügt!`, ephemeral: true}); + const account = await client.addAccount(gameName, tagLine); + if (account) + await interaction.reply({content: `${account} hinzugefügt!`, ephemeral: true}); else await interaction.reply({content: "Fehler!", ephemeral: true}); } diff --git a/src/commands/list.ts b/src/commands/list.ts index 20a9115..a4e6e83 100644 --- a/src/commands/list.ts +++ b/src/commands/list.ts @@ -1,41 +1,34 @@ -import {Interaction, EmbedBuilder, APIEmbedField, codeBlock} from "discord.js"; +import {ChatInputCommandInteraction, EmbedBuilder, APIEmbedField, codeBlock} from "discord.js"; import {Command} from "../command"; import {UEMEloBot} from "../bot"; -import {eloToNumber, Player} from "../player"; +import {eloToNumber, Account} from "../account"; import {checkServer} from "../util"; class List extends Command { constructor() { super("list", "Spieler auflisten"); } - async execute(interaction: Interaction) { - if (!interaction.isChatInputCommand()) - return; - + async execute(interaction: ChatInputCommandInteraction) { console.log("List command used"); - const serverID = await checkServer(interaction); - if (!serverID) - return; - - let players = (interaction.client as UEMEloBot).servers.ensure(serverID, () => []); - if (players.length === 0){ + let accounts = await (interaction.client as UEMEloBot).getProgressedAccountList(); + if (accounts.length === 0){ await interaction.reply("0 Spieler registriert!"); return; } - const maxNameLength = players.reduce((max: number, next: Player) => Math.max(max, next.toString().length), 0); - const maxProgressDigits = players.reduce((max, next) => Math.max(max, Math.abs(next.getProgress()).toString().length), 0); + const maxNameLength = accounts.reduce((max: number, next: Account) => Math.max(max, next.toString().length), 0); + const maxProgressDigits = accounts.reduce((max, next) => Math.max(max, Math.abs(next.getProgress()).toString().length), 0); // sort by progress, then current elo, descending - players.sort((a, b) => { + accounts.sort((a, b) => { const diff = b.getProgress() - a.getProgress(); if (diff != 0) return diff; return eloToNumber(b.currentElo) - eloToNumber(a.currentElo); }); - let ranking = players.reduce((before, player, index) => { + let ranking = accounts.reduce((before, player, index) => { const placement = `${index + 1})`.padStart(3, '0'); const fill = "-".repeat(maxNameLength - `${player}`.length + 2); const progress = Intl.NumberFormat(undefined, { @@ -47,13 +40,13 @@ class List extends Command { ranking = ranking.trimEnd(); const baseURL = "https://raw.communitydragon.org/latest/plugins/rcp-fe-lol-shared-components/global/default/"; - const fileName = (players[0].currentElo?.tier.toLowerCase() ?? "unranked") + ".png"; + const fileName = (accounts[0].currentElo?.tier.toLowerCase() ?? "unranked") + ".png"; const iconURL = `${baseURL}${fileName}`; const embed = new EmbedBuilder() .setTitle("UEM-Elo-Challenge Ranking") .setFields([ - {name: "Leader:", value: players[0].toString()}, + {name: "Leader:", value: accounts[0].toString()}, {name: " ", value: codeBlock(ranking)} ]) .setThumbnail(iconURL); diff --git a/src/commands/ping.ts b/src/commands/ping.ts index 76157ca..f504023 100644 --- a/src/commands/ping.ts +++ b/src/commands/ping.ts @@ -1,14 +1,11 @@ -import {Interaction} from "discord.js"; +import {ChatInputCommandInteraction} from "discord.js"; import {Command} from "../command"; class Ping extends Command { constructor() { super("ping", "Pong"); } - async execute(interaction: Interaction) { - if (!interaction.isChatInputCommand()) - return; - + async execute(interaction: ChatInputCommandInteraction) { console.log("Ping command used"); await interaction.reply({content: "Pong", ephemeral: true}); diff --git a/src/commands/remove.ts b/src/commands/remove.ts index e4ed9d4..3559008 100644 --- a/src/commands/remove.ts +++ b/src/commands/remove.ts @@ -1,33 +1,26 @@ -import {Interaction, PermissionsBitField} from "discord.js"; +import {ChatInputCommandInteraction, PermissionsBitField} from "discord.js"; import {Command} from "../command"; import {UEMEloBot} from "../bot"; -import {checkServer} from "../util"; class Remove extends Command { constructor() { super("remove", "Spieler entfernen"); this.data.addStringOption(option => { - return option.setName("riot-id").setDescription("Riot ID: 'Name#Tag'").setRequired(true) + return option.setName("riot-id").setDescription("Riot ID: 'Name#Tag'").setRequired(true); }).setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator); } - async execute(interaction: Interaction) { - if (!interaction.isChatInputCommand()) - return; - + async execute(interaction: ChatInputCommandInteraction) { console.log("Remove command used"); let client = interaction.client as UEMEloBot; const riotId = interaction.options.getString("riot-id", true); const [gameName, tagLine] = riotId.split("#"); - const serverID = await checkServer(interaction); - if (!serverID) - return; - const player = await client.removePlayer(gameName, tagLine, serverID); - if (player) - await interaction.reply({content: `${player} entfernt!`, ephemeral: true}); + const account = await client.removeAccount(gameName, tagLine); + if (account) + await interaction.reply({content: `${account} entfernt!`, ephemeral: true}); else await interaction.reply({content: "Fehler!", ephemeral: true}); } diff --git a/src/commands/update.ts b/src/commands/update.ts index 5eedcee..01105eb 100644 --- a/src/commands/update.ts +++ b/src/commands/update.ts @@ -1,4 +1,4 @@ -import {Interaction} from "discord.js"; +import {ChatInputCommandInteraction} from "discord.js"; import {Command} from "../command"; import {UEMEloBot} from "../bot"; @@ -6,16 +6,13 @@ class Update extends Command { constructor() { super("update", "Ränge jetzt aktualisieren (passiert automatisch alle 20min)"); } - async execute(interaction: Interaction) { - if (!interaction.isChatInputCommand()) - return; - + async execute(interaction: ChatInputCommandInteraction) { console.log("Update command used"); await interaction.deferReply({ephemeral: true}); let client = interaction.client as UEMEloBot; - await client.updatePlayers(); + await client.updateAccounts(); await interaction.editReply({content: "Aktualisiert!"}); } diff --git a/src/index.ts b/src/index.ts index b9a427c..54adc1e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,4 +10,5 @@ readline.createInterface({input: process.stdin}).on('line', async line => { const client = new UEMEloBot(); + process.on("SIGINT", () => client.stop()); \ No newline at end of file diff --git a/src/player.ts b/src/player.ts deleted file mode 100644 index 6eb08d6..0000000 --- a/src/player.ts +++ /dev/null @@ -1,144 +0,0 @@ -import {PlatformId, RiotAPI, RiotAPITypes} from "@fightmegg/riot-api" -import AccountDTO = RiotAPITypes.Account.AccountDTO; - -const riotAPI = new RiotAPI(process.env["RIOT_API_KEY"] as string); - -export class Player { - - puuid: string - - gameName?: string - tagLine?: string - startElo?: Elo - currentElo?: Elo - - constructor(puuid: string) { - this.puuid = puuid; - } - - static Load(raw: any): Player { - let p = new Player(raw.puuid); - p.startElo = raw.startElo; - p.gameName = raw.gameName; - p.tagLine = raw.tagLine; - return p; - } - - static async TryCreateFrom(gameName: string, tagLine: string) { - let account: AccountDTO; - try { - account = await riotAPI.account.getByRiotId({ - region: PlatformId.EUROPE, - gameName: gameName, - tagLine: tagLine - }); - } catch (error) { - console.error(`Riot ID not found: ${gameName}#${tagLine}!`); - return; - } - let p = new Player(account.puuid); - p.gameName = account.gameName; - p.tagLine = account.tagLine; - await p.updateCurrentElo(); - p.startElo = p.currentElo; - return p; - } - - async updateFullName(){ - const account = await riotAPI.account.getByPUUID({ - region: PlatformId.EUROPE, - puuid: this.puuid - }); - this.gameName = account.gameName; - this.tagLine = account.tagLine; - } - - async updateCurrentElo() { - const tryRegion = async (region: RiotAPITypes.LoLRegion) => { - return await riotAPI.league.getEntriesBySummonerId({ - region: region, - summonerId: (await riotAPI.summoner.getByPUUID({ - region: region, - puuid: this.puuid - })).id - }); - }; - - let entries; - - try { - entries = await tryRegion(PlatformId.EUW1); - } catch (error){ - try { - entries = await tryRegion(PlatformId.EUNE1); - } catch (error){ - let response = error as Response; - console.error(await response.json()); - return; - } - } - - let soloQ = entries.find(e => e.queueType == "RANKED_SOLO_5x5"); - if (soloQ && ((soloQ.wins + soloQ.losses) >= 5)){ - this.currentElo = { - tier: soloQ.tier, - rank: soloQ.rank, - points: soloQ.leaguePoints - }; - } - } - - getProgress(){ - return eloToNumber(this.currentElo) - eloToNumber(this.startElo); - } - - toString() { - return `${this.gameName}#${this.tagLine}`; - } - - // noinspection JSUnusedGlobalSymbols - toJSON(){ - return { - puuid: this.puuid, - gameName: this.gameName, - tagLine: this.tagLine, - startElo: this.startElo - } - } -} - -interface Elo { - tier: string, - rank: string, - points: number -} - -export function eloToNumber(elo: Elo | undefined){ - if (!elo) - return 0; - let tiers: Record = { - "CHALLENGER": 2800, - "GRANDMASTER": 2800, - "MASTER": 2800, - "DIAMOND": 2400, - "EMERALD": 2000, - "PLATINUM": 1600, - "GOLD": 1200, - "SILVER": 800, - "BRONZE": 400, - "IRON": 0, - }; - - let ranks: Record = { - "I": 300, - "II": 200, - "III": 100, - "IV": 0, - }; - - let tier = elo.tier; - if (tier === "MASTER" || tier === "GRANDMASTER" || tier === "CHALLENGER") - return tiers[tier] + elo.points; - - return tiers[tier] + ranks[elo.rank] + elo.points; -} \ No newline at end of file diff --git a/src/util.ts b/src/util.ts index 12966ca..b24508b 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,4 +1,5 @@ import {ChatInputCommandInteraction} from "discord.js"; +import mysql from "mysql2/promise"; export async function checkServer(interaction: ChatInputCommandInteraction){ const serverID = interaction.guild?.id; @@ -22,4 +23,14 @@ console.log = function(...args) { console.error = function(...args) { originalError(`[${getTimestamp()}]`, ...args); -}; \ No newline at end of file +}; + +export async function getMySQLConnection(){ + return await mysql.createConnection({ + port: parseInt(process.env["MYSQL_PORT"] ?? "3306"), + user: process.env["MYSQL_USER"], + password: process.env["MYSQL_PASSWORD"], + database: process.env["MYSQL_DATABASE"], + multipleStatements: true + }); +} \ No newline at end of file