diff --git a/.env.example b/.env.example index c40219a6..605f1fab 100644 --- a/.env.example +++ b/.env.example @@ -13,10 +13,6 @@ ADMINISTRATOR_TAG_ID=1014267063788908585 ADMIN_POINT_REQUEST_CHANNEL=1310614733803618416 PR_TEMPLATE_ALLOWED_CHANNELS="1270770663048613983,general;1309628262456098878,qa-request;" -# Assign task Forum -AVAILABLE_TAG_ID='1351647448019370105' # ID for available tag for tasks -ASSIGNED_TAG_ID='1351647194251268318' # ID for assigned tag for tasks - # Scheduled messages TIME_ZONE='America/Bogota' SCHEDULED_CALENDAR_INTERVAL='*/20 8-17 * * 1-5' diff --git a/docs/configuration/discord_webhook/integrations.png b/docs/configuration/discord_webhook/integrations.png deleted file mode 100644 index 75f7f053..00000000 Binary files a/docs/configuration/discord_webhook/integrations.png and /dev/null differ diff --git a/docs/configuration/discord_webhook/server_settings.png b/docs/configuration/discord_webhook/server_settings.png deleted file mode 100644 index bbe13d97..00000000 Binary files a/docs/configuration/discord_webhook/server_settings.png and /dev/null differ diff --git a/docs/configuration/discord_webhook/webhook_configs.png b/docs/configuration/discord_webhook/webhook_configs.png deleted file mode 100644 index a50fcdcb..00000000 Binary files a/docs/configuration/discord_webhook/webhook_configs.png and /dev/null differ diff --git a/docs/configuration/github_pat/classic_token.png b/docs/configuration/github_pat/classic_token.png deleted file mode 100644 index 94608b58..00000000 Binary files a/docs/configuration/github_pat/classic_token.png and /dev/null differ diff --git a/docs/configuration/github_pat/developer_settings.png b/docs/configuration/github_pat/developer_settings.png deleted file mode 100644 index 534bfbef..00000000 Binary files a/docs/configuration/github_pat/developer_settings.png and /dev/null differ diff --git a/docs/configuration/github_pat/pat_classic_form.png b/docs/configuration/github_pat/pat_classic_form.png deleted file mode 100644 index 5d8f43c0..00000000 Binary files a/docs/configuration/github_pat/pat_classic_form.png and /dev/null differ diff --git a/docs/configuration/github_pat/profile_settings.png b/docs/configuration/github_pat/profile_settings.png deleted file mode 100644 index 78fadc00..00000000 Binary files a/docs/configuration/github_pat/profile_settings.png and /dev/null differ diff --git a/docs/configuration/github_webhook/add_webhook_button.png b/docs/configuration/github_webhook/add_webhook_button.png deleted file mode 100644 index 606e0729..00000000 Binary files a/docs/configuration/github_webhook/add_webhook_button.png and /dev/null differ diff --git a/docs/configuration/github_webhook/config_payload_url.png b/docs/configuration/github_webhook/config_payload_url.png deleted file mode 100644 index 087bf211..00000000 Binary files a/docs/configuration/github_webhook/config_payload_url.png and /dev/null differ diff --git a/docs/configuration/github_webhook/payload_url.png b/docs/configuration/github_webhook/payload_url.png deleted file mode 100644 index 854c8305..00000000 Binary files a/docs/configuration/github_webhook/payload_url.png and /dev/null differ diff --git a/docs/configuration/github_webhook/repository_settings.png b/docs/configuration/github_webhook/repository_settings.png deleted file mode 100644 index 1b04148f..00000000 Binary files a/docs/configuration/github_webhook/repository_settings.png and /dev/null differ diff --git a/docs/configuration/github_webhook/webhook_tab.png b/docs/configuration/github_webhook/webhook_tab.png deleted file mode 100644 index f1ed9709..00000000 Binary files a/docs/configuration/github_webhook/webhook_tab.png and /dev/null differ diff --git a/docs/github-webhook.md b/docs/github-webhook.md deleted file mode 100644 index 9fc3dacf..00000000 --- a/docs/github-webhook.md +++ /dev/null @@ -1,113 +0,0 @@ -# Github webhook Setup for Discord Bot - -This documentation details the process of setting up an integration between Discord and GitHub, enabling seamless communication and automation of key notifications directly within your Discord channels. By connecting your GitHub repository with Discord, you'll be able to receive instant alerts about opened `Pull requests`. This integration facilitates real-time collaboration and keeps your team informed about repository changes, improving efficiency and responsiveness to project updates. The integration needs some configs like generate `discord webhook`, setup a `github webhook` in a repository and create `Personal Access Token` in github profile (PAT) - -## Configuration Keys - -The following keys, filled it with the information of your configuration: - -``` - GITHUB_PR_REVIEW_CHANNEL = DISCORD_CHANNEL_REVIEW - GITHUB_ACCESS_TOKEN = YOUR_GITHUB_ACCESS_TOKEN - GITHUB_PR_PUBLISH_ENABLED = ENABLED_MESSAGES_SENDING -``` - -- **GITHUB_PR_REVIEW_CHANNEL**: Discord channel where the bot will send PR review messages. -- **GITHUB_ACCESS_TOKEN**: Your access token for authentication (PAT). -- **GITHUB_PR_PUBLISH_ENABLED**: Controls whether the bot will publish messages related to newly opened Pull Requests (PRs) on GitHub. - -> [!NOTE] -> if you want detailed information about this integration process you could check this [doc](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) - -- [Configuration Keys](#configuration-keys) -- [Steps to Enable Integration Github Webhook](#steps-to-enable-integration-github-webhook) - - [1. Generate discord webhook](#1-generate-discord-webhook) - - [2. Setup Github webhook](#2-setup-github-webhook) - - [3. Setup Github PAT](#3-setup-github-pat) - - [4. Setup Discord Bot with collected Information](#4-setup-discord-bot-with-collected-information) - -## Steps to Enable Integration Github Webhook - -### 1. Generate discord webhook - -To enable Discord's Webhook, you need to go to the integrations section in your server settings and obtain Webhook URL where github will send messages about PRs. - -#### Steps: - -1. Open your Server Settings and head into the Integrations tab - ![server settings](./configuration/discord_webhook/server_settings.png) - -2. Click the "Create Webhook" button to create a new webhook!. - ![integration settings](./configuration/discord_webhook/integrations.png) - -3. Click on "New Webhook" then, there will be a few options to config your webhook like avatar, name and Channel - ![webhook configs](./configuration/discord_webhook/webhook_configs.png) - -4. Now the URL for your webhook is available to use on the app to receive messages from. - -### 2. Setup Github webhook - -Once Webhook URL from discord is available you could start with Github settings in the github repository. For more information you could check this [doc](https://docs.github.com/en/webhooks/using-webhooks/creating-webhooks#creating-a-repository-webhook) - -#### Steps: - -1. On GitHub, navigate to the main page of the repository that will be integrated with webhook. Under your repository name, click Settings. If you cannot see the `Settings` tab, select the dropdown menu, then click Settings. - ![repository settings](./configuration/github_webhook/repository_settings.png) - -2. Click the `Webhooks` tab in the left sidebar then `add webhook` to start creating new webhook - ![new webhook](./configuration/github_webhook/webhook_tab.png) - -3. In the section under `Payload URL`, type the URL where you'd like to receive payloads (the URL obtained in previous step). - -![payload url](./configuration/github_webhook/payload_url.png) - -> [!IMPORTANT] -> To make the webhook display messages properly, it's really important that you append `/github` at the end of it. For example: https://discord.com/api/webhooks/123572432467723/abcdef_zdfwewkqlwd--Kto-KB-Kb4kxsFj-BV - -4. Optionally, select the Content type drop-down menu, and click a data format to receive the webhook payload in. For this case `application/json` is needed. - -![config payload url](./configuration//github_webhook/config_payload_url.png) - -5. Under "Which events would you like to trigger this webhook?", select the webhook events that you want to receive. You should only subscribe to the webhook events that you need in your app. In this case the `Let me select individual events.` option will be enabled to select only the 'Pull request' events. - -6. To make the webhook active immediately after adding the configuration, select Active and Click `Add webhook` button. - -![add webhook](./configuration/github_webhook/add_webhook_button.png) - -### 3. Setup Github PAT - -Personal access tokens are an alternative to using passwords for authentication to GitHub when using the GitHub API or the command line. Personal access tokens are intended to access GitHub resources on behalf of yourself. - -> [!NOTE] -> if you want detailed information about this integration process you could check this [documentation](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic) - -1. In the upper-right corner of any page on GitHub, click your profile photo, then click Settings. - ![profile settings](./configuration/github_pat/profile_settings.png) - -2. In the left sidebar, click Developer settings. - ![developer settings](./configuration/github_pat/developer_settings.png) - -3. In the left sidebar, under PAT tab, click Tokens (classic). Then click `Generate new token` (classic). - ![classic token](./configuration/github_pat/classic_token.png) - -4. In the "Note" field, give your token a descriptive name. - -5. To give your token an expiration, select Expiration, then choose a default option or click Custom to enter a date. - -6. Select the scopes you'd like to grant this token. To use your token to access repositories from the command line, select repo. A token with no assigned scopes can only access public information. For more information, see Scopes for OAuth apps. - -![pat_classic_form](./configuration/github_pat/pat_classic_form.png) - -7. Click Generate token. So the token is available for your app now! - -### 4. Finalize Discord Bot Configuration - -Once the different configurations have been made to connect discord with the github webhook, some of the data obtained is taken to finish with the configuration and integration of the bot. - -#### Steps: - -1. Configure the PAT obtained from the github profile in the environment variable `GITHUB_ACCESS_TOKEN` - -2. The channel that will receive the open PR alert messages is configured by means of the environment variable `GITHUB_PR_REVIEW_CHANNEL` - -3. Set the environment variable `GITHUB_PR_PUBLISH_ENABLED` to `true` to enable the publication of PR messages on Discord. diff --git a/src/commands/commandList.js b/src/commands/commandList.js index 4aec6f50..bc1af4e3 100644 --- a/src/commands/commandList.js +++ b/src/commands/commandList.js @@ -13,7 +13,6 @@ const serverCommand = require('./utility/server'); const userCommand = require('./utility/user'); const votePointsCommand = require('./utility/vote-points'); const assingPointCommand = require('./utility/assignPoint'); -const assignTaskForum = require('./utility/assignTaskForum'); const commandList = [ pingCommand, @@ -31,6 +30,5 @@ const commandList = [ userCommand, votePointsCommand, assingPointCommand, - assignTaskForum, ]; module.exports = commandList; diff --git a/src/commands/utility/assignTaskForum.js b/src/commands/utility/assignTaskForum.js deleted file mode 100644 index 1bd2ed4f..00000000 --- a/src/commands/utility/assignTaskForum.js +++ /dev/null @@ -1,95 +0,0 @@ -const { SlashCommandBuilder } = require('discord.js'); -const { ASSIGN_TASK_FORUM } = require('../../config'); -const { translateLanguage, keyTranslations } = require('../../languages'); -const { sendErrorToChannel } = require('../../utils/send-error'); - -const CHANNEL_TYPES = { - FORUM: 15, -}; - -async function replyWithError(interaction, translationKey) { - return await interaction.editReply({ - content: translateLanguage(translationKey), - ephemeral: true, - }); -} - -function getUserTagForAssignment(forum, assignedUser, channel) { - if (CHANNEL_TYPES.FORUM !== forum.type) { - return { reply: 'assignTaskForum.isNotForumThread' }; - } - - const forumAvailableTag = forum.availableTags; - const userTag = forumAvailableTag.find( - (tag) => tag.name.toLowerCase() === assignedUser - ); - - if (!userTag) { - return { reply: 'assignTaskForum.userTagNotAvailable' }; - } - - if (channel.appliedTags.includes(userTag.id)) { - return { reply: 'assignTaskForum.userAssignedTask' }; - } - - return { userTag }; -} - -async function handleError(interaction, error) { - console.error(error); - console.log({ error }); - await sendErrorToChannel(interaction, error); - await interaction.editReply(translateLanguage('assignTaskForum.error')); -} - -module.exports = { - data: new SlashCommandBuilder() - .setName('assign') - .setDescription(translateLanguage('assignTaskForum.description')) - .setDescriptionLocalizations(keyTranslations('assignTaskForum.description')) - .addUserOption((option) => - option - .setName('user') - .setDescription(translateLanguage('assignTaskForum.userOption')) - .setDescriptionLocalizations( - keyTranslations('assignTaskForum.userOption') - ) - .setRequired(false) - ), - async execute(interaction) { - try { - await interaction.deferReply(); - const { channel, options, user } = interaction; - - if (!channel.isThread()) { - return replyWithError(interaction, 'changeStatus.notAThread'); - } - - const forum = channel.parent; - const assignedUser = ( - options.getUser('user')?.username || user.username - ).toLowerCase(); - const { userTag, reply } = getUserTagForAssignment( - forum, - assignedUser, - channel - ); - - if (reply) { - return replyWithError(interaction, reply); - } - - await channel.setAppliedTags([undefined, userTag.id]); - - await interaction.editReply({ - content: translateLanguage('assignTaskForum.taskAssignedSuccessful'), - }); - } catch (error) { - await handleError(interaction, { - ...error, - stack: error.stack, - message: `${error.message.slice(0, 100)}... ASSIGN_TASK_FORUM: ${JSON.stringify(ASSIGN_TASK_FORUM)}`, - }); - } - }, -}; diff --git a/src/commands/utility/changeStatus.js b/src/commands/utility/changeStatus.js index 25fe7ae9..279e78ca 100644 --- a/src/commands/utility/changeStatus.js +++ b/src/commands/utility/changeStatus.js @@ -18,6 +18,7 @@ const createStatusMenu = (channel) => { Object.keys(MAPPED_STATUS_COMMANDS[selectedChannel]).map((status) => { return new StringSelectMenuOptionBuilder() .setLabel(status) + .setDescription(status) .setValue(status); }) ); @@ -27,28 +28,11 @@ module.exports = { data: new SlashCommandBuilder() .setName('change-status') .setDescription(translateLanguage('changeStatus.description')) - .setDescriptionLocalizations(keyTranslations('changeStatus.description')) - .addStringOption((option) => - option - .setName('message') - .setDescription(translateLanguage('changeStatus.messageOption')) - .setDescriptionLocalizations( - keyTranslations('changeStatus.messageOption') - ) - .setRequired(false) - ), + .setDescriptionLocalizations(keyTranslations('changeStatus.description')), async execute(interaction) { try { - const { channel, user } = interaction; + const { channel } = interaction; const selectMenu = createStatusMenu(channel.name); - const message = interaction.options.getString('message'); - - if (!interaction.channel.isThread()) { - return await interaction.editReply({ - content: translateLanguage('changeStatus.notAThread'), - ephemeral: true, - }); - } const row = new ActionRowBuilder().addComponents(selectMenu); @@ -56,19 +40,6 @@ module.exports = { content: translateLanguage('changeStatus.selectStatus'), components: [row], }); - - if (message) { - const markdownMessage = - `# ${translateLanguage('changeStatus.markdownMessage')}\n\n` + - `${message}\n\n` + - `> ${user}`; - - await channel.send(markdownMessage); - } - - await interaction.followUp({ - content: translateLanguage('changeStatus.statusUpdatedSuccesfully'), - }); } catch (error) { console.error(error); await sendErrorToChannel(interaction, error); diff --git a/src/config.js b/src/config.js index e31631d1..4f998584 100644 --- a/src/config.js +++ b/src/config.js @@ -179,13 +179,6 @@ const GITHUB_PUBLISH_OPENED_PR = { githubPRPublishEnabled: process.env.GITHUB_PR_PUBLISH_ENABLED === 'true', }; -const ASSIGN_TASK_FORUM = { - tags: { - availableTagId: process.env.AVAILABLE_TAG_ID, - assignedTagId: process.env.ASSIGNED_TAG_ID, - }, -}; - module.exports = { LISTEN_NEW_EVENTS, DISCORD_SERVER, @@ -202,5 +195,4 @@ module.exports = { FIREBASE_CONFIG, ADMIN_ROLE_ID, GITHUB_PUBLISH_OPENED_PR, - ASSIGN_TASK_FORUM, }; 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, diff --git a/src/languages/translations/en-US.json b/src/languages/translations/en-US.json index ed4eec3a..ce111580 100644 --- a/src/languages/translations/en-US.json +++ b/src/languages/translations/en-US.json @@ -8,10 +8,8 @@ "error": "An error occurred while updating the status.", "messageOption": "Send a message to detail your changes", "statusChanged": "Status changed to {{status}}", - "selectStatus": "Choose your status", - "placeholder": "Make a selection", - "markdownMessage": "Channel status changed", - "statusUpdatedSuccesfully": "Status updated successfully!" + "chooseYourStatus": "Choose your status", + "placeholder": "Make a selection" }, "convertTime": { "description": "Convert time between time zones", @@ -227,14 +225,5 @@ "pullRequestOpen": { "errorExtractDataFromURL": "Error extracting repository or PR number from URL.", "errorFetchPR": "Error trying to fetch PR data to {{prURL}}. URL: {{prURL}}. status: {{status}}" - }, - "assignTaskForum": { - "description": "Assign the user tag to a task posted on the forum.", - "userOption": "User to assign", - "isNotForumThread": "This thread is not from a forum for assigning tasks.", - "userTagNotAvailable": "This user is not available in tags.", - "userAssignedTask": "This user is already assigned in this task", - "taskAssignedSuccessful": "This task was assigned successfully!", - "error": "An error occurred while assigning the task." } } diff --git a/src/languages/translations/es-ES.json b/src/languages/translations/es-ES.json index 95ea0878..e720ab02 100644 --- a/src/languages/translations/es-ES.json +++ b/src/languages/translations/es-ES.json @@ -8,10 +8,8 @@ "error": "Ocurrió un error al actualizar el estado.", "messageOption": "Envia un mensage para detallar tus cambios", "statusChanged": "Estado cambiado a {{status}}", - "selectStatus": "Elige tu estado", - "placeholder": "Haz una selección", - "markdownMessage": "Estado del canal cambiado", - "statusUpdatedSuccesfully": "Estado actualizado exitosamente!" + "chooseYourStatus": "Elige tu estado", + "placeholder": "Haz una selección" }, "convertTime": { "description": "Convertir hora entre zonas horarias", @@ -228,14 +226,5 @@ "pullRequestOpen": { "errorExtractDataFromURL": "Error al extraer el nombre del repositorio o el número de PR de la URL.", "errorFetchPR": "Error al intentar obtener datos del PR. URL: {{prURL}}. Estado: {{status}}" - }, - "assignTaskForum": { - "description": "Asigna la etiqueta de usuario a una tarea publicada en el foro.", - "userOption": "Usuario para asignar", - "isNotForumThread": "Este hilo no es de un foro para asignar tareas.", - "userTagNotAvailable": "Este usuario no está disponible en las etiquetas.", - "userAssignedTask": "Este usuario ya está asignado en esta tarea", - "taskAssignedSuccessful": "¡Esta tarea se asignó exitosamente!", - "error": "Ocurrió un error al asignar la tarea." } } diff --git a/src/utils/date-to-cron-expression.js b/src/utils/date-to-cron-expression.js index 6c42b87e..69567ef4 100644 --- a/src/utils/date-to-cron-expression.js +++ b/src/utils/date-to-cron-expression.js @@ -1,23 +1,32 @@ +const moment = require('moment-timezone'); +const { SCHEDULE_MESSAGES } = require('../config'); + /** * 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 * 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) { + const timeZoneTarget = eventTimeZone + ? eventTimeZone + : SCHEDULE_MESSAGES.timeZone; + const date = moment.tz(dateString, timeZoneTarget); + + const minutes = date.minutes(); + const hours = date.hours(); + const day = date.date(); + const month = date.month() + 1; + return `${minutes} ${hours} ${day} ${month} *`; }