Skip to content
This repository was archived by the owner on May 15, 2025. It is now read-only.

Commit ad58956

Browse files
committed
feat: Event log command.
1 parent 0ac6653 commit ad58956

File tree

1 file changed

+210
-134
lines changed

1 file changed

+210
-134
lines changed

src/commands/main/log.ts

Lines changed: 210 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -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';
1418
import { 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

Comments
 (0)