diff --git a/src/app.ts b/src/app.ts index 612c9a60..250ee8fb 100644 --- a/src/app.ts +++ b/src/app.ts @@ -2,7 +2,7 @@ import { getHandler, interactionExtensions } from './interaction-handling/interaction-entry-point.js'; -import { Guild, Events } from 'discord.js'; +import { Guild, Events, ChannelType } from 'discord.js'; import { AttendingServer } from './attending-server/base-attending-server.js'; import { black, green, red, yellow } from './utils/command-line-colors.js'; import { EmbedColor, SimpleEmbed } from './utils/embed-helper.js'; @@ -128,6 +128,19 @@ client.on(Events.GuildMemberAdd, async member => { } }); +/** + * If a queue is renamed manually, it renames the queue name for queue embeds, roles, and calendar extension + */ +client.on(Events.ChannelUpdate, async (oldChannel, newChannel) => { + if ( + oldChannel.type === ChannelType.GuildCategory && + newChannel.type === ChannelType.GuildCategory && + oldChannel.name !== newChannel.name + ) { + await AttendingServer.safeGet(oldChannel.guild.id)?.updateQueueName(oldChannel, newChannel); + } +}); + /** * Used for inviting YABOB to a server with existing roles * Once YABOB has the highest role, start the initialization call diff --git a/src/attending-server/base-attending-server.ts b/src/attending-server/base-attending-server.ts index 17aa7a08..8315c232 100644 --- a/src/attending-server/base-attending-server.ts +++ b/src/attending-server/base-attending-server.ts @@ -555,6 +555,56 @@ class AttendingServer { await this.getQueueChannels(false); } + /** + * Updates the queue name for queue embeds, roles, and calendar extension + * @param oldChannel Old category channel before the name was updated + * @param newChannel New category channel after the name was updated + */ + async updateQueueName(oldChannel: CategoryChannel, newChannel: CategoryChannel) { + const oldName = oldChannel.name; + const newName = newChannel.name; + const channelQueue = this._queues.get(oldChannel.id); + if (!channelQueue) { + return; + } + const role = this.guild.roles.cache.find(role => role.name === oldChannel.name); + const newQueueChannel: QueueChannel = { + channelObj: channelQueue.queueChannel.channelObj, + queueName: newName, + parentCategoryId: channelQueue.parentCategoryId + }; + const nameTaken = this._queues.some( + queue => queue.queueName === newName && queue !== channelQueue + ); + const roleTaken = this.guild.roles.cache.some( + role => role.name === newChannel.name + ); + const cachedChannelIndex = this.queueChannelsCache.findIndex( + queueChannel => queueChannel.queueName === oldName + ); + if (cachedChannelIndex !== -1){ + this.queueChannelsCache.splice(cachedChannelIndex, 1); + this.queueChannelsCache.push(newQueueChannel); + } + if (nameTaken) { + await newChannel.setName(oldName); + LOGGER.error( + `Queue name '${newName}' already exists. Rename '${oldName}' to a different name.` + ); + return; + } + if (role && !roleTaken) { + await role.setName(newName); + } + channelQueue.queueChannel = newQueueChannel; + await channelQueue.triggerRender(); + await Promise.all( + this.serverExtensions.map(extension => + extension.onQueueChannelUpdate(this, oldName, newQueueChannel) + ) + ); + } + /** * Deletes a queue by categoryID * @param parentCategoryId CategoryChannel.id of the target queue diff --git a/src/extensions/extension-interface.ts b/src/extensions/extension-interface.ts index ae94e3b1..4d84ae84 100644 --- a/src/extensions/extension-interface.ts +++ b/src/extensions/extension-interface.ts @@ -26,6 +26,7 @@ import { SelectMenuHandlerProps } from '../interaction-handling/handler-interface.js'; import { CommandData } from '../utils/type-aliases.js'; +import { QueueChannel } from '../attending-server/base-attending-server.js'; interface InteractionExtension { /** @@ -161,6 +162,14 @@ interface ServerExtension { * @param server the server to backup */ onServerRequestBackup: (server: FrozenServer) => Promise; + /** + * When a queue category channel is updated + * @param server the server of the category queue + * @param oldName old name of queue category channel + * @param newChannel new queue category channel object + * @returns + */ + onQueueChannelUpdate: (server: FrozenServer, oldName: string, newChannel: QueueChannel) => Promise; } /** Extensions for individual queues */ @@ -333,6 +342,9 @@ abstract class BaseServerExtension implements ServerExtension { onServerRequestBackup(server: FrozenServer): Promise { return Promise.resolve(); } + onQueueChannelUpdate(server: FrozenServer, oldName: string, newChannel: QueueChannel): Promise{ + return Promise.resolve(); + } } /** diff --git a/src/extensions/session-calendar/calendar-queue-extension.ts b/src/extensions/session-calendar/calendar-queue-extension.ts index b6999954..3988ca86 100644 --- a/src/extensions/session-calendar/calendar-queue-extension.ts +++ b/src/extensions/session-calendar/calendar-queue-extension.ts @@ -27,7 +27,7 @@ class CalendarQueueExtension extends BaseQueueExtension { */ private constructor( private readonly renderIndex: RenderIndex, - private readonly queueChannel: QueueChannel, + public queueChannel: QueueChannel, private readonly display: FrozenDisplay ) { super(); diff --git a/src/extensions/session-calendar/calendar-server-extension.ts b/src/extensions/session-calendar/calendar-server-extension.ts index e5e7b05c..49b0c1e0 100644 --- a/src/extensions/session-calendar/calendar-server-extension.ts +++ b/src/extensions/session-calendar/calendar-server-extension.ts @@ -3,6 +3,7 @@ import { BaseServerExtension } from '../extension-interface.js'; import { FrozenServer } from '../extension-utils.js'; import { CalendarExtensionState } from './calendar-states.js'; import { CALENDAR_LOGGER } from './shared-calendar-functions.js'; +import { QueueChannel } from '../../attending-server/base-attending-server.js'; /** * Server extension of session calendar @@ -50,6 +51,21 @@ class CalendarServerExtension extends BaseServerExtension { clearInterval(this.timerId); CalendarExtensionState.allStates.delete(server.guild.id); } + + /** + * If a queue channel name is changed on discord, update the calendar name + * @param server the server of the changed queue channel + * @param oldName new name of queue channel + * @param newChannel new queue channel object + */ + override async onQueueChannelUpdate( + server: FrozenServer, + oldName: string, + newChannel: QueueChannel + ): Promise { + const state = CalendarExtensionState.get(server.guild.id); + await state.renameCalendar(oldName, newChannel); + } } export { CalendarServerExtension }; diff --git a/src/extensions/session-calendar/calendar-states.ts b/src/extensions/session-calendar/calendar-states.ts index d9536368..48305131 100644 --- a/src/extensions/session-calendar/calendar-states.ts +++ b/src/extensions/session-calendar/calendar-states.ts @@ -17,6 +17,7 @@ import { z } from 'zod'; import { CalendarServerExtension } from './calendar-server-extension.js'; import { ExpectedCalendarErrors } from './calendar-constants/expected-calendar-errors.js'; import { ServerExtension } from '../extension-interface.js'; +import { QueueChannel } from '../../attending-server/base-attending-server.js'; import { Logger } from 'pino'; /** @@ -230,6 +231,23 @@ class CalendarExtensionState { await this.emitStateChangeEvent(); } + /** + * Resets the name of the queue extension + * change the key(queue channel name) that maps to the value (queue extension object) + * @param oldName + * @param newQueueChannel + */ + async renameCalendar(oldName: string, newQueueChannel: QueueChannel) { + const newName = newQueueChannel.queueName; + const object = this.queueExtensions.get(oldName); + if (object) { + this.queueExtensions.set(newName, object); + this.queueExtensions.delete(oldName); + object.queueChannel = newQueueChannel; + await this.emitStateChangeEvent(); + } + } + /** * Backs up the calendar config to firebase * - This is synchronous, we don't need to `await backupToFirebase` diff --git a/src/help-queue/help-queue.ts b/src/help-queue/help-queue.ts index bb74b4e6..d0d18d4a 100644 --- a/src/help-queue/help-queue.ts +++ b/src/help-queue/help-queue.ts @@ -92,7 +92,7 @@ class HelpQueue { * @param backupData if defined, use this data to restore the students array */ protected constructor( - public readonly queueChannel: QueueChannel, + public queueChannel: QueueChannel, private readonly queueExtensions: QueueExtension[], private readonly display: QueueDisplay, backupData?: QueueBackup & {