1+ const { SlashCommandBuilder, ActionRowBuilder, EmbedBuilder, ButtonStyle, ButtonBuilder, ModalBuilder, TextInputBuilder, TextInputStyle } = require ( 'discord.js' ) ;
2+ const ValoAPI = require ( '../../api/valorantApi' ) ;
3+ const ValorantUser = require ( '../../schemas/valorantUserSystem' ) ;
4+ const images = require ( '../../images' ) ;
5+
6+ module . exports = {
7+ data : new SlashCommandBuilder ( )
8+ . setName ( 'valorant' )
9+ . setDescription ( 'valorant commands' )
10+ . addSubcommand ( command => command . setName ( 'login' ) . setDescription ( 'Connect your Valorant account' ) )
11+ . addSubcommand ( command => command . setName ( 'search-skin' ) . setDescription ( 'search a skin by its name' ) . addStringOption ( option => option . setName ( 'name' ) . setDescription ( 'Name of the skin to search!' ) . setRequired ( true ) . setAutocomplete ( true ) ) )
12+ . addSubcommand ( command => command . setName ( 'store' ) . setDescription ( 'View your Valorant store' ) ) ,
13+
14+ async autocomplete ( interaction , client ) {
15+ const focused = interaction . options . getFocused ( true ) ;
16+
17+ if ( focused . name === 'name' ) {
18+ let skins = [ ] ;
19+ if ( focused . value ) {
20+ skins = client . skins . filter ( s =>
21+ s [ "displayName" ] [ "de-DE" ] . toLowerCase ( ) . includes ( focused . value . toLowerCase ( ) ) ||
22+ s [ "displayName" ] [ "en-US" ] . toLowerCase ( ) . includes ( focused . value . toLowerCase ( ) )
23+ ) ;
24+ }
25+ await interaction . respond (
26+ skins . slice ( 0 , 25 ) . map ( d => ( {
27+ name : d [ "displayName" ] [ "en-US" ] ,
28+ value : d [ "uuid" ]
29+ } ) )
30+ ) ;
31+ }
32+ } ,
33+ async execute ( interaction , client ) {
34+
35+ const sub = interaction . options . getSubcommand ( ) ;
36+
37+ switch ( sub ) {
38+
39+ case 'login' :
40+
41+ const Embed = new EmbedBuilder ( )
42+ . setAuthor ( { name : `Valorant Login | Developed by arnsfh` , iconURL : "https://i.postimg.cc/RVzrNstM/arnsfh.webp" } )
43+ . setFooter ( { text : `Valorant Login` , iconURL : interaction . user . displayAvatarURL ( ) } )
44+ . setTimestamp ( )
45+ . setTitle ( `${ client . user . username } Valorant Login ${ client . config . arrowEmoji } ` )
46+ . setDescription ( '**__Login to valorant account__** \n\nLogin your Riot Games account for **1** hour!\n`1.` Click on "Get URL"\n`2.` On the 404 Page copy the **full** URL\n`3.` Click the "Login button" and paste the copied URL' )
47+ . setColor ( client . config . embedColor ) ;
48+
49+ const Buttons = new ActionRowBuilder ( )
50+ . addComponents (
51+ new ButtonBuilder ( )
52+ . setCustomId ( 'login-button' )
53+ . setLabel ( 'Login' )
54+ . setStyle ( ButtonStyle . Primary ) ,
55+
56+ new ButtonBuilder ( )
57+ . setLabel ( 'Get URL' )
58+ . setURL ( 'https://auth.riotgames.com/authorize?redirect_uri=https%3A%2F%2Fplayvalorant.com%2Fopt_in&client_id=play-valorant-web-prod&response_type=token%20id_token&nonce=1&scope=account%20openid' )
59+ . setStyle ( ButtonStyle . Link )
60+ ) ;
61+
62+ const response = await interaction . reply ( { embeds : [ Embed ] ,
63+ components : [ Buttons ] ,
64+ ephemeral : true
65+ } ) ;
66+
67+ const collector = response . createMessageComponentCollector ( {
68+ filter : i => i . customId === 'login-button' && i . user . id === interaction . user . id ,
69+ time : 60000
70+ } ) ;
71+
72+ collector . on ( 'collect' , async i => {
73+ const Modal = new ModalBuilder ( )
74+ . setCustomId ( 'riot-login' )
75+ . setTitle ( 'Riot Login' )
76+ . addComponents (
77+ new ActionRowBuilder ( )
78+ . addComponents (
79+ new TextInputBuilder ( )
80+ . setCustomId ( 'accessTokenURL' )
81+ . setLabel ( 'URL' )
82+ . setStyle ( TextInputStyle . Short )
83+ . setPlaceholder ( 'Enter url' )
84+ )
85+ ) ;
86+
87+ await i . showModal ( Modal ) ;
88+
89+ const modalSubmit = await i . awaitModalSubmit ( {
90+ filter : i => i . customId === 'riot-login' ,
91+ time : 60000
92+ } ) . catch ( ( ) => null ) ;
93+
94+ if ( modalSubmit ) {
95+ const aTURL = modalSubmit . fields . getTextInputValue ( 'accessTokenURL' ) ;
96+
97+ const valApi = new ValoAPI ( {
98+ accessTokenURL : aTURL ,
99+ SkinsData : client . skins ,
100+ SkinsTier : client . skinsTier
101+ } ) ;
102+
103+ await valApi . initialize ( ) ;
104+
105+ const { access_token, entitlement_token, user_uuid } = valApi . getTokens ( ) ;
106+ const ExpireDate = Math . floor ( Date . now ( ) + 59 * 60 * 1000 ) ;
107+
108+ await ValorantUser . findOneAndUpdate (
109+ { userId : modalSubmit . user . id } ,
110+ {
111+ userId : modalSubmit . user . id ,
112+ accessToken : access_token ,
113+ entitlementToken : entitlement_token ,
114+ userUUID : user_uuid ,
115+ expires : new Date ( ExpireDate )
116+ } ,
117+ { upsert : true }
118+ ) ;
119+
120+ await modalSubmit . reply ( { content : `Successfully logged in! Expires (<t:${ Math . floor ( ExpireDate / 1000 ) } :R>)` , ephemeral : true } ) ;
121+ }
122+ } ) ;
123+
124+ break ;
125+ case 'search-skin' :
126+
127+ const skinUUID = interaction . options . getString ( 'name' ) ;
128+ const foundSkin = client . skins . find ( s => s [ "uuid" ] === skinUUID ) ;
129+
130+ if ( ! foundSkin ) {
131+ return interaction . reply ( {
132+ content : 'Skin not found!' ,
133+ ephemeral : true
134+ } ) ;
135+ }
136+
137+ const skinTier = client . skinsTier . find ( s => s [ "uuid" ] === foundSkin [ "contentTierUuid" ] ) || {
138+ highlightColor : "808080FF" ,
139+ displayIcon : null ,
140+ price : 0
141+ } ;
142+
143+ const skinEmbed = new EmbedBuilder ( )
144+ . setAuthor ( { name : `Valorant Skin Search | Developed by arnsfh` , iconURL : "https://i.postimg.cc/RVzrNstM/arnsfh.webp" } )
145+ . setTitle ( `${ client . user . username } Valorant Skin Search ${ client . config . arrowEmoji } ` )
146+ . setColor ( `#${ skinTier [ "highlightColor" ] . slice ( 0 , - 2 ) } ` || "DarkerGrey" )
147+ . setThumbnail ( skinTier [ "displayIcon" ] )
148+ . setImage ( foundSkin [ "displayIcon" ] || foundSkin [ "levels" ] [ 0 ] [ "displayIcon" ] )
149+ . setFooter ( { text : `Valorant Skin Search` , iconURL : interaction . user . displayAvatarURL ( ) } )
150+ . setTimestamp ( )
151+ . setDescription ( `> **${ foundSkin [ "displayName" ] [ "en-US" ] } ** \n\nPrice: **${ ! foundSkin [ "displayName" ] [ "en-US" ] . includes ( 'Knife' ) ? skinTier [ "price" ] || 0 : "1750 - 5950" } ** ${ client . config . valoPoints } ` ) ;
152+
153+ await interaction . reply ( { embeds : [ skinEmbed ] } ) ;
154+
155+ break ;
156+ case 'store' :
157+
158+ const userAccount = await ValorantUser . findOne ( { userId : interaction . user . id } ) ;
159+
160+ if ( ! userAccount ) return interaction . reply ( { content : 'No account found! use /login' , ephemeral : true } ) ;
161+
162+ if ( Date . now ( ) > userAccount . expires . getTime ( ) ) return interaction . reply ( { content : 'Account access token expired! use /login' , ephemeral : true } ) ;
163+
164+ const valApi = new ValoAPI ( {
165+ SkinsData : client . skins ,
166+ SkinsTier : client . skinsTier ,
167+ accessToken : userAccount . accessToken ,
168+ entitlementToken : userAccount . entitlementToken ,
169+ userUUID : userAccount . userUUID
170+ } ) ;
171+
172+ await valApi . initialize ( ) ;
173+
174+ const wallet = await valApi . getWallet ( ) ;
175+ const { StoreSkins, NewStore } = await valApi . getStore ( ) ;
176+
177+ let Embeds = [
178+ new EmbedBuilder ( )
179+ . setAuthor ( { name : 'Valorant Store | Developed by arnsfh' , iconURL : "https://i.postimg.cc/RVzrNstM/arnsfh.webp" } )
180+ . setTitle ( `${ client . user . username } Valorant Store ${ client . config . arrowEmoji } ` )
181+ . setColor ( 'LightGrey' )
182+ . setDescription ( `> **__${ interaction . user . username } 's Store__** \n\n**${ wallet } **\n\nNext Store in <t:${ Math . floor ( NewStore ) } :R>` )
183+ . setFooter ( { text : `Valorant Store` , iconURL : interaction . user . displayAvatarURL ( ) } )
184+ . setTimestamp ( )
185+ ] ;
186+
187+ const usedEditions = new Set ( ) ;
188+
189+ for ( const Skin of StoreSkins ) {
190+ const tierName = Skin . tier . emoji . match ( / : ( .* ?) : / ) [ 1 ] ;
191+ const embed = new EmbedBuilder ( )
192+ . setColor ( Skin . tier . color )
193+ . setTitle ( `${ Skin . tier . emoji } - ${ Skin . name } ` )
194+ . setDescription ( `Price: **${ Skin . price } **` )
195+ . setImage ( Skin . icon ) ;
196+
197+ if ( tierName ) {
198+ usedEditions . add ( tierName ) ;
199+ embed . setThumbnail ( images . getEditionURL ( tierName ) ) ;
200+ }
201+
202+ Embeds . push ( embed ) ;
203+ }
204+
205+ await interaction . reply ( {
206+ embeds : Embeds ,
207+ files : Array . from ( usedEditions ) . map ( edition =>
208+ images . getAttachment ( edition . toLowerCase ( ) . replace ( '_edition' , '' ) )
209+ )
210+ } ) ;
211+
212+ break ;
213+ }
214+ }
215+ }
0 commit comments