33import client from '.' ;
44import { ActionRowBuilder , ButtonBuilder , ButtonStyle , type CommandInteraction , ChannelType , type APIApplicationCommandOption , GuildMember , AttachmentBuilder , ComponentType } from 'discord.js' ;
55import { heapStats } from 'bun:jsc' ;
6- import { getGuildLeaderboard , makeGETRequest , getRoles , removeRole , addRole , enableUpdates , disableUpdates , getCooldown , setCooldown , getUpdatesChannel , setUpdatesChannel } from './utils/requestAPI' ;
6+ import { getGuildLeaderboard , makeGETRequest , getRoles , removeRole , addRole , enableUpdates , disableUpdates , getCooldown , setCooldown , getUpdatesChannel , setUpdatesChannel , setXP , setLevel } from './utils/requestAPI' ;
77import convertToLevels from './utils/convertToLevels' ;
88import quickEmbed from './utils/quickEmbed' ;
99import { Font , RankCardBuilder } from 'canvacord' ;
@@ -154,74 +154,74 @@ const commands: Record<string, Command> = {
154154 } ) ;
155155 return ;
156156 }
157-
157+
158158 const rank = leaderboard . leaderboard . findIndex ( ( entry : ( { id : string ; } ) ) => entry . id === user ) + 1 ;
159-
159+
160160 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 + 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-
161+ . setDisplayName ( member . displayName )
162+ . setAvatar ( member . displayAvatarURL ( { forceStatic : true , size : 4096 } ) ) // user avatar
163+ . setCurrentXP ( xp . xp ) // current xp
164+ . setRequiredXP ( xp . xp + 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+
170170 if ( interaction . user . discriminator !== "0" ) {
171- card . setUsername ( "#" + member . user . discriminator )
171+ card . setUsername ( "#" + member . user . discriminator )
172172 } else {
173- card . setUsername ( "@" + member . user . username )
173+ card . setUsername ( "@" + member . user . username )
174174 }
175-
175+
176176 const color = member . roles . highest . hexColor ?? "#ffffff"
177177
178- card . setStyles ( {
179- progressbar : {
180- thumb : {
181- style : {
182- backgroundColor : color
178+ card . setStyles ( {
179+ progressbar : {
180+ thumb : {
181+ style : {
182+ backgroundColor : color
183183 }
184- }
185- } ,
186- } )
187-
188- const image = await card . build ( {
189- format : "png"
190- } ) ;
191- const attachment = new AttachmentBuilder ( image , { name : `${ user } .png` } ) ;
184+ }
185+ } ,
186+ } )
187+
188+ const image = await card . build ( {
189+ format : "png"
190+ } ) ;
191+ const attachment = new AttachmentBuilder ( image , { name : `${ user } .png` } ) ;
192192
193193 const msg = await interaction . followUp ( {
194- files : [ attachment ] ,
194+ files : [ attachment ] ,
195195 components : [
196- new ActionRowBuilder < ButtonBuilder > ( ) . setComponents (
196+ new ActionRowBuilder < ButtonBuilder > ( ) . setComponents (
197197 new ButtonBuilder ( )
198- . setCustomId ( "text-mode" )
199- . setLabel ( "Use text mode" )
200- . setStyle ( ButtonStyle . Secondary )
198+ . setCustomId ( "text-mode" )
199+ . setLabel ( "Use text mode" )
200+ . setStyle ( ButtonStyle . Secondary )
201201 )
202202 ] ,
203203 fetchReply : true
204204 } ) ;
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 : [
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 : [
225225 quickEmbed (
226226 {
227227 color,
@@ -248,10 +248,10 @@ const commands: Record<string, Command> = {
248248 ] ,
249249 files : [ ] ,
250250 components : [ ]
251- } )
252- } )
253-
254- function createProgressBar ( progress : number ) : string {
251+ } )
252+ } )
253+
254+ function createProgressBar ( progress : number ) : string {
255255 const filled = Math . floor ( progress / 10 ) ;
256256 const empty = 10 - filled ;
257257 return '▰' . repeat ( filled ) + '▱' . repeat ( empty ) ;
@@ -286,11 +286,11 @@ const commands: Record<string, Command> = {
286286 } , interaction ) ;
287287
288288 // Add a field for each user with a mention
289- leaderboard . leaderboard . forEach ( ( entry : { user_id : string ; xp : number ; } , index : number ) => {
289+ leaderboard . leaderboard . forEach ( ( entry : { id : string ; xp : number ; } , index : number ) => {
290290 leaderboardEmbed . addFields ( [
291291 {
292292 name : `${ index + 1 } .` ,
293- value : `<@${ entry . user_id } >: ${ entry . xp . toLocaleString ( "en-US" ) } XP` ,
293+ value : `<@${ entry . id } >: ${ entry . xp . toLocaleString ( "en-US" ) } XP` ,
294294 inline : false
295295 }
296296 ] ) ;
@@ -475,7 +475,7 @@ const commands: Record<string, Command> = {
475475 value : 'reset' ,
476476 } ,
477477 ]
478- } , {
478+ } , {
479479 name : 'channel' ,
480480 description : 'Enter the channel ID. Required for set action.' ,
481481 type : 7 ,
@@ -523,8 +523,8 @@ const commands: Record<string, Command> = {
523523 }
524524 await interaction . reply ( { ephemeral : true , content : 'Updates are now disabled for this server' } ) . catch ( console . error ) ;
525525 return ;
526- case 'set' :
527- if ( ! channelId ) {
526+ case 'set' :
527+ if ( ! channelId ) {
528528 await interaction . reply ( { ephemeral : true , content : 'ERROR: Channel was not specified!' } ) ;
529529 return ;
530530 }
@@ -592,7 +592,7 @@ const commands: Record<string, Command> = {
592592 value : 'set' ,
593593 }
594594 ]
595- } , {
595+ } , {
596596 name : 'cooldown' ,
597597 description : 'Enter the cooldown in seconds. Required for set action.' ,
598598 type : 4 ,
@@ -648,6 +648,79 @@ const commands: Record<string, Command> = {
648648 return ;
649649 }
650650 }
651+ } ,
652+ set : {
653+ data : {
654+ options : [ {
655+ name : 'user' ,
656+ description : 'The user you want to update the XP or level of.' ,
657+ type : 6 ,
658+ required : true ,
659+ } , {
660+ name : 'type' ,
661+ description : 'Select the data type to set' ,
662+ type : 3 ,
663+ required : true ,
664+ choices : [
665+ {
666+ name : 'XP' ,
667+ value : 'xp' ,
668+ } ,
669+ {
670+ name : 'Level' ,
671+ value : 'level' ,
672+ }
673+ ]
674+ } , {
675+ name : 'value' ,
676+ description : 'The new value to set' ,
677+ type : 3 ,
678+ required : true ,
679+ } ] ,
680+ name : 'set' ,
681+ description : 'Set the XP or level of a user!' ,
682+ integration_types : [ 0 ] ,
683+ contexts : [ 0 , 2 ] ,
684+ } ,
685+ execute : async ( interaction ) => {
686+ if ( ! interaction . memberPermissions ?. has ( 'ManageGuild' ) ) {
687+ const errorEmbed = quickEmbed ( {
688+ color : 'Red' ,
689+ title : 'Error!' ,
690+ description : 'Missing permissions: `Manage Server`'
691+ } , interaction ) ;
692+ await interaction . reply ( {
693+ ephemeral : true ,
694+ embeds : [ errorEmbed ]
695+ } )
696+ . catch ( console . error ) ;
697+ return ;
698+ }
699+
700+ const user = interaction . options . get ( 'user' ) ?. value as string ;
701+ const type = interaction . options . get ( 'type' ) ?. value ;
702+ const value = interaction . options . get ( 'value' ) ?. value ;
703+
704+ let apiSuccess ;
705+ switch ( type ) {
706+ case 'xp' :
707+ apiSuccess = await setXP ( interaction . guildId as string , user , parseInt ( value as string ) ) ;
708+ if ( ! apiSuccess ) {
709+ await interaction . reply ( { ephemeral : true , content : 'Error setting XP!' } ) ;
710+ return ;
711+ }
712+ await interaction . reply ( { ephemeral : true , content : `XP set to ${ value } for <@${ user } >` } ) ;
713+ return ;
714+ case 'level' :
715+ apiSuccess = await setLevel ( interaction . guildId as string , user , parseInt ( value as string ) ) ;
716+ if ( ! apiSuccess ) {
717+ await interaction . reply ( { ephemeral : true , content : 'Error setting level!' } ) ;
718+ return ;
719+ }
720+ await interaction . reply ( { ephemeral : true , content : `Level set to ${ value } for <@${ user } >` } ) ;
721+ return ;
722+ }
723+ }
651724 }
652725} ;
653726
0 commit comments