@@ -2,14 +2,18 @@ import {
22 EmbedBuilder ,
33 ChatInputCommandInteraction ,
44 TextChannel ,
5- DMChannel ,
6- NewsChannel ,
75 Colors ,
86 SlashCommandBuilder ,
97 ActionRowBuilder ,
108 ButtonBuilder ,
119 ButtonStyle ,
12- GuildMemberRoleManager
10+ UserSelectMenuBuilder ,
11+ ComponentType ,
12+ MessageFlags ,
13+ ButtonInteraction ,
14+ CollectorFilter ,
15+ Interaction ,
16+ Channel
1317} from 'discord.js' ;
1418import { KOGBot } from '../../index.js' ;
1519
@@ -18,161 +22,233 @@ class LogEventCommand implements SlashCommand {
1822 . setName ( 'logevent' )
1923 . setDescription ( 'Log an event.' ) ;
2024
21- dev = false ; // reverting after kapatz testing g
25+ dev = false ;
26+ mr = true ;
2227 kogBot : KOGBot ;
2328
2429 constructor ( kogBot : KOGBot ) {
2530 this . kogBot = kogBot ;
2631 }
2732
2833 async execute ( interaction : ChatInputCommandInteraction ) : Promise < void > {
29-
30- const db = this . kogBot . database ;
34+ let loggedUsers : string [ ] = [ ] ;
3135
32- const member = interaction . member ;
36+ const userSelect = new UserSelectMenuBuilder ( )
37+ . setCustomId ( 'attendee_list' )
38+ . setPlaceholder ( 'Select event attendees.' )
39+ . setMinValues ( 1 )
40+ . setMaxValues ( 25 ) ;
3341
34- const mrRoleId = this . kogBot . environment . discord . mr_role ;
42+ const finalizeButton = new ButtonBuilder ( )
43+ . setCustomId ( 'finalize_log' )
44+ . setLabel ( 'Finalize' )
45+ . setStyle ( ButtonStyle . Success )
46+ . setEmoji ( '✅' ) ;
3547
36- if ( ! member || ! ( 'roles' in member ) || ! ( member . roles instanceof GuildMemberRoleManager ) || ! member . roles . cache . has ( mrRoleId ) ) {
37- const errorEmbed = new EmbedBuilder ( )
38- . setColor ( Colors . Red )
39- . setTitle ( 'Access Denied' )
40- . setDescription ( 'You do not have the required role to use this command.' )
41- . setTimestamp ( ) ;
48+ const cancelButton = new ButtonBuilder ( )
49+ . setCustomId ( 'cancel_log' )
50+ . setLabel ( 'Cancel' )
51+ . setStyle ( ButtonStyle . Danger )
52+ . setEmoji ( '❌' ) ;
4253
43- await interaction . reply ( {
44- embeds : [ errorEmbed ]
45- } ) ;
46- return ;
47- }
54+ const buttonRow = new ActionRowBuilder < ButtonBuilder > ( )
55+ . addComponents ( finalizeButton , cancelButton ) ;
56+
57+ const selectionRow = new ActionRowBuilder < UserSelectMenuBuilder > ( )
58+ . addComponents ( userSelect ) ;
4859
4960 const initialEmbed = new EmbedBuilder ( )
5061 . setColor ( Colors . Yellow )
51- . setTitle ( 'Logging Event' )
52- . setDescription ( 'Please mention all attendees\n\n**Example:** <@1234567890>, <@0987654321>, <@1234567890>\n\nPlease follow the format above if you can.' )
53- . setTimestamp ( ) ;
54-
55- await interaction . reply ( { embeds : [ initialEmbed ] } ) ;
56-
57- const messageFilter = ( response : { author : { id : string } } ) => response . author . id === interaction . user . id ;
58-
59- if ( interaction . channel instanceof TextChannel || interaction . channel instanceof DMChannel || interaction . channel instanceof NewsChannel ) {
60- const messageCollected = await interaction . channel . awaitMessages ( {
61- filter : messageFilter ,
62- max : 1 ,
63- time : 60_000 ,
64- } ) . catch ( ( ) => null ) ;
65-
66- const response = messageCollected ?. first ( ) ?. content ;
67-
68- if ( ! response ) {
69- const timeoutEmbed = new EmbedBuilder ( )
70- . setColor ( Colors . Red )
71- . setTitle ( 'Timeout' )
72- . setDescription ( 'You took too long to respond. Please try again.' )
73- . setTimestamp ( ) ;
74-
75- await interaction . followUp ( { embeds : [ timeoutEmbed ] } ) ;
62+ . setTitle ( 'Event log' )
63+ . setDescription ( 'Hello! Thank you for starting an event log. Please use the action row below and select all attendees that have attended the event. You can mention multiple users in one go.' )
64+ . setFields (
65+ { name : 'Host' , value : `<@${ interaction . user . id } >` } ,
66+ { name : 'Attendees' , value : 'None.' }
67+ )
68+ . setTimestamp ( )
69+
70+
71+ const res = await interaction . reply ( {
72+ embeds : [ initialEmbed ] ,
73+ components : [ selectionRow , buttonRow ] ,
74+ withResponse : true
75+ } ) ;
76+
77+ const collector = res . resource ?. message ?. createMessageComponentCollector ( {
78+ componentType : ComponentType . UserSelect
79+ } ) ;
80+
81+ collector ?. on ( 'collect' , async ( i ) => {
82+ if ( i . user . id !== interaction . user . id ) {
83+ i . reply ( {
84+ embeds : [
85+ new EmbedBuilder ( )
86+ . setTitle ( 'Unauthorized' )
87+ . setDescription ( 'You are not authorized to interact with this message.\n Only the initial user can interact with this message.' )
88+ . setColor ( Colors . Red )
89+ . setTimestamp ( )
90+ . setFooter ( {
91+ text : 'KOG Bot' ,
92+ iconURL : interaction . guild ?. iconURL ( ) as string
93+ } )
94+ ] ,
95+ flags : MessageFlags . Ephemeral
96+ } )
7697 return ;
7798 }
99+ const selection = i . values ;
100+ loggedUsers = selection ;
101+
102+ await i . update ( {
103+ embeds : [
104+ initialEmbed . setFields (
105+ { name : 'Host' , value : `<@${ interaction . user . id } >` } ,
106+ { name : 'Attendees' , value : loggedUsers . map ( user => `- <@${ user } >` ) . join ( '\n' ) }
107+ )
108+ ]
109+ } ) ;
110+ } ) ;
78111
79- const mentionRegex = / < @ ! ? ( \d + ) > / g;
80- const mentions = Array . from ( response . matchAll ( mentionRegex ) , m => m [ 0 ] ) ;
81-
82- if ( mentions . length === 0 ) {
83- const errorEmbed = new EmbedBuilder ( )
84- . setColor ( Colors . Red )
85- . setTitle ( 'Error: No Mentions Found' )
86- . setDescription ( 'Please ensure that you mention at least one user.' )
87- . setTimestamp ( ) ;
88-
89- await interaction . followUp ( { embeds : [ errorEmbed ] } ) ;
90- return ;
91- }
112+ collector ?. on ( 'end' , async ( collected , reason : string ) => {
113+ if ( reason === 'time' ) {
114+ const disabledRow = new ActionRowBuilder < UserSelectMenuBuilder > ( )
115+ . addComponents ( userSelect . setDisabled ( true ) ) ;
92116
93- const successEmbed = new EmbedBuilder ( )
94- . setColor ( Colors . Green )
95- . setTitle ( 'Mentions Collected' )
96- . setDescription (
97- `The following attendees were collected successfully:\n${ mentions . join ( ', ' ) } \n\nClick **Done** to proceed or **Cancel** to stop.`
98- )
99- . setTimestamp ( ) ;
100-
101- const actionRow = new ActionRowBuilder < ButtonBuilder > ( )
102- . addComponents (
103- new ButtonBuilder ( )
104- . setCustomId ( 'done' )
105- . setLabel ( 'Done' )
106- . setStyle ( ButtonStyle . Success ) ,
107- new ButtonBuilder ( )
108- . setCustomId ( 'cancel' )
109- . setLabel ( 'Cancel' )
110- . setStyle ( ButtonStyle . Danger )
111- ) ;
112-
113- await interaction . followUp ( { embeds : [ successEmbed ] , components : [ actionRow ] } ) ;
114-
115- const buttonFilter = ( i : { user : { id : string } } ) => i . user . id === interaction . user . id ;
116- const buttonCollected = await interaction . channel . awaitMessageComponent ( {
117- filter : buttonFilter ,
118- time : 60_000 ,
119- } ) . catch ( ( ) => null ) ;
120-
121- if ( ! buttonCollected ) {
122- const timeoutEmbed = new EmbedBuilder ( )
123- . setColor ( Colors . Red )
124- . setTitle ( 'Error: Timeout' )
125- . setDescription ( 'You took too long to respond. Please try again.' )
126- . setTimestamp ( ) ;
127-
128- await interaction . followUp ( { embeds : [ timeoutEmbed ] } ) ;
129- return ;
117+ await res . resource ?. message ?. edit ( {
118+ embeds : [ initialEmbed . setColor ( Colors . Red ) ] ,
119+ components : [ disabledRow ]
120+ } ) ;
130121 }
131-
132- if ( buttonCollected . customId === 'cancel' ) {
133- const cancelEmbed = new EmbedBuilder ( )
134- . setColor ( Colors . Red )
135- . setTitle ( 'Event Logging Canceled' )
136- . setDescription ( 'You have canceled the event log.' )
137- . setTimestamp ( ) ;
138-
139- await buttonCollected . reply ( { embeds : [ cancelEmbed ] } ) ;
140- return ;
122+ } ) ;
123+
124+ try {
125+ const collectorFilter = ( i : ButtonInteraction ) => i . user . id === interaction . user . id ;
126+ const confirmation = await res . resource ?. message ?. awaitMessageComponent ( { filter : collectorFilter , time : 3_600_000 , componentType : ComponentType . Button } ) ;
127+
128+ if ( confirmation ?. customId === 'finalize_log' ) {
129+ const logChannel = await this . kogBot . discord_client . channels . cache . get ( this . kogBot . environment . discord . ids . log_channel_id ) as TextChannel ;
130+
131+ if ( ! logChannel || ! logChannel . isTextBased ( ) ) {
132+ await confirmation . update ( {
133+ content : 'The log channel has not been found, please contact the bot developers.' ,
134+ embeds : [ ] ,
135+ components : [ ]
136+ } ) ;
137+ return ;
138+ }
139+
140+ const followUp = await confirmation . reply ( {
141+ embeds : [
142+ new EmbedBuilder ( )
143+ . setColor ( Colors . Yellow )
144+ . setTitle ( 'Logging in process.' )
145+ . setDescription ( 'The event has been submitted and is in the process of being logged. Please wait!' )
146+ . setThumbnail ( 'https://cdn.astrohweston.xyz/u/87efda8e-31df-424d-9881-efb7df7c996b.gif' ) // i know this is hardcoded, try me
147+ . setTimestamp ( )
148+ . setFooter ( {
149+ text : 'KOG Bot' ,
150+ iconURL : interaction . guild ?. iconURL ( ) as string
151+ } )
152+ ] ,
153+ flags : MessageFlags . Ephemeral ,
154+ withResponse : true
155+ } ) ;
156+
157+ await checkExistance ( this . kogBot , loggedUsers ) ;
158+
159+ // Update events_attended for each attendee
160+ for ( const userId of loggedUsers ) {
161+ await this . kogBot . database ( 'users' )
162+ . where ( { discord_id : userId } )
163+ . increment ( 'events_attended' , 1 ) ;
164+ }
165+
166+ await logChannel . send ( {
167+ embeds : [
168+ new EmbedBuilder ( )
169+ . setTitle ( 'Event log' )
170+ . setColor ( Colors . Purple )
171+ . setFields (
172+ { name : 'Host' , value : `<@${ interaction . user . id } >` , inline : true } ,
173+ { name : 'Timestamp' , value : `<t:${ Math . floor ( Date . now ( ) / 1000 ) } :F>` , inline : true } ,
174+ { name : 'Attendees' , value : loggedUsers . map ( user => `- <@${ user } >` ) . join ( ',\n' ) } ,
175+ )
176+ . setTimestamp ( )
177+ . setFooter ( {
178+ text : 'KOG Bot' ,
179+ iconURL : interaction . guild ?. iconURL ( ) as string
180+ } )
181+ ]
182+ } ) ;
183+
184+ await confirmation . message . edit ( {
185+ embeds : [
186+ new EmbedBuilder ( )
187+ . setTitle ( 'Event logged.' )
188+ . setDescription ( 'The event has been successfully logged.' )
189+ . setColor ( Colors . Green )
190+ . setFields (
191+ { name : 'Host' , value : `<@${ interaction . user . id } >` } ,
192+ { name : 'Attendees' , value : loggedUsers . map ( user => `- <@${ user } >` ) . join ( '\n' ) }
193+ )
194+ . setTimestamp ( )
195+ . setFooter ( {
196+ text : 'KOG Bot' ,
197+ iconURL : interaction . guild ?. iconURL ( ) as string
198+ } )
199+ ] ,
200+ components : [ ]
201+ } ) ;
202+
203+ await confirmation . editReply ( {
204+ embeds : [
205+ new EmbedBuilder ( )
206+ . setColor ( Colors . Green )
207+ . setTitle ( 'Event logged.' )
208+ . setDescription ( 'The event has been successfully logged.' )
209+ . setThumbnail ( 'https://cdn.astrohweston.xyz/u/0ab52b9e-646f-47df-8607-f2b68b0b6307.gif' ) // i know this is hardcoded too, try me
210+ . setTimestamp ( )
211+ . setFooter ( {
212+ text : 'KOG Bot' ,
213+ iconURL : interaction . guild ?. iconURL ( ) as string
214+ } )
215+ ]
216+ } ) ;
217+
218+ } else if ( confirmation ?. customId === 'cancel_log' ) {
219+ await confirmation . update ( {
220+ embeds : [
221+ new EmbedBuilder ( )
222+ . setTitle ( 'Event log cancelled.' )
223+ . setDescription ( 'The event log has been cancelled.' )
224+ . setColor ( Colors . Red )
225+ . setTimestamp ( )
226+ . setFooter ( {
227+ text : 'KOG Bot' ,
228+ iconURL : interaction . guild ?. iconURL ( ) as string
229+ } )
230+ ] ,
231+ components : [ ]
232+ } ) ;
141233 }
142-
143- if ( buttonCollected . customId === 'done' ) {
144- let databaseUpdated = false ; // Placeholder for database interaction logic
145- const doneEmbed = new EmbedBuilder ( )
146- . setColor ( databaseUpdated ? "#9033FF" : Colors . Red )
147- . setTitle ( 'Event Logging Completed' )
148- . addFields (
149- { name : 'Host' , value : `<@${ interaction . user . id } >` , inline : true } ,
150- { name : 'Timestamp' , value : `<t:${ Math . floor ( Date . now ( ) / 1000 ) } :F>` , inline : true } ,
151- { name : 'Attendees' , value : mentions . join ( ', ' ) || 'None' , inline : false } ,
152- { name : 'Squadron Rally' , value : 'False' , inline : false } ,
153- { name : 'Database Updated' , value : databaseUpdated ? 'True' : 'Failed' , inline : false }
154- )
155- . setTimestamp ( ) ;
156-
157- // Fetch the specific channel
158- const logChannel = interaction . client . channels . cache . get ( '1002610487378321520' ) as TextChannel ; // i'll optimize this im just tired :(
159-
160- if ( logChannel ) {
161- await logChannel . send ( { embeds : [ doneEmbed ] } ) ;
162- } else {
163- console . error ( 'Log channel not found!' ) ;
234+ } catch ( err ) {
235+ console . log ( err ) ;
236+ }
164237 }
238+ }
165239
166- const loggedEmbed = new EmbedBuilder ( )
167- . setColor ( Colors . Green )
168- . setTitle ( 'Logged Successfully' )
169- . setDescription ( 'The event has been logged successfully.' )
170- . setTimestamp ( ) ;
240+ async function checkExistance ( kogBot : KOGBot , userIds : string [ ] ) {
241+ for ( const userId of userIds ) {
242+ const userData = await kogBot . database ( 'users' )
243+ . select ( '*' )
244+ . where ( 'discord_id' , userId )
245+ . first ( ) ;
171246
172- await buttonCollected . reply ( { embeds : [ loggedEmbed ] } ) ;
173- }
247+ if ( ! userData ) {
248+ await kogBot . database ( 'users' )
249+ . insert ( { discord_id : userId } ) ;
174250 }
175251 }
176252}
177253
178- export default LogEventCommand ;
254+ export default LogEventCommand ;
0 commit comments