diff --git a/.version b/.version index 90a27f9..af0b7dd 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -1.0.5 +1.0.6 diff --git a/src/client/shutdown.ts b/src/client/shutdown.ts index 8a9a97f..af4c49c 100644 --- a/src/client/shutdown.ts +++ b/src/client/shutdown.ts @@ -73,35 +73,6 @@ export async function gracefulShutdown( const [eventId] = allTimers[i]; const channelId = eventManager.getChannelId(eventId); - let isFinalizing = false; - - if (channelId) { - const channel = await withRetryOrNull( - () => client.channels.fetch(channelId), - MEDIUM_RETRY_OPTIONS, - ); - - if (channel?.isTextBased() && !channel.isDMBased()) { - const message = await withRetryOrNull( - () => channel.messages.fetch(eventId), - MEDIUM_RETRY_OPTIONS, - ); - - if (message) { - isFinalizing = eventManager.isEventFinalizing(message); - } - } - } - - if (isFinalizing) { - const waitTime = - TIMINGS.EVENT_START_DELAY_MINUTES * TIMINGS.MINUTE_IN_MS * 2; - console.log( - `Event ${eventId} is finalizing, waiting ${waitTime}ms before cleanup...`, - ); - - await new Promise((resolve) => setTimeout(resolve, waitTime)); - } console.log( `Updating event message ${i + 1}/${allTimers.length}: ${eventId}`, diff --git a/src/commands/kick-command.ts b/src/commands/kick-command.ts index 52019b2..2d9cd8e 100644 --- a/src/commands/kick-command.ts +++ b/src/commands/kick-command.ts @@ -135,14 +135,8 @@ export async function handleKickCommand( if (timerData && updatedParticipants) { const embed = EmbedBuilder.from(message.embeds[0]); - const isFinalizing = eventManager.isEventFinalizing(message); - updateParticipantFields( - embed, - updatedParticipants, - timerData, - isFinalizing, - ); + updateParticipantFields(embed, updatedParticipants); const queue = eventManager.getQueue(userEventId); updateQueueField(embed, queue); diff --git a/src/constants.ts b/src/constants.ts index 1b16a7f..82b6491 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -29,7 +29,7 @@ export const TIMINGS = { HOUR_IN_MS: 60 * 60 * 1000, DAY_IN_MS: 24 * 60 * 60 * 1000, PROCESSING_TIMEOUT_MS: 30000, - EVENT_START_DELAY_MINUTES: DEV ? 0 : 0.25, + EVENT_START_DELAY_MINUTES: DEV ? 0 : 0, SHUTDOWN_EVENT_CLEANUP_DELAY_MS: 2000, REPING_COOLDOWN_MS: 15 * 60 * 1000, } as const; @@ -59,7 +59,6 @@ export const PARTICIPANT_FIELD_NAME = (current: number) => export const COLORS = { OPEN: '#626CE9', - FINALIZING: '#E9D662', STARTED: '#1cff5c', CANCELLED: '#ff1c1c', FINISHED: '#ff1c1c', @@ -90,11 +89,10 @@ export const THREAD_NAME = (shortId: string) => export const STATUS_MESSAGES = { OPEN: '🟢 Open for Sign Ups', READY: '✅ Ready to Start!', - FINALIZING: '⏳ Finalizing...', STARTED: '✅ Event Started!', CANCELLED: '❌ Event cancelled', FINISHED: '🏁 Event Finished', - EXPIRED: '⏰ Event Expired (24h timeout)', + EXPIRED: '⏰ Event Expired (6h timeout)', SHUTDOWN: '⚠️ Event closed due to bot shutdown!', } as const; @@ -108,8 +106,6 @@ export const ERROR_MESSAGES = { 'You are already signed up for an event. Please sign out, cancel, or wait for the event to finish before joining a new one.', NOT_SIGNED_UP: 'You need to be signed up to perform this action.', EVENT_FULL: 'This event is already full! You cannot sign up.', - EVENT_FINALIZING: - 'The event is finalizing and will start soon. Only role changes are allowed.', CREATOR_ONLY_START: 'Only the event creator can start the event.', CREATOR_ONLY_CANCEL: diff --git a/src/event/event-lifecycle.ts b/src/event/event-lifecycle.ts index 3fd838d..93f8fc5 100644 --- a/src/event/event-lifecycle.ts +++ b/src/event/event-lifecycle.ts @@ -204,7 +204,7 @@ export async function cleanupStaleEvents( voiceChannelManager: VoiceChannelManager, telemetry?: TelemetryService, ) { - const MAX_EVENT_LIFETIME = TIMINGS.DAY_IN_MS; + const MAX_EVENT_LIFETIME = TIMINGS.HOUR_IN_MS * 6; const now = Date.now(); for (const [messageId, timerData] of eventManager.getAllTimers()) { diff --git a/src/event/event-manager.ts b/src/event/event-manager.ts index f12a860..78ede6c 100644 --- a/src/event/event-manager.ts +++ b/src/event/event-manager.ts @@ -1,5 +1,5 @@ -import { type Client, EmbedBuilder, type Message } from 'discord.js'; -import { FIELD_NAMES, STATUS_MESSAGES, TIMINGS } from '../constants.js'; +import { type Client, EmbedBuilder } from 'discord.js'; +import { TIMINGS } from '../constants.js'; import type { TelemetryService } from '../telemetry/telemetry.js'; import { updateQueueField } from '../utils/embed-utils.js'; import { ErrorSeverity, handleError } from '../utils/error-handler.js'; @@ -303,16 +303,6 @@ export class EventManager { return this.userToEventIndex.has(userId); } - isEventFinalizing(message: Message) { - const embed = message.embeds[0]; - if (!embed || !embed.fields) return false; - - const statusField = embed.fields.find( - (field) => field.name === FIELD_NAMES.STATUS, - ); - return statusField?.value === STATUS_MESSAGES.FINALIZING; - } - getQueue(eventId: string) { return this.queues.get(eventId) || []; } diff --git a/src/interactions/button-handlers.ts b/src/interactions/button-handlers.ts index 965f0e7..259ab9a 100644 --- a/src/interactions/button-handlers.ts +++ b/src/interactions/button-handlers.ts @@ -476,7 +476,7 @@ export async function handleDropOutButton( const updatedParticipantMap = eventManager.getParticipants(messageId); if (updatedParticipantMap) { - updateParticipantFields(embed, updatedParticipantMap, timerData, false); + updateParticipantFields(embed, updatedParticipantMap); } const queue = eventManager.getQueue(messageId); @@ -578,7 +578,7 @@ export async function handleDropInButton( } const embed = EmbedBuilder.from(interaction.message.embeds[0]); - updateParticipantFields(embed, participantMap, timerData, false); + updateParticipantFields(embed, participantMap); await interaction.editReply({ embeds: [embed] }); const matchId = eventManager.getMatchId(messageId); @@ -615,14 +615,8 @@ async function updateParticipantEmbed( telemetry?: TelemetryService, ) { const embed = EmbedBuilder.from(interaction.message.embeds[0]); - const isFinalizing = eventManager.isEventFinalizing(interaction.message); - updateParticipantFields(embed, participantMap, timerData, isFinalizing); - - if (isFinalizing) { - await interaction.editReply({ embeds: [embed] }); - return; - } + updateParticipantFields(embed, participantMap); const timeElapsed = Date.now() - timerData.startTime; const timeIsUpOrNotSet = diff --git a/src/interactions/menu-handlers.ts b/src/interactions/menu-handlers.ts index 73ed5ea..f2d5e48 100644 --- a/src/interactions/menu-handlers.ts +++ b/src/interactions/menu-handlers.ts @@ -1,6 +1,6 @@ import type { GuildMember, StringSelectMenuInteraction } from 'discord.js'; import { EmbedBuilder } from 'discord.js'; -import { ERROR_MESSAGES, FIELD_NAMES, STATUS_MESSAGES } from '../constants.js'; +import { ERROR_MESSAGES } from '../constants.js'; import type { EventManager } from '../event/event-manager.js'; import { updateParticipantFields } from '../utils/embed-utils.js'; import { ErrorSeverity, handleError } from '../utils/error-handler.js'; @@ -51,11 +51,8 @@ export async function handleRoleSelection( if (!timerData) return; const embed = EmbedBuilder.from(interaction.message.embeds[0]); - const isFinalizing = - embed.data.fields?.find((field) => field.name === FIELD_NAMES.STATUS) - ?.value === STATUS_MESSAGES.FINALIZING; - updateParticipantFields(embed, participantMap, timerData, isFinalizing); + updateParticipantFields(embed, participantMap); await interaction.editReply({ embeds: [embed] }); } catch (error) { diff --git a/src/tests/commands/kick-command.test.ts b/src/tests/commands/kick-command.test.ts index 855815d..43ecdf6 100644 --- a/src/tests/commands/kick-command.test.ts +++ b/src/tests/commands/kick-command.test.ts @@ -88,7 +88,6 @@ describe('kick-command', () => { getVoiceChannels: vi.fn(), getMatchId: vi.fn(() => 'match123'), removeParticipant: vi.fn(), - isEventFinalizing: vi.fn(() => false), getQueue: vi.fn(() => []), }; } diff --git a/src/tests/event/event-manager.test.ts b/src/tests/event/event-manager.test.ts index 81cb9f0..46bcee5 100644 --- a/src/tests/event/event-manager.test.ts +++ b/src/tests/event/event-manager.test.ts @@ -1,6 +1,5 @@ -import type { Client, Message } from 'discord.js'; +import type { Client } from 'discord.js'; import { beforeEach, describe, expect, it, vi } from 'vitest'; -import { FIELD_NAMES, STATUS_MESSAGES } from '../../constants.js'; import { EventManager, type EventTimer, @@ -259,50 +258,6 @@ describe('EventManager', () => { }); }); - describe('isEventFinalizing', () => { - it('should return true when status is finalizing', () => { - const message = { - embeds: [ - { - fields: [ - { - name: FIELD_NAMES.STATUS, - value: STATUS_MESSAGES.FINALIZING, - }, - ], - }, - ], - } as unknown as Message; - - expect(eventManager.isEventFinalizing(message)).toBe(true); - }); - - it('should return false when status is not finalizing', () => { - const message = { - embeds: [ - { - fields: [ - { - name: FIELD_NAMES.STATUS, - value: STATUS_MESSAGES.OPEN, - }, - ], - }, - ], - } as unknown as Message; - - expect(eventManager.isEventFinalizing(message)).toBe(false); - }); - - it('should return false when no embeds', () => { - const message = { - embeds: [], - } as unknown as Message; - - expect(eventManager.isEventFinalizing(message)).toBe(false); - }); - }); - describe('clearAllEventData', () => { it('should clear all data associated with event', () => { const participants: ParticipantMap = new Map([ diff --git a/src/tests/interactions/button-handlers.test.ts b/src/tests/interactions/button-handlers.test.ts index f5ef1b5..c2020be 100644 --- a/src/tests/interactions/button-handlers.test.ts +++ b/src/tests/interactions/button-handlers.test.ts @@ -85,7 +85,6 @@ describe('button-handlers', () => { getThread: vi.fn(), getVoiceChannels: vi.fn(), isUserInAnyEvent: vi.fn(() => false), - isEventFinalizing: vi.fn(() => false), addParticipant: vi.fn(), removeParticipant: vi.fn(), setProcessing: vi.fn(), diff --git a/src/tests/utils/embed-utils.test.ts b/src/tests/utils/embed-utils.test.ts index 54e7f8e..21b6c6e 100644 --- a/src/tests/utils/embed-utils.test.ts +++ b/src/tests/utils/embed-utils.test.ts @@ -8,7 +8,7 @@ import { STATUS_MESSAGES, WEAPON_ROLES, } from '../../constants.js'; -import type { EventTimer, ParticipantMap } from '../../event/event-manager.js'; +import type { ParticipantMap } from '../../event/event-manager.js'; import { createEventButtons, createEventEmbed, @@ -268,13 +268,7 @@ describe('embed-utils', () => { ['user2', { userId: 'user2', role: 'Support', rank: null }], ]); - const timerData: EventTimer = { - startTime: Date.now(), - duration: undefined, - hasStarted: false, - }; - - updateParticipantFields(embed, participantMap, timerData, false); + updateParticipantFields(embed, participantMap); const fields = embed.data.fields || []; const participantField = fields.find((f) => @@ -301,74 +295,12 @@ describe('embed-utils', () => { }); } - const timerData: EventTimer = { - startTime: Date.now(), - duration: 10 * 60 * 1000, - hasStarted: false, - }; - - updateParticipantFields(embed, participantMap, timerData, false); + updateParticipantFields(embed, participantMap); const fields = embed.data.fields || []; const statusField = fields.find((f) => f.name === FIELD_NAMES.STATUS); - expect([STATUS_MESSAGES.READY, STATUS_MESSAGES.FINALIZING]).toContain( - statusField?.value, - ); - }); - - it('should set finalizing color and status when full and timer expired', () => { - const embed = new EmbedBuilder().addFields( - { name: PARTICIPANT_FIELD_NAME(1), value: '- <@user1>' }, - { name: FIELD_NAMES.ROLE, value: '- None' }, - { name: FIELD_NAMES.STATUS, value: STATUS_MESSAGES.OPEN }, - ); - - const participantMap: ParticipantMap = new Map(); - for (let i = 0; i < MAX_PARTICIPANTS; i++) { - participantMap.set(`user${i}`, { - userId: `user${i}`, - role: 'None', - rank: null, - }); - } - - const timerData: EventTimer = { - startTime: Date.now() - 20 * 60 * 1000, - duration: 10 * 60 * 1000, - hasStarted: false, - }; - - updateParticipantFields(embed, participantMap, timerData, false); - - expect(embed.data.color).toBe(parseInt(COLORS.FINALIZING.slice(1), 16)); - const fields = embed.data.fields || []; - const statusField = fields.find((f) => f.name === FIELD_NAMES.STATUS); - expect(statusField?.value).toBe(STATUS_MESSAGES.FINALIZING); - }); - - it('should not update status when finalizing', () => { - const embed = new EmbedBuilder().addFields( - { name: PARTICIPANT_FIELD_NAME(1), value: '- <@user1>' }, - { name: FIELD_NAMES.ROLE, value: '- None' }, - { name: FIELD_NAMES.STATUS, value: STATUS_MESSAGES.FINALIZING }, - ); - - const participantMap: ParticipantMap = new Map([ - ['user1', { userId: 'user1', role: 'None', rank: null }], - ]); - - const timerData: EventTimer = { - startTime: Date.now(), - duration: undefined, - hasStarted: false, - }; - - updateParticipantFields(embed, participantMap, timerData, true); - - const fields = embed.data.fields || []; - const statusField = fields.find((f) => f.name === FIELD_NAMES.STATUS); - expect(statusField?.value).toBe(STATUS_MESSAGES.FINALIZING); + expect([STATUS_MESSAGES.READY]).toContain(statusField?.value); }); it('should update roles alongside participants', () => { @@ -383,13 +315,7 @@ describe('embed-utils', () => { ['user2', { userId: 'user2', role: '🛡️ Support', rank: null }], ]); - const timerData: EventTimer = { - startTime: Date.now(), - duration: undefined, - hasStarted: false, - }; - - updateParticipantFields(embed, participantMap, timerData, false); + updateParticipantFields(embed, participantMap); const fields = embed.data.fields || []; const roleField = fields.find((f) => f.name === FIELD_NAMES.ROLE); diff --git a/src/utils/embed-utils.ts b/src/utils/embed-utils.ts index 58e1a16..480b954 100644 --- a/src/utils/embed-utils.ts +++ b/src/utils/embed-utils.ts @@ -17,7 +17,7 @@ import { TITLES, WEAPON_ROLES, } from '../constants.js'; -import type { EventTimer, ParticipantMap } from '../event/event-manager.js'; +import type { ParticipantMap } from '../event/event-manager.js'; export function updateEmbedField( embed: EmbedBuilder, @@ -171,8 +171,6 @@ export function createRoleSelectMenu() { export function updateParticipantFields( embed: EmbedBuilder, participantMap: ParticipantMap, - timerData: EventTimer, - isFinalizing: boolean, ) { updateEmbedFieldByMatch( embed, @@ -191,28 +189,11 @@ export function updateParticipantFields( .join('\n'), ); - if (isFinalizing) { - return; - } - const status = participantMap.size === MAX_PARTICIPANTS ? STATUS_MESSAGES.READY : STATUS_MESSAGES.OPEN; updateEmbedField(embed, FIELD_NAMES.STATUS, status); - - const timeElapsed = Date.now() - timerData.startTime; - const timeIsUpOrNotSet = - !timerData.duration || timeElapsed >= timerData.duration; - - if ( - participantMap.size === MAX_PARTICIPANTS && - timeIsUpOrNotSet && - !timerData.hasStarted - ) { - embed.setColor(COLORS.FINALIZING); - updateEmbedField(embed, FIELD_NAMES.STATUS, STATUS_MESSAGES.FINALIZING); - } } export function updateQueueField(embed: EmbedBuilder, queue: string[]) { diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 5bc3a5c..c275eb8 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -11,7 +11,6 @@ import type { import { PermissionFlagsBits } from 'discord.js'; import { ADMIN_PERMISSIONS, - ERROR_MESSAGES, EXCALIBUR_GUILD_ID, EXCALIBUR_RANKS, PING_ROLE_NAMES, @@ -154,14 +153,5 @@ export async function checkProcessingStates( return true; } - if ( - interaction && - 'message' in interaction && - eventManager.isEventFinalizing(interaction.message) - ) { - await safeReplyToInteraction(interaction, ERROR_MESSAGES.EVENT_FINALIZING); - return true; - } - return false; }