From d7dc9fcd36581af5c0b3e85534c0da8521c19969 Mon Sep 17 00:00:00 2001 From: ToastedToast Date: Sun, 14 Jul 2024 09:36:47 +0800 Subject: [PATCH 1/9] feat(bot): add rankcard --- bot/commands.ts | 118 +++++++++++++++++++++++++++++++++++++----------- bot/types.d.ts | 3 ++ bun.lockb | Bin 97008 -> 144956 bytes package.json | 2 + 4 files changed, 96 insertions(+), 27 deletions(-) create mode 100644 bot/types.d.ts diff --git a/bot/commands.ts b/bot/commands.ts index 01fb3d0..3e60a93 100644 --- a/bot/commands.ts +++ b/bot/commands.ts @@ -1,11 +1,15 @@ // Commands taken from https://github.com/NiaAxern/discord-youtube-subscriber-count/blob/main/src/commands/utilities.ts import client from '.'; -import { ActionRowBuilder, ButtonBuilder, ButtonStyle, type CommandInteraction, ChannelType, type APIApplicationCommandOption } from 'discord.js'; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle, type CommandInteraction, ChannelType, type APIApplicationCommandOption, GuildMember, AttachmentBuilder, ComponentType } from 'discord.js'; import { heapStats } from 'bun:jsc'; import { getGuildLeaderboard, makeGETRequest, getRoles, removeRole, addRole, enableUpdates, disableUpdates, getCooldown, setCooldown, checkIfGuildHasUpdatesEnabled } from './utils/requestAPI'; import convertToLevels from './utils/convertToLevels'; import quickEmbed from './utils/quickEmbed'; +import { Font, RankCardBuilder } from 'canvacord'; +import { getColor } from 'colorthief' + +Font.loadDefault(); interface Command { data: { @@ -129,29 +133,87 @@ const commands: Record = { contexts: [0, 2], }, execute: async (interaction) => { - if (interaction?.guildId) { - const guild = interaction.guild?.id - const user = interaction.user.id - const xp = await makeGETRequest(guild as string, user) - - if (!xp) { - await interaction.reply({ - ephemeral: true, - content: "No XP data available." - }); - return; - } - - const progress = xp.user_progress_next_level; - const progressBar = createProgressBar(progress); + const guild = interaction.guild?.id + const user = interaction.user.id + const xp = await makeGETRequest(guild as string, user) + if (!xp) { await interaction.reply({ - embeds: [ + ephemeral: true, + content: "No XP data available." + }); + return; + } + + const card = new RankCardBuilder() + .setDisplayName((interaction.member as GuildMember).displayName) + .setAvatar(interaction.user.displayAvatarURL()) // user avatar + .setCurrentXP(300) // current xp + .setRequiredXP(600) // required xp + .setLevel(2) // user level + .setRank(5) // user rank + .setOverlay(90) // overlay percentage. Overlay is a semi-transparent layer on top of the background + .setBackground("#23272a") + + if (interaction.user.discriminator !== "0") { + card.setUsername("#" + interaction.user.discriminator) + } else { + card.setUsername("@" + interaction.user.username) + } + + const color = await getColor(interaction.user.displayAvatarURL({ extension: "png" })); + card.setStyles({ + progressbar: { + thumb: { + style: { + backgroundColor: `rgb(${color[0]}, ${color[1]}, ${color[2]})` + } + } + } + }) + + const image = await card.build({ + format: "png" + }); + const attachment = new AttachmentBuilder(image, { name: `${user}.png` }); + + const msg = await interaction.reply({ + files: [attachment], + components: [ + new ActionRowBuilder().setComponents( + new ButtonBuilder() + .setCustomId("text-mode") + .setLabel("Use text mode") + .setStyle(ButtonStyle.Secondary) + ) + ], + fetchReply: true + }); + + const collector = msg.createMessageComponentCollector({ + componentType: ComponentType.Button, + time: 60 * 1000 + }); + + collector.on("collect", async (i) => { + if (i.user.id !== user) + return i.reply({ + content: "You're not the one who initialized this message! Try running /xp on your own.", + ephemeral: true + }); + + if (i.customId !== "text-mode") return; + + const progress = xp.progress_next_level; + const progressBar = createProgressBar(progress); + + await i.update({ + embeds: [ quickEmbed( { color: 'Blurple', title: 'XP', - description: `<@${user}> you have ${xp.xp.toLocaleString("en-US")} XP! (Level ${convertToLevels(xp.xp).toLocaleString("en-US")})`, + description: `<@${user}> you have ${xp.xp} XP! (Level ${convertToLevels(xp.xp)})`, }, interaction ).addFields([ @@ -162,18 +224,20 @@ const commands: Record = { }, { name: 'XP Required', - value: `${xp.user_xp_needed_next_level.toLocaleString("en-US")} XP`, + value: `${xp.xp_needed_next_level} XP`, inline: true, }, ]), ], - }); - - function createProgressBar(progress: number): string { - const filled = Math.floor(progress / 10); - const empty = 10 - filled; - return '▰'.repeat(filled) + '▱'.repeat(empty); - } + files: [], + components: [] + }) + }) + + function createProgressBar(progress: number): string { + const filled = Math.floor(progress / 10); + const empty = 10 - filled; + return '▰'.repeat(filled) + '▱'.repeat(empty); } } }, @@ -487,7 +551,7 @@ const commands: Record = { const action = interaction.options.get('action')?.value; const cooldown = interaction.options.get('cooldown')?.value; - + let cooldownData; let apiSuccess; diff --git a/bot/types.d.ts b/bot/types.d.ts new file mode 100644 index 0000000..172a3b6 --- /dev/null +++ b/bot/types.d.ts @@ -0,0 +1,3 @@ +declare module "colorthief" { + function getColor(url: string): Promise<[number, number, number]>; +} diff --git a/bun.lockb b/bun.lockb index 3b811406317c72ac757d72505b0476a2eccb085a..0fce008178e990e9d3c82b4bfaf719ae4efc29e2 100755 GIT binary patch delta 44255 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 Y?QeH1;F9Lqw$;WXBc8pn&FCEU|K-ZKx&QzG delta 15263 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 diff --git a/package.json b/package.json index 396b6c2..c9f2d8d 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,8 @@ "typescript": "^5.0.0" }, "dependencies": { + "canvacord": "^6.0.2", + "colorthief": "^2.4.0", "cors": "^2.8.5", "discord.js": "^14.15.3", "ejs": "^3.1.10", From 79cd22fbffd879c36c686011c1f73ad37edfb5f4 Mon Sep 17 00:00:00 2001 From: ToastedToast Date: Sun, 14 Jul 2024 15:36:59 +0800 Subject: [PATCH 2/9] fix(commands): readd `.toLocaleString()` for text mode in `/xp` --- bot/commands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/commands.ts b/bot/commands.ts index 3e60a93..539fd84 100644 --- a/bot/commands.ts +++ b/bot/commands.ts @@ -213,7 +213,7 @@ const commands: Record = { { color: 'Blurple', title: 'XP', - description: `<@${user}> you have ${xp.xp} XP! (Level ${convertToLevels(xp.xp)})`, + description: `<@${user}> you have ${xp.xp.toLocaleString()} XP! (Level ${convertToLevels(xp.xp)})`, }, interaction ).addFields([ From bf4e796fd36064402a30ebb50c08e5d97ef1de5d Mon Sep 17 00:00:00 2001 From: ToastedToast Date: Sun, 14 Jul 2024 15:40:45 +0800 Subject: [PATCH 3/9] fix(xp): use highest role color for progress bar --- bot/commands.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bot/commands.ts b/bot/commands.ts index 539fd84..9dc63c5 100644 --- a/bot/commands.ts +++ b/bot/commands.ts @@ -161,13 +161,12 @@ const commands: Record = { card.setUsername("@" + interaction.user.username) } - const color = await getColor(interaction.user.displayAvatarURL({ extension: "png" })); card.setStyles({ progressbar: { thumb: { style: { - backgroundColor: `rgb(${color[0]}, ${color[1]}, ${color[2]})` - } + backgroundColor: (interaction.member as GuildMember).roles.highest.hexColor ?? "#ffffff" + } } } }) From 41124d7be497a4166aafbb40a5c45d2dae229fc5 Mon Sep 17 00:00:00 2001 From: ToastedToast Date: Sun, 14 Jul 2024 15:42:53 +0800 Subject: [PATCH 4/9] chore: remove unused imports --- bot/commands.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/bot/commands.ts b/bot/commands.ts index 9dc63c5..15d43f5 100644 --- a/bot/commands.ts +++ b/bot/commands.ts @@ -7,7 +7,6 @@ import { getGuildLeaderboard, makeGETRequest, getRoles, removeRole, addRole, ena import convertToLevels from './utils/convertToLevels'; import quickEmbed from './utils/quickEmbed'; import { Font, RankCardBuilder } from 'canvacord'; -import { getColor } from 'colorthief' Font.loadDefault(); From c059fdd7efff2044abdf6a4727ace16909e999f8 Mon Sep 17 00:00:00 2001 From: ToastedToast Date: Sun, 14 Jul 2024 17:20:12 +0800 Subject: [PATCH 5/9] fix(commands): readd `.toLocaleString()` for progress in /xp --- bot/commands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/commands.ts b/bot/commands.ts index 15d43f5..10fedd9 100644 --- a/bot/commands.ts +++ b/bot/commands.ts @@ -222,7 +222,7 @@ const commands: Record = { }, { name: 'XP Required', - value: `${xp.xp_needed_next_level} XP`, + value: `${xp.xp_needed_next_level.toLocaleString()} XP`, inline: true, }, ]), From ff76fbf367cce35f3b2066ac730075f83fb8bec1 Mon Sep 17 00:00:00 2001 From: ToastedToast Date: Sun, 14 Jul 2024 17:30:14 +0800 Subject: [PATCH 6/9] feat(commands): allow specifying a different user for /xp --- bot/commands.ts | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/bot/commands.ts b/bot/commands.ts index 10fedd9..0ff7d97 100644 --- a/bot/commands.ts +++ b/bot/commands.ts @@ -125,15 +125,22 @@ const commands: Record = { }, xp: { data: { - options: [], + options: [{ + name: 'user', + description: 'The user you want to check the XP of.', + type: 6, + required: false, + }], name: 'xp', description: 'Get your XP and Points', integration_types: [0], contexts: [0, 2], }, execute: async (interaction) => { + const optionUser = interaction.options.get('user')?.value as string | null; + const member = (optionUser ? interaction.guild!.members.cache.get(optionUser) : interaction.member) as GuildMember; const guild = interaction.guild?.id - const user = interaction.user.id + const user = member.id; const xp = await makeGETRequest(guild as string, user) if (!xp) { @@ -145,8 +152,8 @@ const commands: Record = { } const card = new RankCardBuilder() - .setDisplayName((interaction.member as GuildMember).displayName) - .setAvatar(interaction.user.displayAvatarURL()) // user avatar + .setDisplayName(member.displayName) + .setAvatar(member.displayAvatarURL()) // user avatar .setCurrentXP(300) // current xp .setRequiredXP(600) // required xp .setLevel(2) // user level @@ -155,16 +162,16 @@ const commands: Record = { .setBackground("#23272a") if (interaction.user.discriminator !== "0") { - card.setUsername("#" + interaction.user.discriminator) + card.setUsername("#" + member.user.discriminator) } else { - card.setUsername("@" + interaction.user.username) + card.setUsername("@" + member.user.username) } card.setStyles({ progressbar: { thumb: { style: { - backgroundColor: (interaction.member as GuildMember).roles.highest.hexColor ?? "#ffffff" + backgroundColor: member.roles.highest.hexColor ?? "#ffffff" } } } From ddb560b322e0709560a6b3fe672231e7e2e09c39 Mon Sep 17 00:00:00 2001 From: ToastedToast Date: Sun, 14 Jul 2024 17:48:55 +0800 Subject: [PATCH 7/9] feat(rankcard): use banner for background if any --- bot/commands.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/bot/commands.ts b/bot/commands.ts index 0ff7d97..0595a88 100644 --- a/bot/commands.ts +++ b/bot/commands.ts @@ -137,8 +137,11 @@ const commands: Record = { contexts: [0, 2], }, execute: async (interaction) => { + await interaction.deferReply() + const optionUser = interaction.options.get('user')?.value as string | null; const member = (optionUser ? interaction.guild!.members.cache.get(optionUser) : interaction.member) as GuildMember; + await interaction.guild!.members.fetch({ user: member.id, force: true }) const guild = interaction.guild?.id const user = member.id; const xp = await makeGETRequest(guild as string, user) @@ -153,13 +156,13 @@ const commands: Record = { const card = new RankCardBuilder() .setDisplayName(member.displayName) - .setAvatar(member.displayAvatarURL()) // user avatar + .setAvatar(member.displayAvatarURL({ forceStatic: true, size: 4096 })) // user avatar .setCurrentXP(300) // current xp .setRequiredXP(600) // required xp .setLevel(2) // user level .setRank(5) // user rank - .setOverlay(90) // overlay percentage. Overlay is a semi-transparent layer on top of the background - .setBackground("#23272a") + .setOverlay(member.user.banner ? 95 : 90) // overlay percentage. Overlay is a semi-transparent layer on top of the background + .setBackground(member.user.bannerURL({ forceStatic: true, size: 4096 }) ?? "#23272a") if (interaction.user.discriminator !== "0") { card.setUsername("#" + member.user.discriminator) @@ -174,7 +177,7 @@ const commands: Record = { backgroundColor: member.roles.highest.hexColor ?? "#ffffff" } } - } + }, }) const image = await card.build({ @@ -182,7 +185,7 @@ const commands: Record = { }); const attachment = new AttachmentBuilder(image, { name: `${user}.png` }); - const msg = await interaction.reply({ + const msg = await interaction.followUp({ files: [attachment], components: [ new ActionRowBuilder().setComponents( From 80af83dc6aa57e927f42137675ca5f2a057ae2dd Mon Sep 17 00:00:00 2001 From: ToastedToast Date: Sun, 14 Jul 2024 17:59:36 +0800 Subject: [PATCH 8/9] fix(rankcard): use actual data --- bot/commands.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/bot/commands.ts b/bot/commands.ts index 0595a88..aff0382 100644 --- a/bot/commands.ts +++ b/bot/commands.ts @@ -144,9 +144,10 @@ const commands: Record = { await interaction.guild!.members.fetch({ user: member.id, force: true }) const guild = interaction.guild?.id const user = member.id; + const leaderboard = await getGuildLeaderboard(guild as string); const xp = await makeGETRequest(guild as string, user) - if (!xp) { + if (!xp || leaderboard.length === 0) { await interaction.reply({ ephemeral: true, content: "No XP data available." @@ -154,13 +155,15 @@ const commands: Record = { return; } + const rank = leaderboard.leaderboard.findIndex((entry: ({ id: string; })) => entry.id === user) + 1; + const card = new RankCardBuilder() .setDisplayName(member.displayName) .setAvatar(member.displayAvatarURL({ forceStatic: true, size: 4096 })) // user avatar - .setCurrentXP(300) // current xp - .setRequiredXP(600) // required xp - .setLevel(2) // user level - .setRank(5) // user rank + .setCurrentXP(xp.xp) // current xp + .setRequiredXP(xp.xp_needed_next_level) // required xp + .setLevel(xp.level) // user level + .setRank(rank) // user rank .setOverlay(member.user.banner ? 95 : 90) // overlay percentage. Overlay is a semi-transparent layer on top of the background .setBackground(member.user.bannerURL({ forceStatic: true, size: 4096 }) ?? "#23272a") @@ -170,11 +173,13 @@ const commands: Record = { card.setUsername("@" + member.user.username) } + const color = member.roles.highest.hexColor ?? "#ffffff" + card.setStyles({ progressbar: { thumb: { style: { - backgroundColor: member.roles.highest.hexColor ?? "#ffffff" + backgroundColor: color } } }, @@ -219,7 +224,7 @@ const commands: Record = { embeds: [ quickEmbed( { - color: 'Blurple', + color, title: 'XP', description: `<@${user}> you have ${xp.xp.toLocaleString()} XP! (Level ${convertToLevels(xp.xp)})`, }, From f028869dfcb1002fdb647e063971d1390410cd11 Mon Sep 17 00:00:00 2001 From: ToastedToast Date: Sun, 14 Jul 2024 18:01:28 +0800 Subject: [PATCH 9/9] feat(commands): add rank to text mode in `/xp` --- bot/commands.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bot/commands.ts b/bot/commands.ts index aff0382..475dc4c 100644 --- a/bot/commands.ts +++ b/bot/commands.ts @@ -230,6 +230,10 @@ const commands: Record = { }, interaction ).addFields([ + { + name: 'Rank', + value: `#${rank.toLocaleString()}`, + }, { name: 'Progress To Next Level', value: `${progressBar} ${progress}%`,