|
1 | 1 | // Commands taken from https://github.com/NiaAxern/discord-youtube-subscriber-count/blob/main/src/commands/utilities.ts |
2 | 2 |
|
3 | 3 | import client from '.'; |
4 | | -import { ActionRowBuilder, ButtonBuilder, ButtonStyle, type CommandInteraction, ChannelType, type APIApplicationCommandOption } from 'discord.js'; |
| 4 | +import { ActionRowBuilder, ButtonBuilder, ButtonStyle, type CommandInteraction, ChannelType, type APIApplicationCommandOption, GuildMember, AttachmentBuilder, ComponentType } from 'discord.js'; |
5 | 5 | import { heapStats } from 'bun:jsc'; |
6 | 6 | import { getGuildLeaderboard, makeGETRequest, getRoles, removeRole, addRole, enableUpdates, disableUpdates, getCooldown, setCooldown, checkIfGuildHasUpdatesEnabled } from './utils/requestAPI'; |
7 | 7 | import convertToLevels from './utils/convertToLevels'; |
8 | 8 | import quickEmbed from './utils/quickEmbed'; |
| 9 | +import { Font, RankCardBuilder } from 'canvacord'; |
| 10 | + |
| 11 | +Font.loadDefault(); |
9 | 12 |
|
10 | 13 | interface Command { |
11 | 14 | data: { |
@@ -122,58 +125,136 @@ const commands: Record<string, Command> = { |
122 | 125 | }, |
123 | 126 | xp: { |
124 | 127 | data: { |
125 | | - options: [], |
| 128 | + options: [{ |
| 129 | + name: 'user', |
| 130 | + description: 'The user you want to check the XP of.', |
| 131 | + type: 6, |
| 132 | + required: false, |
| 133 | + }], |
126 | 134 | name: 'xp', |
127 | 135 | description: 'Get your XP and Points', |
128 | 136 | integration_types: [0], |
129 | 137 | contexts: [0, 2], |
130 | 138 | }, |
131 | 139 | execute: async (interaction) => { |
132 | | - if (interaction?.guildId) { |
133 | | - const guild = interaction.guild?.id |
134 | | - const user = interaction.user.id |
135 | | - const xp = await makeGETRequest(guild as string, user) |
| 140 | + await interaction.deferReply() |
136 | 141 |
|
137 | | - if (!xp) { |
138 | | - await interaction.reply({ |
139 | | - ephemeral: true, |
140 | | - content: "No XP data available." |
141 | | - }); |
142 | | - return; |
143 | | - } |
144 | | - |
145 | | - const progress = xp.user_progress_next_level; |
146 | | - const progressBar = createProgressBar(progress); |
| 142 | + const optionUser = interaction.options.get('user')?.value as string | null; |
| 143 | + const member = (optionUser ? interaction.guild!.members.cache.get(optionUser) : interaction.member) as GuildMember; |
| 144 | + await interaction.guild!.members.fetch({ user: member.id, force: true }) |
| 145 | + const guild = interaction.guild?.id |
| 146 | + const user = member.id; |
| 147 | + const leaderboard = await getGuildLeaderboard(guild as string); |
| 148 | + const xp = await makeGETRequest(guild as string, user) |
147 | 149 |
|
| 150 | + if (!xp || leaderboard.length === 0) { |
148 | 151 | await interaction.reply({ |
149 | | - embeds: [ |
| 152 | + ephemeral: true, |
| 153 | + content: "No XP data available." |
| 154 | + }); |
| 155 | + return; |
| 156 | + } |
| 157 | + |
| 158 | + const rank = leaderboard.leaderboard.findIndex((entry: ({ id: string; })) => entry.id === user) + 1; |
| 159 | + |
| 160 | + const card = new RankCardBuilder() |
| 161 | + .setDisplayName(member.displayName) |
| 162 | + .setAvatar(member.displayAvatarURL({ forceStatic: true, size: 4096 })) // user avatar |
| 163 | + .setCurrentXP(xp.xp) // current xp |
| 164 | + .setRequiredXP(xp.xp_needed_next_level) // required xp |
| 165 | + .setLevel(xp.level) // user level |
| 166 | + .setRank(rank) // user rank |
| 167 | + .setOverlay(member.user.banner ? 95 : 90) // overlay percentage. Overlay is a semi-transparent layer on top of the background |
| 168 | + .setBackground(member.user.bannerURL({ forceStatic: true, size: 4096 }) ?? "#23272a") |
| 169 | + |
| 170 | + if (interaction.user.discriminator !== "0") { |
| 171 | + card.setUsername("#" + member.user.discriminator) |
| 172 | + } else { |
| 173 | + card.setUsername("@" + member.user.username) |
| 174 | + } |
| 175 | + |
| 176 | + const color = member.roles.highest.hexColor ?? "#ffffff" |
| 177 | + |
| 178 | + card.setStyles({ |
| 179 | + progressbar: { |
| 180 | + thumb: { |
| 181 | + style: { |
| 182 | + backgroundColor: color |
| 183 | + } |
| 184 | + } |
| 185 | + }, |
| 186 | + }) |
| 187 | + |
| 188 | + const image = await card.build({ |
| 189 | + format: "png" |
| 190 | + }); |
| 191 | + const attachment = new AttachmentBuilder(image, { name: `${user}.png` }); |
| 192 | + |
| 193 | + const msg = await interaction.followUp({ |
| 194 | + files: [attachment], |
| 195 | + components: [ |
| 196 | + new ActionRowBuilder<ButtonBuilder>().setComponents( |
| 197 | + new ButtonBuilder() |
| 198 | + .setCustomId("text-mode") |
| 199 | + .setLabel("Use text mode") |
| 200 | + .setStyle(ButtonStyle.Secondary) |
| 201 | + ) |
| 202 | + ], |
| 203 | + fetchReply: true |
| 204 | + }); |
| 205 | + |
| 206 | + const collector = msg.createMessageComponentCollector({ |
| 207 | + componentType: ComponentType.Button, |
| 208 | + time: 60 * 1000 |
| 209 | + }); |
| 210 | + |
| 211 | + collector.on("collect", async (i) => { |
| 212 | + if (i.user.id !== user) |
| 213 | + return i.reply({ |
| 214 | + content: "You're not the one who initialized this message! Try running /xp on your own.", |
| 215 | + ephemeral: true |
| 216 | + }); |
| 217 | + |
| 218 | + if (i.customId !== "text-mode") return; |
| 219 | + |
| 220 | + const progress = xp.progress_next_level; |
| 221 | + const progressBar = createProgressBar(progress); |
| 222 | + |
| 223 | + await i.update({ |
| 224 | + embeds: [ |
150 | 225 | quickEmbed( |
151 | 226 | { |
152 | | - color: 'Blurple', |
| 227 | + color, |
153 | 228 | title: 'XP', |
154 | | - description: `<@${user}> you have ${xp.xp.toLocaleString("en-US")} XP! (Level ${convertToLevels(xp.xp).toLocaleString("en-US")})`, |
| 229 | + description: `<@${user}> you have ${xp.xp.toLocaleString()} XP! (Level ${convertToLevels(xp.xp)})`, |
155 | 230 | }, |
156 | 231 | interaction |
157 | 232 | ).addFields([ |
| 233 | + { |
| 234 | + name: 'Rank', |
| 235 | + value: `#${rank.toLocaleString()}`, |
| 236 | + }, |
158 | 237 | { |
159 | 238 | name: 'Progress To Next Level', |
160 | 239 | value: `${progressBar} ${progress}%`, |
161 | 240 | inline: true, |
162 | 241 | }, |
163 | 242 | { |
164 | 243 | name: 'XP Required', |
165 | | - value: `${xp.user_xp_needed_next_level.toLocaleString("en-US")} XP`, |
| 244 | + value: `${xp.xp_needed_next_level.toLocaleString()} XP`, |
166 | 245 | inline: true, |
167 | 246 | }, |
168 | 247 | ]), |
169 | 248 | ], |
170 | | - }); |
171 | | - |
172 | | - function createProgressBar(progress: number): string { |
173 | | - const filled = Math.floor(progress / 10); |
174 | | - const empty = 10 - filled; |
175 | | - return '▰'.repeat(filled) + '▱'.repeat(empty); |
176 | | - } |
| 249 | + files: [], |
| 250 | + components: [] |
| 251 | + }) |
| 252 | + }) |
| 253 | + |
| 254 | + function createProgressBar(progress: number): string { |
| 255 | + const filled = Math.floor(progress / 10); |
| 256 | + const empty = 10 - filled; |
| 257 | + return '▰'.repeat(filled) + '▱'.repeat(empty); |
177 | 258 | } |
178 | 259 | } |
179 | 260 | }, |
@@ -487,7 +568,7 @@ const commands: Record<string, Command> = { |
487 | 568 |
|
488 | 569 | const action = interaction.options.get('action')?.value; |
489 | 570 | const cooldown = interaction.options.get('cooldown')?.value; |
490 | | - |
| 571 | + |
491 | 572 | let cooldownData; |
492 | 573 | let apiSuccess; |
493 | 574 |
|
|
0 commit comments