diff --git a/README.md b/README.md index 7e6ee41d..2fecf59b 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ You can look at all of the libraries I use by going to `package.json` in the mai - [rosu-pp's JavaScript bind](https://github.com/MaxOhn/rosu-pp-js) to calculate pp, bpm values, other technical stuff of osu! -Invite the bot to your server using [this link](https://discord.com/api/oauth2/authorize?client_id=995999045157916763&permissions=330752&scope=bot) +Invite the bot to your guild using [this link](https://discord.com/api/oauth2/authorize?client_id=995999045157916763&permissions=330752&scope=bot) ## Commands ⭐ @@ -42,7 +42,7 @@ These are the bare-bone ones. - `/top` get a user's osu! top plays -use `/help` in your server for more information. +use `/help` in your guild for more information. ## Contributing πŸ˜Άβ€πŸŒ«οΈ diff --git a/src/cleaners/scores.ts b/src/cleaners/scores.ts index 4c10b186..1b13db17 100644 --- a/src/cleaners/scores.ts +++ b/src/cleaners/scores.ts @@ -1,6 +1,7 @@ import { accuracyCalculator, getPerformanceResults, getRetryCount, hitValueCalculator } from "@utils/osu"; import { grades, rulesets } from "@utils/emotes"; import { insertData } from "@utils/database"; +import { Tables } from "@type/database"; import type { Mode, UserScore, Beatmap, LeaderboardScores, PlayStatistics, ScoresInfo, Score, UserBestScore } from "@type/osu"; import type { ISOTimestamp } from "osu-web.js"; @@ -75,19 +76,19 @@ export async function getScore({ scores, beatmap: map_, index, mode, mapData }: if (play.passed && "score" in play) { insertData({ - table: "osu_scores_pp", + table: Tables.PP, id: play.id, data: [ { - name: "pp", + key: "pp", value: current.pp }, { - name: "pp_fc", + key: "pp_fc", value: fc.pp }, { - name: "pp_perfect", + key: "pp_perfect", value: perfect.pp } ] diff --git a/src/commands/general/config.ts b/src/commands/general/config.ts index 89fed039..f24a3a30 100644 --- a/src/commands/general/config.ts +++ b/src/commands/general/config.ts @@ -1,5 +1,5 @@ -import { getUser, insertData } from "@utils/database"; -import { ScoreEmbed } from "@type/database"; +import { getEntry, insertData } from "@utils/database"; +import { ScoreEmbed, Tables } from "@type/database"; import { ApplicationCommandOptionType } from "lilybird"; import type { ApplicationCommandData, GuildInteraction } from "@lilybird/transformers"; import type { EmbedStructure } from "lilybird"; @@ -89,9 +89,9 @@ async function list(interaction: GuildInteraction): Prom }; const userId = interaction.member.user.id; - let user = getUser(userId); + let user = getEntry(Tables.USER, userId); if (!user) { - insertData({ table: "users", id: userId, data: [ { name: "banchoId", value: null } ] }); + insertData({ table: Tables.USER, id: userId, data: [ { key: "banchoId", value: null } ] }); user = { banchoId: null, mode: null, score_embeds: null, embed_type: null, id: userId }; } const embeds: EmbedStructure = { fields: [], title: `Config settings of ${interaction.member.user.username}` }; @@ -110,13 +110,13 @@ async function list(interaction: GuildInteraction): Prom } function mode(memberId: string, choice: string): void { - insertData({ table: "users", id: memberId, data: [ { name: "mode", value: choice } ] }); + insertData({ table: Tables.USER, id: memberId, data: [ { key: "mode", value: choice } ] }); } function scoreEmbed(memberId: string, choice: number): void { - insertData({ table: "users", id: memberId, data: [ { name: "score_embeds", value: choice } ] }); + insertData({ table: Tables.USER, id: memberId, data: [ { key: "score_embeds", value: choice } ] }); } function embedType(memberId: string, choice: string): void { - insertData({ table: "users", id: memberId, data: [ { name: "embed_type", value: choice } ] }); + insertData({ table: Tables.USER, id: memberId, data: [ { key: "embed_type", value: choice } ] }); } diff --git a/src/commands/general/prefix.ts b/src/commands/general/prefix.ts index 2bd768a5..7f61c431 100644 --- a/src/commands/general/prefix.ts +++ b/src/commands/general/prefix.ts @@ -1,6 +1,7 @@ -import { getServer, insertData } from "@utils/database"; +import { getEntry, insertData } from "@utils/database"; import { DEFAULT_PREFIX, MAX_AMOUNT_OF_PREFIXES } from "@utils/constants"; import { prefixesCache } from "@listeners/guildCreate"; +import { Tables } from "@type/database"; import { ApplicationCommandOptionType, EmbedType, PermissionFlags } from "lilybird"; import type { ApplicationCommandData, GuildInteraction } from "@lilybird/transformers"; import type { SlashCommand } from "@type/commands"; @@ -75,9 +76,9 @@ async function add({ prefix, interaction, guildId }: { prefix?: string, interact if (checkPerms === false) return; - const server = getServer(guildId); - if (!prefix || !server) return; - let { prefixes } = server; + const guild = getEntry(Tables.GUILD, guildId); + if (typeof prefix === "undefined" || guild === null) return; + let { prefixes } = guild; if (prefixes !== null && !Array.isArray(prefixes)) prefixes = JSON.parse(prefixes) as Array; @@ -94,7 +95,7 @@ async function add({ prefix, interaction, guildId }: { prefix?: string, interact const newPrefixes = prefixes === null ? [prefix] : [...prefixes, prefix]; - insertData({ table: "servers", id: guildId, data: [ { name: "prefixes", value: JSON.stringify(newPrefixes) } ] }); + insertData({ table: Tables.GUILD, id: guildId, data: [ { key: "prefixes", value: JSON.stringify(newPrefixes) } ] }); prefixesCache.set(guildId, newPrefixes); await interaction.editReply(`**The prefix \`${prefix}\` has been added to the list.**`); @@ -106,12 +107,12 @@ async function remove({ prefix, interaction, guildId }: { prefix?: string, inter if (checkPerms === false) return; - const server = getServer(guildId); - if (!prefix || !server) return; - const { prefixes } = server; + const guild = getEntry(Tables.GUILD, guildId); + if (typeof prefix === "undefined" || guild === null) return; + const { prefixes } = guild; if (prefixes === null) { - await interaction.editReply("**There aren't any prefixes on this server. You can add a new prefixes by using `/prefix add`**"); + await interaction.editReply("**There aren't any prefixes on this guild. You can add a new prefixes by using `/prefix add`**"); return; } @@ -121,27 +122,27 @@ async function remove({ prefix, interaction, guildId }: { prefix?: string, inter } const newPrefixes = prefixes.filter((item) => item !== prefix); - insertData({ table: "servers", id: guildId, data: [ { name: "prefixes", value: newPrefixes.length > 0 ? JSON.stringify(newPrefixes) : null } ] }); + insertData({ table: Tables.GUILD, id: guildId, data: [ { key: "prefixes", value: newPrefixes.length > 0 ? JSON.stringify(newPrefixes) : null } ] }); prefixesCache.set(guildId, newPrefixes.length > 0 ? newPrefixes : DEFAULT_PREFIX); let message = `**The prefix \`${prefix}\` has been removed from the list.**`; if (newPrefixes.length === 0) - message = `**__Warning__:\nThere are no more custom prefixes on the server left. The default prefix is \`${DEFAULT_PREFIX.join("")}\`**`; + message = `**__Warning__:\nThere are no more custom prefixes on the guild left. The default prefix is \`${DEFAULT_PREFIX.join("")}\`**`; await interaction.editReply(message); return; } async function list({ interaction, guildId }: { interaction: GuildInteraction, guildId: string }): Promise { - const server = getServer(guildId); - if (!server) return; + const guild = getEntry(Tables.GUILD, guildId); + if (guild === null) return; - const { prefixes } = server; + const { prefixes } = guild; if (prefixes === null) { - await interaction.editReply(`**There aren't any custom prefixes on this server. The default is \`${DEFAULT_PREFIX.join("")}\`**`); + await interaction.editReply(`**There aren't any custom prefixes on this guild. The default is \`${DEFAULT_PREFIX.join("")}\`**`); return; } - await interaction.editReply({ embeds: [ { type: EmbedType.Rich, title: "Prefixes!", description: `- \`${prefixes.join("`\n- `")}\`` } ] }); + await interaction.editReply({ embeds: [ { type: EmbedType.Rich, title: "Currently defined prefixes", description: `**\`${prefixes.join("`**, `")}\`**` } ] }); } diff --git a/src/commands/osu/unlink.ts b/src/commands/osu/unlink.ts index 07922dd7..76753cba 100644 --- a/src/commands/osu/unlink.ts +++ b/src/commands/osu/unlink.ts @@ -1,5 +1,6 @@ -import { getUser, removeUser } from "@utils/database"; +import { getEntry, removeEntry } from "@utils/database"; import { slashCommandsIds } from "@utils/cache"; +import { Tables } from "@type/database"; import type { SlashCommand } from "@type/commands"; import type { ApplicationCommandData, GuildInteraction } from "@lilybird/transformers"; @@ -13,12 +14,12 @@ async function run(interaction: GuildInteraction): Promi const linkCommand = slashCommandsIds.get("link"); const userId = interaction.member.user.id; - const user = getUser(userId); + const user = getEntry(Tables.USER, userId); if (!user?.banchoId) { await interaction.editReply(`You are not linked to the bot! You can link yourself using ${linkCommand}, if you want.`); return; } - removeUser(userId); + removeEntry(Tables.USER, userId); await interaction.editReply(`Sad to see you go :(\nYou can always re-link yourself using ${linkCommand}!`); } diff --git a/src/commands/owner/owner.ts b/src/commands/owner/owner.ts index eca8f246..b952e798 100644 --- a/src/commands/owner/owner.ts +++ b/src/commands/owner/owner.ts @@ -1,4 +1,5 @@ -import { getServer, query } from "@utils/database"; +import { getEntry, query } from "@utils/database"; +import { Tables } from "@type/database"; import { ApplicationCommandOptionType } from "lilybird"; import type { ApplicationCommandData, GuildInteraction } from "@lilybird/transformers"; import type { SlashCommand } from "@type/commands"; @@ -73,7 +74,7 @@ ${response} async function servers(interaction: GuildInteraction): Promise { const id = interaction.data.getString("id", true); - const guild = getServer(id); + const guild = getEntry(Tables.GUILD, id); if (guild === null) { await interaction.editReply("Server not found!"); return; diff --git a/src/embed-builders/compare.ts b/src/embed-builders/compare.ts index b40865d4..7d9bfff2 100644 --- a/src/embed-builders/compare.ts +++ b/src/embed-builders/compare.ts @@ -1,8 +1,9 @@ import { getProfile } from "@cleaners/profile"; import { getScore } from "@cleaners/scores"; import { SPACE } from "@utils/constants"; -import { getMap } from "@utils/database"; +import { getEntry } from "@utils/database"; import { downloadBeatmap, saveScoreDatas } from "@utils/osu"; +import { Tables } from "@type/database"; import { EmbedType } from "lilybird"; import type { CompareBuilderOptions } from "@type/embedBuilders"; import type { EmbedStructure } from "lilybird"; @@ -59,7 +60,7 @@ async function getMultiplePlays({ plays, profile, beatmap, mode }: mode: Mode }): Promise> { const beatmapId = beatmap.id; - const mapData = getMap(beatmapId)?.data ?? (await downloadBeatmap(beatmapId)).contents; + const mapData = getEntry(Tables.MAP, beatmapId)?.data ?? (await downloadBeatmap(beatmapId)).contents; const playsTemp: Array> = []; for (let i = 0; i < plays.length; i++) playsTemp.push(getScore({ scores: plays, index: i, mode, beatmap, mapData })); diff --git a/src/embed-builders/leaderboard.ts b/src/embed-builders/leaderboard.ts index 31e107a9..c581db6b 100644 --- a/src/embed-builders/leaderboard.ts +++ b/src/embed-builders/leaderboard.ts @@ -1,7 +1,8 @@ import { getScore } from "@cleaners/scores"; import { SPACE } from "@utils/constants"; -import { getMap } from "@utils/database"; +import { getEntry } from "@utils/database"; import { downloadBeatmap } from "@utils/osu"; +import { Tables } from "@type/database"; import { EmbedType } from "lilybird"; import type { LeaderboardBuilderOptions } from "@type/embedBuilders"; import type { EmbedStructure } from "lilybird"; @@ -28,7 +29,7 @@ export async function leaderboardBuilder({ async function getPlays(plays: Array, beatmap: Beatmap, page: number): Promise> { const beatmapId = beatmap.id; const mode = beatmap.mode; - const mapData = getMap(beatmapId)?.data ?? (await downloadBeatmap(beatmapId)).contents; + const mapData = getEntry(Tables.MAP, beatmapId)?.data ?? (await downloadBeatmap(beatmapId)).contents; const pageStart = page * 5; const pageEnd = pageStart + 5; diff --git a/src/embed-builders/map.ts b/src/embed-builders/map.ts index 7151a0ac..b64942af 100644 --- a/src/embed-builders/map.ts +++ b/src/embed-builders/map.ts @@ -1,7 +1,8 @@ import { client } from "@utils/initalize"; import { downloadBeatmap, getPerformanceResults } from "@utils/osu"; -import { getMap } from "@utils/database"; +import { getEntry } from "@utils/database"; import { rulesets } from "@utils/emotes"; +import { Tables } from "@type/database"; import { EmbedType } from "lilybird"; import type { MapBuilderOptions } from "@type/embedBuilders"; import type { EmbedStructure } from "lilybird"; @@ -24,7 +25,7 @@ export async function mapBuilder({ const { beatmapset: mapset, mode, version } = map; - const mapData = getMap(beatmapId)?.data ?? (await downloadBeatmap(beatmapId)).contents; + const mapData = getEntry(Tables.MAP, beatmapId)?.data ?? (await downloadBeatmap(beatmapId)).contents; const performancesAsync = []; const accuracyList = [98, 97, 95]; diff --git a/src/embed-builders/plays.ts b/src/embed-builders/plays.ts index d213a0e4..6b467e51 100644 --- a/src/embed-builders/plays.ts +++ b/src/embed-builders/plays.ts @@ -4,7 +4,7 @@ import { SPACE } from "@utils/constants"; import { EmbedScoreType } from "@type/database"; import { saveScoreDatas } from "@utils/osu"; import { EmbedType } from "lilybird"; -import type { DatabaseUser } from "@type/database"; +import type { User } from "@type/database"; import type { UserScore, Mode, ProfileInfo, ScoresInfo, UserBestScore } from "@type/osu"; import type { PlaysBuilderOptions } from "@type/embedBuilders"; import type { EmbedAuthorStructure, EmbedFieldStructure, EmbedFooterStructure, EmbedImageStructure, EmbedStructure, EmbedThumbnailStructure } from "lilybird"; @@ -75,7 +75,7 @@ async function getSinglePlay({ mode, index, plays, profile, authorDb, isMultiple mode: Mode, profile: ProfileInfo, index: number, - authorDb: DatabaseUser | null, + authorDb: User | null, isMultiple?: boolean }): Promise> { const isMaximized = (authorDb?.score_embeds ?? 1) === 1; @@ -215,7 +215,7 @@ async function getMultiplePlays({ plays, page, mode, profile, authorDb }: page: number, mode: Mode, profile: ProfileInfo, - authorDb: DatabaseUser | null + authorDb: User | null }): Promise> { const embedType = authorDb?.embed_type ?? EmbedScoreType.Hanami; diff --git a/src/embed-builders/simulate.ts b/src/embed-builders/simulate.ts index c190f298..bfe854cd 100644 --- a/src/embed-builders/simulate.ts +++ b/src/embed-builders/simulate.ts @@ -2,9 +2,10 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { client } from "@utils/initalize"; import { accuracyCalculator, downloadBeatmap, getPerformanceResults, gradeCalculator, hitValueCalculator } from "@utils/osu"; -import { getMap } from "@utils/database"; +import { getEntry } from "@utils/database"; import { grades, rulesets } from "@utils/emotes"; import { SPACE } from "@utils/constants"; +import { Tables } from "@type/database"; import { EmbedType } from "lilybird"; import type { Mode } from "@type/osu"; import type { SimulateBuilderOptions } from "@type/embedBuilders"; @@ -31,7 +32,7 @@ export async function simulateBuilder({ const { beatmapset: mapset, mode, version } = map; - const mapData = getMap(beatmapId)?.data ?? (await downloadBeatmap(beatmapId)).contents; + const mapData = getEntry(Tables.MAP, beatmapId)?.data ?? (await downloadBeatmap(beatmapId)).contents; const performance = await getPerformanceResults({ beatmapId, diff --git a/src/listeners/guildCreate.ts b/src/listeners/guildCreate.ts index 3650714c..14d7dfbb 100644 --- a/src/listeners/guildCreate.ts +++ b/src/listeners/guildCreate.ts @@ -1,4 +1,6 @@ -import { getServer, insertData, removeServer } from "@utils/database"; +import { getEntry, insertData, removeEntry } from "@utils/database"; +import { Tables } from "@type/database"; +import type { Guild } from "@type/database"; import type { Event } from "@lilybird/handlers"; export const prefixesCache = new Map>(); @@ -6,24 +8,25 @@ export const prefixesCache = new Map>(); export default { event: "guildCreate", run: (guild) => { + // Remove guild from database if it's unavailable. if (!("name" in guild)) { - removeServer(guild.id); + removeEntry(Tables.GUILD, guild.id); return; } - const document = getServer(guild.id); + const document = getEntry(Tables.GUILD, guild.id); - const data: Array<{ name: string, value: string | number | null }> = [ - { name: "name", value: guild.name }, - { name: "owner_id", value: guild.ownerId }, - { name: "joined_at", value: guild.joinedAt } + const data: Array<{ key: keyof Guild, value: string | number | null }> = [ + { key: "name", value: guild.name }, + { key: "owner_id", value: guild.ownerId }, + { key: "joined_at", value: guild.joinedAt } ]; if (document === null) - data.push({ name: "prefixes", value: null }); + data.push({ key: "prefixes", value: null }); insertData({ - table: "servers", + table: Tables.GUILD, id: guild.id, data }); diff --git a/src/listeners/guildDelete.ts b/src/listeners/guildDelete.ts index 0a377146..0fa4fdc6 100644 --- a/src/listeners/guildDelete.ts +++ b/src/listeners/guildDelete.ts @@ -1,4 +1,5 @@ -import { removeServer } from "@utils/database"; +import { removeEntry } from "@utils/database"; +import { Tables } from "@type/database"; import type { Event } from "@lilybird/handlers"; export const prefixesCache = new Map>(); @@ -6,6 +7,6 @@ export const prefixesCache = new Map>(); export default { event: "guildDelete", run: (_, guild) => { - removeServer(guild.id); + removeEntry(Tables.GUILD, guild.id); } } satisfies Event<"guildDelete">; diff --git a/src/listeners/interactionCreate.ts b/src/listeners/interactionCreate.ts index 722f9577..4c7d2842 100644 --- a/src/listeners/interactionCreate.ts +++ b/src/listeners/interactionCreate.ts @@ -1,5 +1,5 @@ import { compareBuilder, leaderboardBuilder, mapBuilder, playBuilder, profileBuilder } from "../embed-builders"; -import { getCommand, insertData } from "@utils/database"; +import { getEntry, insertData } from "@utils/database"; import { client, applicationCommands, loadLogs } from "@utils/initalize"; import { mesageDataForButtons } from "@utils/cache"; import { EmbedBuilderType } from "@type/embedBuilders"; @@ -8,6 +8,7 @@ import { backgroundBuilder } from "@builders/background"; import { avatarBuilder } from "@builders/avatar"; import { bannerBuilder } from "@builders/banner"; import { simulateBuilder } from "@builders/simulate"; +import { Tables } from "@type/database"; import type { DMInteraction, Interaction, InteractionReplyOptions, Message, MessageComponentData } from "@lilybird/transformers"; import type { EmbedStructure } from "lilybird"; import type { Event } from "@lilybird/handlers"; @@ -28,28 +29,28 @@ async function run(interaction: Interaction): Promise { try { await command.run(interaction); - const server = await interaction.client.rest.getGuild(interaction.guildId); - await loadLogs(`INFO: [${server.name}] ${username} used slash command \`${command.data.name}\`${interaction.data.subCommand ? ` -> \`${interaction.data.subCommand}\`` : ""}`); + const guild = await interaction.client.rest.getGuild(interaction.guildId); + await loadLogs(`INFO: [${guild.name}] ${username} used slash command \`${command.data.name}\`${interaction.data.subCommand ? ` -> \`${interaction.data.subCommand}\`` : ""}`); - const docs = getCommand(interaction.data.name); + const docs = getEntry(Tables.COMMAND_SLASH, interaction.data.name); if (docs === null) - insertData({ table: "commands_slash", data: [ { name: "count", value: 1 } ], id: command.data.name }); + insertData({ table: Tables.COMMAND_SLASH, data: [ { key: "count", value: 1 } ], id: command.data.name }); else - insertData({ table: "commands_slash", data: [ { name: "count", value: Number(docs.count ?? 0) + 1 } ], id: docs.id }); + insertData({ table: Tables.COMMAND_SLASH, data: [ { key: "count", value: Number(docs.count ?? 0) + 1 } ], id: docs.id }); } catch (error) { - const server = await interaction.client.rest.getGuild(interaction.guildId); + const guild = await interaction.client.rest.getGuild(interaction.guildId); const err = error as Error; console.log(error); await loadLogs( - `ERROR: [${server.name}] ${username} had an error in slash command \`${command.data.name}\`${interaction.data.subCommand ? ` -> \`${interaction.data.subCommand}\`` : ""}: ${err.stack}`, + `ERROR: [${guild.name}] ${username} had an error in slash command \`${command.data.name}\`${interaction.data.subCommand ? ` -> \`${interaction.data.subCommand}\`` : ""}: ${err.stack}`, true ); - const docs = getCommand(interaction.data.name); + const docs = getEntry(Tables.COMMAND_SLASH, interaction.data.name); if (docs === null) - insertData({ table: "commands_slash", data: [ { name: "count", value: 1 } ], id: command.data.name }); + insertData({ table: Tables.COMMAND_SLASH, data: [ { key: "count", value: 1 } ], id: command.data.name }); else - insertData({ table: "commands_slash", data: [ { name: "count", value: Number(docs.count ?? 0) + 1 } ], id: docs.id }); + insertData({ table: Tables.COMMAND_SLASH, data: [ { key: "count", value: Number(docs.count ?? 0) + 1 } ], id: docs.id }); } } } @@ -215,7 +216,7 @@ async function handleVerify(interaction: DMInteraction { try { await command.run({ client: client, message, args, prefix, index, commandName, channel }); - const server = await client.rest.getGuild(guildId); - await loadLogs(`INFO: [${server.name}] ${author.username} used prefix command \`${command.name}\``); - const docs = getCommand(commandName); + const guild = await client.rest.getGuild(guildId); + await loadLogs(`INFO: [${guild.name}] ${author.username} used prefix command \`${command.name}\``); + const docs = getEntry(Tables.COMMAND, commandName); if (docs === null) - insertData({ table: "commands", data: [ { name: "count", value: 1 } ], id: command.name }); + insertData({ table: Tables.COMMAND, data: [ { key: "count", value: 1 } ], id: command.name }); else - insertData({ table: "commands", data: [ { name: "count", value: Number(docs.count ?? 0) + 1 } ], id: docs.id }); + insertData({ table: Tables.COMMAND, data: [ { key: "count", value: Number(docs.count ?? 0) + 1 } ], id: docs.id }); } catch (error) { const err = error as Error; - const server = await client.rest.getGuild(guildId); + const guild = await client.rest.getGuild(guildId); console.log(err); - await loadLogs(`ERROR: [${server.name}] ${author.username} had an error in prefix command \`${command.name}\`: ${err.stack}`, true); - const docs = getCommand(command.name); + await loadLogs(`ERROR: [${guild.name}] ${author.username} had an error in prefix command \`${command.name}\`: ${err.stack}`, true); + const docs = getEntry(Tables.COMMAND, commandName); if (docs === null) - insertData({ table: "commands", data: [ { name: "count", value: 1 } ], id: command.name }); + insertData({ table: Tables.COMMAND, data: [ { key: "count", value: 1 } ], id: command.name }); else - insertData({ table: "commands", data: [ { name: "count", value: Number(docs.count ?? 0) + 1 } ], id: docs.id }); + insertData({ table: Tables.COMMAND, data: [ { key: "count", value: Number(docs.count ?? 0) + 1 } ], id: docs.id }); } cooldown.set(`${command.name}${author.id}`, Date.now() + command.cooldown); @@ -128,5 +129,5 @@ function verifyUser(message: Message): void { if (!content) return; const [discordId, osuId] = content.split("\n"); - insertData({ table: "users", id: discordId, data: [ { name: "banchoId", value: osuId } ] }); + insertData({ table: Tables.USER, id: discordId, data: [ { key: "banchoId", value: osuId } ] }); } diff --git a/src/types/commandArgs.ts b/src/types/commandArgs.ts index 4f7aa2e9..d3bc459f 100644 --- a/src/types/commandArgs.ts +++ b/src/types/commandArgs.ts @@ -1,4 +1,4 @@ -import type { DatabaseUser } from "./database"; +import type { User as UserDb } from "./database"; import type { Mod } from "osu-web.js"; import type { Mode } from "./osu"; @@ -9,8 +9,8 @@ export const enum UserType { interface BaseUser { type: UserType; - authorDb: DatabaseUser | null; - playerDb?: DatabaseUser | null; + authorDb: UserDb | null; + playerDb?: UserDb | null; beatmapId: string | null; } diff --git a/src/types/database.ts b/src/types/database.ts index 4c37642a..fc29c661 100644 --- a/src/types/database.ts +++ b/src/types/database.ts @@ -1,12 +1,22 @@ import type { Mode } from "./osu"; +export enum Tables { + USER = "users", + GUILD = "servers", + MAP = "maps", + COMMAND = "commands", + COMMAND_SLASH = "commands_slash", + SCORE = "osu_scores", + PP = "osu_scores_pp" +} + export enum EmbedScoreType { Hanami = "hanami", Bathbot = "bathbot", Owo = "owobot" } -export interface DatabaseUser { +export interface User { id: string; banchoId: string | null; score_embeds: number | null; @@ -14,7 +24,7 @@ export interface DatabaseUser { mode: string | null; } -export interface DatabaseGuild { +export interface Guild { id: string; name: string; owner_id: string; @@ -22,17 +32,17 @@ export interface DatabaseGuild { prefixes: Array | null; } -export interface DatabaseMap { +export interface Map { id: string; data: string; } -export interface DatabaseCommands { +export interface Command { id: string; count: string | null; } -export interface DatabaseScores { +export interface Score { id: number; user_id: number; map_id: number; @@ -52,7 +62,7 @@ export interface DatabaseScores { ended_at: string; } -export interface DatabaseScoresPp { +export interface ScorePp { id: number; pp: number; pp_fc: number; @@ -64,3 +74,21 @@ export enum ScoreEmbed { Minimized = 0 } +export type TableToArgument = + T extends "users" ? keyof User : + T extends "servers" ? keyof Guild : + T extends "maps" ? keyof Map : + T extends "commands" ? keyof Command : + T extends "commands_slash" ? keyof Command : + T extends "osu_scores" ? keyof Score : + T extends "osu_scores_pp" ? keyof ScorePp : never; + +export type TableToType = + T extends "users" ? User : + T extends "servers" ? Guild : + T extends "maps" ? Map : + T extends "commands" ? Command : + T extends "commands_slash" ? Command : + T extends "osu_scores" ? Score : + T extends "osu_scores_pp" ? ScorePp : never; + diff --git a/src/types/embedBuilders.ts b/src/types/embedBuilders.ts index 1d675de8..ea399e1e 100644 --- a/src/types/embedBuilders.ts +++ b/src/types/embedBuilders.ts @@ -1,5 +1,5 @@ import type { DifficultyOptions } from "./commandArgs"; -import type { DatabaseUser } from "./database"; +import type { User } from "./database"; import type { UserScore, UserBestScore, Beatmap, LeaderboardScores, Mode, Score } from "./osu"; import type { UserExtended, Mod } from "osu-web.js"; @@ -62,7 +62,7 @@ export interface PlaysBuilderOptions extends BuilderOptions { type: EmbedBuilderType.PLAYS; user: UserExtended; mode: Mode; - authorDb: DatabaseUser | null; + authorDb: User | null; index?: number; isMultiple?: boolean; sortByDate?: boolean; diff --git a/src/utils/args.ts b/src/utils/args.ts index 7d66fd32..ff8e9c88 100644 --- a/src/utils/args.ts +++ b/src/utils/args.ts @@ -1,7 +1,8 @@ -import { getUser } from "./database"; +import { getEntry } from "./database"; import { slashCommandsIds } from "./cache"; import { Mode } from "@type/osu"; import { UserType } from "@type/commandArgs"; +import { Tables } from "@type/database"; import { ModsEnum } from "osu-web.js"; import type { SlashCommandArgs, DifficultyOptions, Mods, PrefixCommandArgs, User } from "@type/commandArgs"; import type { Mod } from "osu-web.js"; @@ -87,9 +88,9 @@ export function getCommandArgs(interaction: GuildInteractiondata.getString("mode") ?? Mode.OSU; let mods: Mods = { @@ -210,7 +211,7 @@ export function parseOsuArguments(message: Message, args: Array, mode: M result.flags[key] = value; } - const userAuthor = getUser(message.author.id); + const userAuthor = getEntry(Tables.USER, message.author.id); if (!result.tempUser && userAuthor?.banchoId) { result.user = { @@ -224,7 +225,7 @@ export function parseOsuArguments(message: Message, args: Array, mode: M const [userArg] = result.tempUser; const discordUserId = (/<@(\d+)>/).exec(userArg)?.[1]; - const discordUser = discordUserId ? getUser(discordUserId) : null; + const discordUser = discordUserId ? getEntry(Tables.USER, discordUserId) : null; const discordId = discordUserId ? discordUser?.banchoId : null; if (discordUserId && !discordId) { diff --git a/src/utils/database.ts b/src/utils/database.ts index 98d6fd6a..02569d7b 100644 --- a/src/utils/database.ts +++ b/src/utils/database.ts @@ -1,30 +1,20 @@ import db from "../data.db" with { type: "sqlite" }; -import type { DatabaseMap, DatabaseGuild, DatabaseUser, DatabaseCommands, DatabaseScores, DatabaseScoresPp } from "@type/database"; +import type { Tables, TableToArgument, TableToType } from "@type/database"; export function query(str: string): unknown { return db.prepare(str).all(); } -export function getUser(id: string | number): DatabaseUser | null { - const data: DatabaseUser | null = db.prepare("SELECT * FROM users WHERE id = ?").get(id) as DatabaseUser | null; - if (typeof data?.score_embeds === "string") data.score_embeds = Number(data.score_embeds); +export function getEntry(table: T, id: string | number): TableToType | null { + const data = db.prepare(`SELECT * FROM ${table} WHERE id = ?`).get(id) as TableToType | null; + if (data !== null && "prefixes" in data && typeof data.prefixes === "string") + data.prefixes = JSON.parse(data.prefixes) as Array; return data; } -export function removeUser(id: string | number): void { - db.prepare("DELETE FROM users WHERE id = ?").run(id); -} - -export function getServer(id: string | number): DatabaseGuild | null { - const data: DatabaseGuild | null = db.prepare("SELECT * FROM servers WHERE id = ?").get(id) as DatabaseGuild | null; - if (typeof data?.prefixes === "string") data.prefixes = JSON.parse(data.prefixes) as Array; - - return data; -} - -export function removeServer(id: string | number): void { - db.prepare("DELETE FROM servers WHERE id = ?").run(id); +export function removeEntry(table: Tables, id: string | number): void { + db.prepare(`DELETE FROM ${table} WHERE id = ?`).run(id); } export function getRowCount(table: string): number { @@ -32,29 +22,25 @@ export function getRowCount(table: string): number { return count["COUNT(*)"]; } -export function getMap(id: string | number): DatabaseMap | null { - return db.prepare("SELECT * FROM maps WHERE id = ?").get(id) as DatabaseMap | null; -} - -export function getCommand(id: string | number): DatabaseCommands | null { - return db.prepare("SELECT * FROM commands WHERE id = ?").get(id) as DatabaseCommands | null; -} - -export function getScores(id: string | number): DatabaseScores | null { - return db.prepare("SELECT * FROM commands WHERE id = ?").get(id) as DatabaseScores | null; -} - -export function getScoresPp(id: string | number): DatabaseScoresPp | null { - return db.prepare("SELECT * FROM commands WHERE id = ?").get(id) as DatabaseScoresPp | null; -} - -export function insertData({ table, id, data }: { table: string, id: string | number, data: Array<{ name: string, value: string | number | boolean | null }> }, ignore?: boolean): void { - const setClause = data.map((item) => `${item.name} = ?`).join(", "); +export function insertData( + { + table, + id, + data + }: + { + table: T, + id: string | number, + data: Array<{ key: TableToArgument, value: string | number | boolean | null }> + }, + ignore?: boolean +): void { + const setClause = data.map((item) => `${item.key} = ?`).join(", "); const values: Array = data.map((item) => item.value); const existingRow = db.prepare(`SELECT * FROM ${table} WHERE id = ?`).get(id); if (!existingRow) { - const fields: Array = data.map((item) => item.name); + const fields: Array> = data.map((item) => item.key); const placeholders = fields.map(() => "?").join(", "); db.prepare(`INSERT OR ${ignore ? "IGNORE" : "REPLACE"} INTO ${table} (id, ${fields.join(", ")}) values (?, ${placeholders});`) @@ -64,19 +50,25 @@ export function insertData({ table, id, data }: { table: string, id: string | nu db.prepare(`UPDATE ${table} SET ${setClause} WHERE id = ?;`) .run(...values, id); } - -export function bulkInsertData(entries: Array<{ table: string, id: string | number, data: Array<{ name: string, value: string | number | boolean | null }> }>, ignore?: boolean): void { +export function bulkInsertData(entries: Array<{ + table: T, + id: string | number, + data: Array<{ key: TableToArgument, value: string | number | boolean | null }>, + ignore?: boolean +}>): void { const insertStatements: Array = []; const values: Array> = []; + // Map values in their respective arrays for (let i = 0; i < entries.length; i++) { - const { table, id, data } = entries[i]; + const { table, id, data, ignore } = entries[i]; const itemValues: Array = data.map((item) => item.value); - insertStatements.push(`INSERT OR ${ignore ? "IGNORE" : "REPLACE"} INTO ${table} (id, ${data.map((item) => item.name).join(", ")}) values (?, ${data.map(() => "?").join(", ")});`); + insertStatements.push(`INSERT OR ${ignore ? "IGNORE" : "REPLACE"} INTO ${table} (id, ${data.map((item) => item.key).join(", ")}) values (?, ${data.map(() => "?").join(", ")});`); values.push([id, ...itemValues]); } + // Prepare for the bulk insertion. db.transaction(() => { for (let i = 0; i < insertStatements.length; i++) db.prepare(insertStatements[i]).run(...values[i]); })(); diff --git a/src/utils/initalize.ts b/src/utils/initalize.ts index f78b55e9..d8601e98 100644 --- a/src/utils/initalize.ts +++ b/src/utils/initalize.ts @@ -1,10 +1,11 @@ import db from "../data.db" with { type: "sqlite" }; import { getAccessToken } from "./osu"; import { slashCommandsIds } from "./cache"; -import { removeServer } from "./database"; +import { removeEntry } from "./database"; +import { Tables } from "@type/database"; import { Client as OsuClient } from "osu-web.js"; import { mkdir, access, readFile, writeFile, readdir } from "node:fs/promises"; -import type { DatabaseGuild } from "@type/database"; +import type { Guild } from "@type/database"; import type { Client as LilybirdClient, POSTApplicationCommandStructure } from "lilybird"; import type { DefaultMessageCommand, DefaultSlashCommand } from "@type/commands"; @@ -86,15 +87,15 @@ export async function loadApplicationCommands(clnt: LilybirdClient): Promise; + const nulledGuilds = db.query("SELECT * FROM servers WHERE name IS NULL;").all() as Array< Guild>; if (nulledGuilds.length === 0) return; for (let i = 0; i < nulledGuilds.length; i++) { const guild = nulledGuilds[i]; - console.log(`Removed server: ${guild.name} (${guild.id})`); - removeServer(guild.id); + console.log(`Removed guild: ${guild.name} (${guild.id})`); + removeEntry(Tables.GUILD, guild.id); } } diff --git a/src/utils/osu.ts b/src/utils/osu.ts index ec6bf8e7..b18661a1 100644 --- a/src/utils/osu.ts +++ b/src/utils/osu.ts @@ -1,10 +1,12 @@ /* eslint-disable @stylistic/no-mixed-operators */ -import { bulkInsertData, getMap, insertData } from "./database"; +import { bulkInsertData, getEntry, insertData } from "./database"; import { Mode } from "@type/osu"; +import { Tables } from "@type/database"; import { Beatmap, BeatmapAttributesBuilder, Performance } from "rosu-pp-js"; import { ModsEnum } from "osu-web.js"; import { ChannelType } from "lilybird"; import https from "https"; +import type { Score as ScoreDatabase } from "@type/database"; import type { Message } from "@lilybird/transformers"; import type { Mod } from "@type/mods"; import type { UserScore, UserBestScore, AccessTokenJSON, AuthScope, LeaderboardScore, LeaderboardScoresRaw, PerformanceInfo, Score } from "@type/osu"; @@ -172,7 +174,7 @@ export async function getPerformanceResults({ play, setId, beatmapId, maxCombo, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion else rulesetId = setId!; - mapData ??= getMap(beatmapId)?.data ?? (await downloadBeatmap(beatmapId)).contents; + mapData ??= getEntry(Tables.MAP, beatmapId)?.data ?? (await downloadBeatmap(beatmapId)).contents; if (!mapData) return null; let modsStringArray: Array = []; @@ -289,7 +291,7 @@ export async function downloadBeatmap(id: string | number, timeoutMs = 6000): Pr response.on("end", function () { const data = Buffer.concat(chunks).toString(); // console.log(data); - insertData({ table: "maps", id, data: [ { name: "data", value: data } ] }); + insertData({ table: Tables.MAP, id, data: [ { key: "data", value: data } ] }); resolve({ id, contents: data }); }); }).on("error", reject); @@ -480,13 +482,10 @@ export function saveScoreDatas(scores: Array | Array | function saveScore(play: UserBestScore | UserScore | Score, mode: Mode, mapTemp?: BeatmapWeb): { id: number, - table: string, + table: Tables, data: Array<{ - name: string, - value: number - } | { - name: string, - value: string + key: keyof ScoreDatabase, + value: number | string }> } { let beatmap; @@ -505,70 +504,70 @@ function saveScore(play: UserBestScore | UserScore | Score, mode: Mode, mapTemp? return { id: play.id, - table: "osu_scores", + table: Tables.SCORE, data: [ { - name: "user_id", + key: "user_id", value: play.user_id }, { - name: "map_id", + key: "map_id", value: beatmap.id }, { - name: "gamemode", + key: "gamemode", value: mode }, { - name: "mods", + key: "mods", value: play.mods.join("") }, { - name: "score", + key: "score", value: play.score }, { - name: "accuracy", + key: "accuracy", value: play.accuracy }, { - name: "max_combo", + key: "max_combo", value: play.max_combo }, { - name: "grade", + key: "grade", value: play.rank }, { - name: "count_50", + key: "count_50", value: statistics.count_50 }, { - name: "count_100", + key: "count_100", value: statistics.count_100 }, { - name: "count_300", + key: "count_300", value: statistics.count_300 }, { - name: "count_geki", + key: "count_geki", value: statistics.count_geki ?? 0 }, { - name: "count_katu", + key: "count_katu", value: statistics.count_katu ?? 0 }, { - name: "count_miss", + key: "count_miss", value: statistics.count_miss }, { - name: "map_state", + key: "map_state", value: beatmap.status }, { - name: "ended_at", + key: "ended_at", value: play.created_at } ]