From 191366157b72d0f02e449d4049a28a462eef6b23 Mon Sep 17 00:00:00 2001 From: hfcRed Date: Sat, 22 Nov 2025 19:03:02 +0100 Subject: [PATCH 1/6] Make reping errors ephemeral --- src/commands/reping-command.ts | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/commands/reping-command.ts b/src/commands/reping-command.ts index 69f64fb..46284db 100644 --- a/src/commands/reping-command.ts +++ b/src/commands/reping-command.ts @@ -22,14 +22,13 @@ export async function handleRepingCommand( telemetry?: TelemetryService, ) { try { - await interaction.deferReply(); - const userId = interaction.user.id; const userEventId = eventManager.userOwnsEvent(userId); if (!userEventId) { - await interaction.editReply({ + await interaction.reply({ content: ERROR_MESSAGES.NO_EVENT_OWNED, + flags: ['Ephemeral'], }); return; } @@ -51,8 +50,9 @@ export async function handleRepingCommand( const remainingMs = TIMINGS.REPING_COOLDOWN_MS - timeSinceLastUse; const remainingMinutes = Math.ceil(remainingMs / 60000); - await interaction.editReply({ + await interaction.reply({ content: ERROR_MESSAGES.REPING_COOLDOWN(remainingMinutes), + flags: ['Ephemeral'], }); return; } @@ -60,8 +60,9 @@ export async function handleRepingCommand( const channelId = eventManager.getChannelId(userEventId); if (!channelId) { - await interaction.editReply({ + await interaction.reply({ content: ERROR_MESSAGES.CHANNEL_NOT_FOUND, + flags: ['Ephemeral'], }); return; } @@ -72,8 +73,9 @@ export async function handleRepingCommand( ); if (!channel || !channel.isTextBased()) { - await interaction.editReply({ + await interaction.reply({ content: ERROR_MESSAGES.CHANNEL_NO_ACCESS, + flags: ['Ephemeral'], }); return; } @@ -84,8 +86,9 @@ export async function handleRepingCommand( ); if (!message || !message.embeds[0]) { - await interaction.editReply({ + await interaction.reply({ content: ERROR_MESSAGES.MESSAGE_NOT_FOUND, + flags: ['Ephemeral'], }); return; } @@ -98,20 +101,24 @@ export async function handleRepingCommand( const missingPlayers = MAX_PARTICIPANTS - currentCount; if (currentCount === MAX_PARTICIPANTS) { - await interaction.editReply({ + await interaction.reply({ content: ERROR_MESSAGES.REPING_EVENT_FULL, + flags: ['Ephemeral'], }); return; } const rolePing = getPingsForServer(interaction, isCasual); if (!rolePing) { - await interaction.editReply({ + await interaction.reply({ content: ERROR_MESSAGES.ROLE_NOT_FOUND, + flags: ['Ephemeral'], }); return; } + await interaction.deferReply(); + await eventManager.deleteRepingMessageIfExists( userEventId, interaction.client, From 71ffc23e70b57cb664fd227b423f6e13450a97f4 Mon Sep 17 00:00:00 2001 From: hfcRed Date: Sat, 22 Nov 2025 19:06:46 +0100 Subject: [PATCH 2/6] Hopefully fix mentions --- src/client/discord-client.ts | 2 +- src/commands/create-command.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/client/discord-client.ts b/src/client/discord-client.ts index 5d8c1de..52e9f3e 100644 --- a/src/client/discord-client.ts +++ b/src/client/discord-client.ts @@ -23,7 +23,7 @@ export function createDiscordClient() { GatewayIntentBits.MessageContent, GatewayIntentBits.DirectMessages, ], - allowedMentions: { parse: ['roles'] }, + allowedMentions: { parse: ['roles', 'users', 'everyone'] }, partials: [Partials.Channel], }); } diff --git a/src/commands/create-command.ts b/src/commands/create-command.ts index 8d7e28f..83d4bbc 100644 --- a/src/commands/create-command.ts +++ b/src/commands/create-command.ts @@ -60,6 +60,7 @@ export async function handleCreateCommand( content: rolePing || undefined, embeds: [embed], components: [buttonRow, selectRow], + allowedMentions: { parse: ['roles', 'users', 'everyone'] }, }); const message = await reply.fetch(); const matchId = randomUUID(); From f9dd16b2463f8eb5adc2281ad21c04ef53f054a2 Mon Sep 17 00:00:00 2001 From: hfcRed Date: Sat, 22 Nov 2025 19:11:48 +0100 Subject: [PATCH 3/6] Missed this --- src/commands/reping-command.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/commands/reping-command.ts b/src/commands/reping-command.ts index 46284db..6689aec 100644 --- a/src/commands/reping-command.ts +++ b/src/commands/reping-command.ts @@ -129,6 +129,7 @@ export async function handleRepingCommand( const reply = await interaction.editReply({ content: REPING_MESSAGE(rolePing, missingPlayers, messageUrl), + allowedMentions: { parse: ['roles', 'users', 'everyone'] }, }); const repingMessage = await withRetry( From 4eefbb1796ccea411ea0f1040f5d65f9f430a202 Mon Sep 17 00:00:00 2001 From: hfcRed Date: Sat, 22 Nov 2025 19:18:02 +0100 Subject: [PATCH 4/6] Update README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f3b5fd3..a68ac75 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,8 @@ The bot requires a handful of permissions to be granted when added to a server. - **Send Messages**: Required to reply to commands with a message - **Send Messages in Threads**: Required to send messages in threads - **Create Private Threads**: Required to create new private threads -- **Manage Threads**: Required to add/remove users from a thread, pin messages in threads, and close and archive threads +- **Manage Threads**: Required to add/remove users from a thread and close and archive threads +- **Mention @€veryone, @here, and All Roles**: Required to ping the casual and competitive roles - **Move Members**: Required to disconnect users from voice channels if kicked or dropped out ## Running locally From c3abb395b9f7f24208598c79273d0392ea636332 Mon Sep 17 00:00:00 2001 From: hfcRed Date: Sat, 22 Nov 2025 23:23:23 +0100 Subject: [PATCH 5/6] Bump version --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index 21e8796..ee90284 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -1.0.3 +1.0.4 From 881c31fa735cd8f7d94e2af4dda34d0d7afc8e8f Mon Sep 17 00:00:00 2001 From: hfcRed Date: Sat, 22 Nov 2025 23:32:43 +0100 Subject: [PATCH 6/6] Fix test --- src/tests/commands/reping-command.test.ts | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/tests/commands/reping-command.test.ts b/src/tests/commands/reping-command.test.ts index f5fc947..dec31c8 100644 --- a/src/tests/commands/reping-command.test.ts +++ b/src/tests/commands/reping-command.test.ts @@ -52,6 +52,7 @@ describe('reping-command', () => { fetch: vi.fn().mockResolvedValue(mockChannel), }, }, + reply: vi.fn().mockResolvedValue(undefined), deferReply: vi.fn().mockResolvedValue(undefined), editReply: vi.fn().mockResolvedValue(mockReply), }; @@ -102,6 +103,8 @@ describe('reping-command', () => { }); it('should reject if user does not own event', async () => { + const mockReply = vi.fn().mockResolvedValue(undefined); + mockInteraction.reply = mockReply; mockEventManager.userOwnsEvent.mockReturnValue(false as never); await handleRepingCommand( @@ -110,12 +113,15 @@ describe('reping-command', () => { mockTelemetry as never, ); - expect(mockInteraction.editReply).toHaveBeenCalledWith({ + expect(mockReply).toHaveBeenCalledWith({ content: ERROR_MESSAGES.NO_EVENT_OWNED, + flags: ['Ephemeral'], }); }); it('should enforce cooldown period', async () => { + const mockReply = vi.fn().mockResolvedValue(undefined); + mockInteraction.reply = mockReply; const now = Date.now(); mockEventManager.getRepingCooldown.mockReturnValue( now - TIMINGS.REPING_COOLDOWN_MS / 2, @@ -127,14 +133,17 @@ describe('reping-command', () => { mockTelemetry as never, ); - expect(mockInteraction.editReply).toHaveBeenCalledWith( + expect(mockReply).toHaveBeenCalledWith( expect.objectContaining({ content: expect.stringContaining('wait'), + flags: ['Ephemeral'], }), ); }); it('should reject if event is full', async () => { + const mockReply = vi.fn().mockResolvedValue(undefined); + mockInteraction.reply = mockReply; const participants = new Map([ ['user1', { userId: 'user1', role: 'None', rank: null }], ['user2', { userId: 'user2', role: 'Tank', rank: null }], @@ -153,8 +162,9 @@ describe('reping-command', () => { mockTelemetry as never, ); - expect(mockInteraction.editReply).toHaveBeenCalledWith({ + expect(mockReply).toHaveBeenCalledWith({ content: ERROR_MESSAGES.REPING_EVENT_FULL, + flags: ['Ephemeral'], }); }); @@ -187,6 +197,8 @@ describe('reping-command', () => { }); it('should reject if channel not found', async () => { + const mockReply = vi.fn().mockResolvedValue(undefined); + mockInteraction.reply = mockReply; mockEventManager.getChannelId.mockReturnValue(undefined as never); await handleRepingCommand( @@ -195,8 +207,9 @@ describe('reping-command', () => { mockTelemetry as never, ); - expect(mockInteraction.editReply).toHaveBeenCalledWith({ + expect(mockReply).toHaveBeenCalledWith({ content: ERROR_MESSAGES.CHANNEL_NOT_FOUND, + flags: ['Ephemeral'], }); });