From c31209c707dbcea26948debad26840f106d6e914 Mon Sep 17 00:00:00 2001 From: Yoel Ferreyra Date: Mon, 24 Mar 2025 19:41:09 -0300 Subject: [PATCH 1/5] fix: adding new configuration to handle timezone --- src/cron/schedule-google-calendar.js | 5 +++-- src/utils/date-to-cron-expression.js | 16 ++++++++++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/cron/schedule-google-calendar.js b/src/cron/schedule-google-calendar.js index b32175a6..465de934 100644 --- a/src/cron/schedule-google-calendar.js +++ b/src/cron/schedule-google-calendar.js @@ -74,11 +74,11 @@ const scheduleEventNotification = async ({ client, event }) => { const startDate = new Date(event.start.dateTime); startDate.setMinutes(startDate.getMinutes() - minutesBeforeEvent); - const cronExpression = dateToCronExpression(startDate); + const cronExpression = dateToCronExpression(startDate, event.start.timeZone); const job = new CronJob( cronExpression, async () => { - await sendCalendarEventNotification(); + await sendCalendarEventNotification(client, event); }, null, true, @@ -123,6 +123,7 @@ const setupCalendarNotifications = async (client) => { () => { console.log('Running scheduled calendar notifications...'); scheduleCalendarNotifications(client); + console.log(activeCronJobs); }, null, true, diff --git a/src/utils/date-to-cron-expression.js b/src/utils/date-to-cron-expression.js index 6c42b87e..e6d6084b 100644 --- a/src/utils/date-to-cron-expression.js +++ b/src/utils/date-to-cron-expression.js @@ -1,3 +1,5 @@ +const moment = require('moment-timezone'); + /** * Converts a date string into a CRON expression. * @@ -12,12 +14,14 @@ * dateToCronExpression('2022-01-01T00:00:00'); * // Returns '0 0 1 1 *' */ -function dateToCronExpression(dateString) { - const date = new Date(dateString); - const minutes = date.getMinutes(); - const hours = date.getHours(); - const day = date.getDate(); - const month = date.getMonth() + 1; +function dateToCronExpression(dateString, eventTimeZone = 'America/Bogota') { + const date = moment.tz(dateString, eventTimeZone); + + const minutes = date.minutes(); + const hours = date.hours(); + const day = date.date(); + const month = date.month() + 1; // Meses en moment van de 0-11, sumamos 1 + return `${minutes} ${hours} ${day} ${month} *`; } From 76db13dee9d8ff460705fb8c098e2d5ee4692d08 Mon Sep 17 00:00:00 2001 From: Yoel Ferreyra Date: Tue, 1 Apr 2025 14:56:34 -0300 Subject: [PATCH 2/5] fix: branch update --- src/commands/utility/changeStatus.js | 116 ++++++++++++++++---------- src/config.js | 38 ++------- src/cron/schedule-code-review.js | 7 +- src/cron/schedule-google-calendar.js | 5 +- src/events/change-status-event.js | 59 ------------- src/events/eventList.js | 2 - src/languages/translations/en-US.json | 5 +- src/languages/translations/es-ES.json | 5 +- 8 files changed, 86 insertions(+), 151 deletions(-) delete mode 100644 src/events/change-status-event.js diff --git a/src/commands/utility/changeStatus.js b/src/commands/utility/changeStatus.js index 279e78ca..290fc9bc 100644 --- a/src/commands/utility/changeStatus.js +++ b/src/commands/utility/changeStatus.js @@ -1,45 +1,92 @@ -const { - SlashCommandBuilder, - StringSelectMenuBuilder, - StringSelectMenuOptionBuilder, - ActionRowBuilder, -} = require('discord.js'); +const { SlashCommandBuilder } = require('discord.js'); const { MAPPED_STATUS_COMMANDS } = require('../../config'); const { translateLanguage, keyTranslations } = require('../../languages'); const { sendErrorToChannel } = require('../../utils/send-error'); -const createStatusMenu = (channel) => { - const selectedChannel = MAPPED_STATUS_COMMANDS[channel] ? channel : 'novabot'; +const COMMAND_KEYS = Object.keys(MAPPED_STATUS_COMMANDS); - return new StringSelectMenuBuilder() - .setCustomId('change_status_select') - .setPlaceholder(translateLanguage('changeStatus.placeholder')) - .addOptions( - Object.keys(MAPPED_STATUS_COMMANDS[selectedChannel]).map((status) => { - return new StringSelectMenuOptionBuilder() - .setLabel(status) - .setDescription(status) - .setValue(status); - }) +const setThreadNameWithStatus = async ({ channel, newStatus }) => { + const emojisRegExp = new RegExp( + `(${Object.values(MAPPED_STATUS_COMMANDS).join('|')})`, + 'ig' + ); + + const channelName = channel.name.replace(emojisRegExp, '').trim(); + + const updatedChannelName = `${newStatus} ${channelName}`; + await channel.setName(updatedChannelName); +}; + +const updateThreadStatus = async (interaction) => { + const { options, channel, user } = interaction; + const status = options.getString('status'); + const message = options.getString('message'); + const newStatus = MAPPED_STATUS_COMMANDS[status]; + + if (!newStatus) { + return await interaction.editReply( + translateLanguage('changeStatus.invalidStatus') ); + } + + await setThreadNameWithStatus({ channel, newStatus }); + + if (message) { + const markdownMessage = + `# ${newStatus} ${status.replaceAll('-', ' ')}\n\n` + + `${message}\n\n` + + `> ${user}`; + await channel.send(markdownMessage); + } + + await interaction.editReply( + translateLanguage('changeStatus.updatedStatus', { + status: status.replaceAll('-', ' '), + }) + ); }; module.exports = { data: new SlashCommandBuilder() .setName('change-status') .setDescription(translateLanguage('changeStatus.description')) - .setDescriptionLocalizations(keyTranslations('changeStatus.description')), + .setDescriptionLocalizations(keyTranslations('changeStatus.description')) + .addStringOption((option) => + option + .setName('status') + .setDescription(translateLanguage('changeStatus.statusOption')) + .setDescriptionLocalizations( + keyTranslations('changeStatus.statusOption') + ) + .setRequired(true) + .addChoices( + COMMAND_KEYS.map((command) => ({ + name: command.replaceAll('pr-', '').replaceAll('-', ' '), + value: command, + })) + ) + ) + .addStringOption((option) => + option + .setName('message') + .setDescription(translateLanguage('changeStatus.messageOption')) + .setDescriptionLocalizations( + keyTranslations('changeStatus.messageOption') + ) + .setRequired(false) + ), async execute(interaction) { try { - const { channel } = interaction; - const selectMenu = createStatusMenu(channel.name); + await interaction.deferReply({ ephemeral: true }); - const row = new ActionRowBuilder().addComponents(selectMenu); + if (!interaction.channel.isThread()) { + return await interaction.editReply({ + content: translateLanguage('changeStatus.notAThread'), + ephemeral: true, + }); + } - await interaction.reply({ - content: translateLanguage('changeStatus.selectStatus'), - components: [row], - }); + await updateThreadStatus(interaction); } catch (error) { console.error(error); await sendErrorToChannel(interaction, error); @@ -47,20 +94,3 @@ module.exports = { } }, }; - -module.exports.handleInteraction = async (interaction) => { - if (!interaction.isStringSelectMenu()) { - return; - } - - if (interaction.customId === 'change_status_select') { - const selectedStatus = interaction.values[0]; - - await interaction.update({ - content: translateLanguage('changeStatus.success', { - status: selectedStatus, - }), - components: [], - }); - } -}; diff --git a/src/config.js b/src/config.js index 4f998584..5760e4a2 100644 --- a/src/config.js +++ b/src/config.js @@ -18,36 +18,14 @@ const LISTEN_NEW_EVENTS = { const MAPPED_STATUS_COMMANDS = { // unicode emojis - novabot: { - 'pr-request-review': '❗', - 'pr-request-changes': 'πŸ”', - 'pr-approved-by-code-review': 'πŸ‘', - 'pr-task-cancelled': '🚫', - 'pr-work-in-progress': 'πŸ‘·πŸΎ', - 'pr-merged-on-dev': '🟑', - 'pr-merged-in-prod': '🟒', - 'pr-done': 'βœ…', - }, - 'i18n-populator': { - 'pr-request-review': '❗', - 'pr-request-changes': 'πŸ”', - 'pr-approved-by-code-review': 'πŸ‘', - 'pr-task-cancelled': '🚫', - 'pr-work-in-progress': 'πŸ‘·πŸΎ', - 'pr-merged-on-dev': '🟑', - 'pr-merged-in-master': '🟒', - 'pr-done': 'βœ…', - }, - 'evo-crypter': { - 'pr-request-review': '❗', - 'pr-request-changes': 'πŸ”', - 'pr-approved-by-code-review': 'πŸ‘', - 'pr-task-cancelled': '🚫', - 'pr-work-in-progress': 'πŸ‘·πŸΎ', - 'pr-merged-on-staging': '🟑', - 'pr-merged-in-prod': '🟒', - 'pr-done': 'βœ…', - }, + 'pr-request-review': '❗', + 'pr-request-changes': 'πŸ”', + 'pr-approved-by-code-review': 'πŸ‘', + 'pr-task-cancelled': '🚫', + 'pr-work-in-progress': 'πŸ‘·πŸΎ', + 'pr-merged-on-staging': '🟑', + 'pr-merged-in-prod': '🟒', + 'pr-done': 'βœ…', }; const PR_TEMPLATE = { diff --git a/src/cron/schedule-code-review.js b/src/cron/schedule-code-review.js index 22fb43c0..d8cd471d 100644 --- a/src/cron/schedule-code-review.js +++ b/src/cron/schedule-code-review.js @@ -12,18 +12,13 @@ const { const STATUS_KEY = 'pr-request-review'; -const flatMappedStatusCommands = Object.values(MAPPED_STATUS_COMMANDS).reduce( - (acc, statuses) => ({ ...acc, ...statuses }), - {} -); - /** * Gets the mapped status text. * @param {string} key - Status key. * @returns {string|null} - Mapped text or null if not found. */ const getMappedStatusText = (key) => { - const statusText = flatMappedStatusCommands[key]; + const statusText = MAPPED_STATUS_COMMANDS[key]; if (!statusText) { console.error(`Mapped status for ${key} not found.`); return null; diff --git a/src/cron/schedule-google-calendar.js b/src/cron/schedule-google-calendar.js index 465de934..b32175a6 100644 --- a/src/cron/schedule-google-calendar.js +++ b/src/cron/schedule-google-calendar.js @@ -74,11 +74,11 @@ const scheduleEventNotification = async ({ client, event }) => { const startDate = new Date(event.start.dateTime); startDate.setMinutes(startDate.getMinutes() - minutesBeforeEvent); - const cronExpression = dateToCronExpression(startDate, event.start.timeZone); + const cronExpression = dateToCronExpression(startDate); const job = new CronJob( cronExpression, async () => { - await sendCalendarEventNotification(client, event); + await sendCalendarEventNotification(); }, null, true, @@ -123,7 +123,6 @@ const setupCalendarNotifications = async (client) => { () => { console.log('Running scheduled calendar notifications...'); scheduleCalendarNotifications(client); - console.log(activeCronJobs); }, null, true, diff --git a/src/events/change-status-event.js b/src/events/change-status-event.js deleted file mode 100644 index e3ea5d73..00000000 --- a/src/events/change-status-event.js +++ /dev/null @@ -1,59 +0,0 @@ -const { Events } = require('discord.js'); -const { MAPPED_STATUS_COMMANDS } = require('../config'); - -const setThreadNameWithStatus = async ({ channel, newStatus }) => { - const allEmojis = Object.values(MAPPED_STATUS_COMMANDS) - .flatMap((category) => Object.values(category)) - .join('|'); - - const emojisRegExp = new RegExp(`(${allEmojis})`, 'gu'); - - const channelName = channel.name.replace(emojisRegExp, '').trim(); - - const updatedChannelName = `${newStatus} ${channelName}`; - - await channel.setName(updatedChannelName); -}; - -const handleStringSelectMenuInteraction = async (interaction) => { - try { - if (!interaction.isStringSelectMenu()) { - return; - } - - const selectedValue = interaction.values[0]; - const { channel } = interaction; - - const newStatus = - Object.values(MAPPED_STATUS_COMMANDS) - .flatMap((category) => Object.entries(category)) - .find(([key]) => key === selectedValue)?.[1] || selectedValue; - - await setThreadNameWithStatus({ - channel, - newStatus, - }); - - await interaction.update({ - content: `βœ… Status changed to **${selectedValue}**!`, - components: [], - }); - } catch (error) { - console.error('Error handling select menu interaction:', error); - if (!interaction.replied && !interaction.deferred) { - await interaction.reply({ - content: '❌ There was an error while handling your selection!', - ephemeral: true, - }); - } - } -}; - -module.exports = { - name: Events.InteractionCreate, - async execute(_client, interaction) { - if (interaction.isStringSelectMenu()) { - await handleStringSelectMenuInteraction(interaction); - } - }, -}; diff --git a/src/events/eventList.js b/src/events/eventList.js index 33fb5bfb..c0764a47 100644 --- a/src/events/eventList.js +++ b/src/events/eventList.js @@ -4,7 +4,6 @@ const listenPullRequestOpenMessage = require('./listenPullRequestOpenMessage'); const pollVotationResultEvent = require('./pollVotationResults'); const qaMetionEvent = require('./qaMention'); const readyEvent = require('./ready'); -const changeStatusEvent = require('./change-status-event'); const eventList = [ guildScheduledEventCreate, @@ -12,7 +11,6 @@ const eventList = [ pollVotationResultEvent, qaMetionEvent, readyEvent, - changeStatusEvent, listenPullRequestOpenMessage, ]; module.exports = eventList; diff --git a/src/languages/translations/en-US.json b/src/languages/translations/en-US.json index ce111580..97630517 100644 --- a/src/languages/translations/en-US.json +++ b/src/languages/translations/en-US.json @@ -6,10 +6,7 @@ "invalidStatus": "Invalid status command.", "updatedStatus": "Status updated to {{status}}", "error": "An error occurred while updating the status.", - "messageOption": "Send a message to detail your changes", - "statusChanged": "Status changed to {{status}}", - "chooseYourStatus": "Choose your status", - "placeholder": "Make a selection" + "messageOption": "Send a message to detail your changes" }, "convertTime": { "description": "Convert time between time zones", diff --git a/src/languages/translations/es-ES.json b/src/languages/translations/es-ES.json index e720ab02..78eac5d1 100644 --- a/src/languages/translations/es-ES.json +++ b/src/languages/translations/es-ES.json @@ -6,10 +6,7 @@ "invalidStatus": "Estado no vΓ‘lido.", "updatedStatus": "Estado actualizado a {{status}}", "error": "OcurriΓ³ un error al actualizar el estado.", - "messageOption": "Envia un mensage para detallar tus cambios", - "statusChanged": "Estado cambiado a {{status}}", - "chooseYourStatus": "Elige tu estado", - "placeholder": "Haz una selecciΓ³n" + "messageOption": "Envia un mensage para detallar tus cambios" }, "convertTime": { "description": "Convertir hora entre zonas horarias", From 3a5caccfdca05f376912c0121db330d7277c54db Mon Sep 17 00:00:00 2001 From: Yoel Ferreyra Date: Thu, 3 Apr 2025 13:45:00 -0300 Subject: [PATCH 3/5] feat: update dateToCronExpression to accept event timezone --- src/utils/date-to-cron-expression.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/utils/date-to-cron-expression.js b/src/utils/date-to-cron-expression.js index e6d6084b..b70438b8 100644 --- a/src/utils/date-to-cron-expression.js +++ b/src/utils/date-to-cron-expression.js @@ -4,10 +4,11 @@ const moment = require('moment-timezone'); * Converts a date string into a CRON expression. * * @param {string} dateString - A string representing a date. + * @param {string} dateString - Event timezone. * @returns {string} A CRON expression derived from the provided date. * * @example - * dateToCronExpression('2022-01-22T15:30:00'); + * dateToCronExpression('2022-01-22T15:30:00', eventTimeZone); * // Returns '30 15 22 1 *' * * @example @@ -20,7 +21,7 @@ function dateToCronExpression(dateString, eventTimeZone = 'America/Bogota') { const minutes = date.minutes(); const hours = date.hours(); const day = date.date(); - const month = date.month() + 1; // Meses en moment van de 0-11, sumamos 1 + const month = date.month() + 1; return `${minutes} ${hours} ${day} ${month} *`; } From 335d12dd8341d968400b0177b520191885f85967 Mon Sep 17 00:00:00 2001 From: Yoel Ferreyra Date: Thu, 3 Apr 2025 13:49:41 -0300 Subject: [PATCH 4/5] fix: clean up code --- src/utils/date-to-cron-expression.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/utils/date-to-cron-expression.js b/src/utils/date-to-cron-expression.js index b70438b8..69567ef4 100644 --- a/src/utils/date-to-cron-expression.js +++ b/src/utils/date-to-cron-expression.js @@ -1,4 +1,5 @@ const moment = require('moment-timezone'); +const { SCHEDULE_MESSAGES } = require('../config'); /** * Converts a date string into a CRON expression. @@ -15,8 +16,11 @@ const moment = require('moment-timezone'); * dateToCronExpression('2022-01-01T00:00:00'); * // Returns '0 0 1 1 *' */ -function dateToCronExpression(dateString, eventTimeZone = 'America/Bogota') { - const date = moment.tz(dateString, eventTimeZone); +function dateToCronExpression(dateString, eventTimeZone) { + const timeZoneTarget = eventTimeZone + ? eventTimeZone + : SCHEDULE_MESSAGES.timeZone; + const date = moment.tz(dateString, timeZoneTarget); const minutes = date.minutes(); const hours = date.hours(); From c1f9a4337f12de4ba2ddf2541376ba235769dc69 Mon Sep 17 00:00:00 2001 From: Yoel Ferreyra Date: Thu, 3 Apr 2025 15:08:04 -0300 Subject: [PATCH 5/5] fix: schedules messages --- src/cron/schedule-google-calendar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cron/schedule-google-calendar.js b/src/cron/schedule-google-calendar.js index b32175a6..c02d0979 100644 --- a/src/cron/schedule-google-calendar.js +++ b/src/cron/schedule-google-calendar.js @@ -78,7 +78,7 @@ const scheduleEventNotification = async ({ client, event }) => { const job = new CronJob( cronExpression, async () => { - await sendCalendarEventNotification(); + await sendCalendarEventNotification(client, event); }, null, true,