From 57d6263b7dbf8f423dc26f07a9914f9e0b52dbe7 Mon Sep 17 00:00:00 2001 From: Yoru Date: Wed, 24 Apr 2024 22:32:40 +0300 Subject: [PATCH] add comomand --- HanamiCard | 1 + package.json | 4 +- src/commands-message/osu/card.ts | 49 +++++++++++++++++++++++++ src/commands/osu/card.ts | 63 ++++++++++++++++++++++++++++++++ src/embed-builders/card.ts | 24 ++++++++++++ src/embed-builders/index.ts | 1 + src/types/embedBuilders.ts | 9 ++++- 7 files changed, 149 insertions(+), 2 deletions(-) create mode 160000 HanamiCard create mode 100644 src/commands-message/osu/card.ts create mode 100644 src/commands/osu/card.ts create mode 100644 src/embed-builders/card.ts diff --git a/HanamiCard b/HanamiCard new file mode 160000 index 00000000..5fd188f5 --- /dev/null +++ b/HanamiCard @@ -0,0 +1 @@ +Subproject commit 5fd188f5e0bf9f34d8f538e4f4c49c31a6dabc29 diff --git a/package.json b/package.json index 055ba9b4..745799c9 100644 --- a/package.json +++ b/package.json @@ -3,15 +3,17 @@ "private": true, "name": "hanamibot", "type": "module", + "main": "src/index.ts", "scripts": { "check": "tsc && eslint", - "start": "bun src/index.ts", + "start": "concurrently 'cd HanamiCard && git pull && go run main.go' 'bun .'", "build": "bun scripts/build.ts", "prod": "bun run build && bun run start" }, "dependencies": { "@lilybird/handlers": "0.4.0-beta.2", "@lilybird/transformers": "0.2.0-alpha.1", + "concurrently": "^8.2.2", "lilybird": "0.6.0-beta.3", "osu-web.js": "^2.4.0", "rosu-pp-js": "^1.0.2", diff --git a/src/commands-message/osu/card.ts b/src/commands-message/osu/card.ts new file mode 100644 index 00000000..2567001e --- /dev/null +++ b/src/commands-message/osu/card.ts @@ -0,0 +1,49 @@ +import { parseOsuArguments } from "@utils/args"; +import { client } from "@utils/initalize"; +import { UserType } from "@type/commandArgs"; +import { EmbedBuilderType } from "@type/embedBuilders"; +import { cardBuilder } from "@builders/card"; +import { Mode } from "@type/osu"; +import { EmbedType } from "lilybird"; +import type { GuildTextChannel, Message } from "@lilybird/transformers"; +import type { MessageCommand } from "@type/commands"; + +export default { + name: "card", + aliases: ["card", "cards", "stats"], + description: "Display card of a user.", + usage: "/card", + cooldown: 1000, + run +} satisfies MessageCommand; + +async function run({ message, args, channel }: { message: Message, args: Array, commandName: string, channel: GuildTextChannel }): Promise { + const { user } = parseOsuArguments(message, args, Mode.OSU); + if (user.type === UserType.FAIL) { + await channel.send(user.failMessage); + return; + } + + const osuUserRequest = await client.safeParse(client.users.getUser(user.banchoId, { urlParams: { mode: user.mode } })); + if (!osuUserRequest.success) { + await channel.send({ + embeds: [ + { + type: EmbedType.Rich, + title: "Uh oh! :x:", + description: `It seems like the user **\`${user.banchoId}\`** doesn't exist! :(` + } + ] + }); + return; + } + const osuUser = osuUserRequest.data; + + const options = await cardBuilder({ + type: EmbedBuilderType.CARD, + initiatorId: message.author.id, + user: osuUser + }); + await channel.send(options); +} + diff --git a/src/commands/osu/card.ts b/src/commands/osu/card.ts new file mode 100644 index 00000000..37f175ed --- /dev/null +++ b/src/commands/osu/card.ts @@ -0,0 +1,63 @@ +import { getCommandArgs } from "@utils/args"; +import { client } from "@utils/initalize"; +import { UserType } from "@type/commandArgs"; +import { EmbedBuilderType } from "@type/embedBuilders"; +import { cardBuilder } from "@builders/card"; +import { ApplicationCommandOptionType, EmbedType } from "lilybird"; +import type { ApplicationCommandData, GuildInteraction } from "@lilybird/transformers"; +import type { SlashCommand } from "@type/commands"; + +export default { + data: { + name: "card", + description: "Display card of a user.", + options: [ + { + type: ApplicationCommandOptionType.STRING, + name: "username", + description: "Specify an osu! username" + }, + { + type: ApplicationCommandOptionType.USER, + name: "discord", + description: "Specify a linked Discord user" + } + ] + }, + run +} satisfies SlashCommand; + +async function run(interaction: GuildInteraction): Promise { + await interaction.deferReply(); + + const { user } = getCommandArgs(interaction); + + if (user.type === UserType.FAIL) { + await interaction.editReply(user.failMessage); + return; + } + + const osuUserRequest = await client.safeParse(client.users.getUser(user.banchoId, { urlParams: { mode: user.mode } })); + if (!osuUserRequest.success) { + await interaction.editReply({ + embeds: [ + { + type: EmbedType.Rich, + title: "Uh oh! :x:", + description: `It seems like the user **\`${user.banchoId}\`** doesn't exist! :(` + } + ] + }); + return; + } + + const osuUser = osuUserRequest.data; + + const embeds = await cardBuilder({ + type: EmbedBuilderType.CARD, + initiatorId: interaction.member.user.id, + user: osuUser + }); + + await interaction.editReply(embeds); +} diff --git a/src/embed-builders/card.ts b/src/embed-builders/card.ts new file mode 100644 index 00000000..e09c92b9 --- /dev/null +++ b/src/embed-builders/card.ts @@ -0,0 +1,24 @@ +import type { CardBuilderOptions } from "@type/embedBuilders"; +import type { ReplyOptions } from "lilybird"; + +export async function cardBuilder({ user }: CardBuilderOptions): Promise { + const { username, statistics } = user; + const params = `username=${username}&rank=${statistics.global_rank}&accuracy=${statistics + .hit_accuracy}&level=${`${statistics.level.current}.${statistics.level.progress}`}&avatar=${user.avatar_url}`; + + const url = `http://localhost:8080/generateCard?${params}`; + const response = await fetch(url); + + if (!response.ok) + throw new Error("Failed to generate card"); + + const responseData = await response.json() as { image: string }; + + const imageBlob = await fetch(`data:image/png;base64,${responseData.image}`).then(async (res) => res.blob()); + + return { + content: `User card for ${username}`, + // @ts-expect-error TypeScript thinks blob is incorrect type but it is. + files: [ { file: imageBlob, name: `${username}.png` } ] + }; +} diff --git a/src/embed-builders/index.ts b/src/embed-builders/index.ts index 2d11550c..6868a8fb 100644 --- a/src/embed-builders/index.ts +++ b/src/embed-builders/index.ts @@ -4,6 +4,7 @@ export * from "./leaderboard"; export * from "./map"; export * from "./plays"; export * from "./profile"; +export * from "./card"; // Simple functions export * from "./simple/avatar"; diff --git a/src/types/embedBuilders.ts b/src/types/embedBuilders.ts index 2bce57ee..7909cbfc 100644 --- a/src/types/embedBuilders.ts +++ b/src/types/embedBuilders.ts @@ -12,6 +12,7 @@ export const enum EmbedBuilderType { AVATAR = "avatarBuilder", BACKGROUND = "backgroundBuilder", BANNER = "bannerBuilder", + CARD = "cardBuilder", SIMULATE = "simulateBuilder" } @@ -77,6 +78,11 @@ export interface ProfileBuilderOptions extends BuilderOptions { mode: Mode; } +export interface CardBuilderOptions extends BuilderOptions { + type: EmbedBuilderType.CARD; + user: UserExtended; +} + export interface AvatarBuilderOptions extends BuilderOptions { type: EmbedBuilderType.AVATAR; user: UserExtended; @@ -102,4 +108,5 @@ export type EmbedBuilderOptions = | AvatarBuilderOptions | BackgroundBuilderOptions | BannerBuilderOptions - | SimulateBuilderOptions; + | SimulateBuilderOptions + | CardBuilderOptions;