From 85c2fdd8fb7b384e490a1ac705247d1f4f9bf727 Mon Sep 17 00:00:00 2001 From: Erik Zuuring Date: Thu, 14 Mar 2024 13:52:49 +0200 Subject: [PATCH 1/4] Command fix ### Notes The `/create-invite` command was failing to appear correctly on the prod version of valkyrie. From local testing it seems like this could be the case of `commands.set` vs `commands.create` --- discord-scripts/invite-management.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/discord-scripts/invite-management.ts b/discord-scripts/invite-management.ts index 4cbbe4a2..0354bdf6 100644 --- a/discord-scripts/invite-management.ts +++ b/discord-scripts/invite-management.ts @@ -32,12 +32,10 @@ export default async function sendInvite(discordClient: Client, robot: Robot) { ) if (existingInviteCommand === undefined) { robot.logger.info("No create-invite command found, creating it!") - await application.commands.set([ - { - name: "create-invite", - description: "Creates a new invite", - }, - ]) + await application.commands.create({ + name: "create-invite", + description: "Creates a new invite", + }) robot.logger.info("create invite command set") } // Create an invite based of the command and channel where the command has been run From 28acebcc80b263be351394bb69afe46f22086f35 Mon Sep 17 00:00:00 2001 From: Erik Zuuring Date: Thu, 14 Mar 2024 14:22:19 +0200 Subject: [PATCH 2/4] Delete fjord.ts Remove `fjord.ts` since the n8n commands have not been implemented and it seems to be causing an issue with setting new commands. --- discord-scripts/fjord.ts | 365 --------------------------------------- 1 file changed, 365 deletions(-) delete mode 100644 discord-scripts/fjord.ts diff --git a/discord-scripts/fjord.ts b/discord-scripts/fjord.ts deleted file mode 100644 index 79469ef4..00000000 --- a/discord-scripts/fjord.ts +++ /dev/null @@ -1,365 +0,0 @@ -import { - ApplicationCommandOptionType, - Client, - CommandInteraction, - TextChannel, -} from "discord.js" -import axios from "axios" -import { Robot } from "hubot" - -// This is the WIP discord implementation of commands to trigger certain workflows on the thesis n8n platform. Most of the integration uses webhooks and chat commands with response headers . -export default async function manageFjord(discordClient: Client, robot: Robot) { - const { application } = discordClient - - if (application) { - const existingFjordCommand = (await application.commands.fetch()).find( - (command) => command.name === "issues", - ) - if (existingFjordCommand === undefined) { - application.commands.set([ - { - name: "debug", - description: - "Runs the debug command, sending logs to Valkyrie console", - }, - { - name: "stale-issues", - description: "Retrieve stale issues from specific git repository", - options: [ - { - name: "repository-owner", - type: ApplicationCommandOptionType.String, - description: "The owner of the repository", - required: true, - }, - { - name: "repository-name", - type: ApplicationCommandOptionType.String, - description: "The name of the repository", - required: true, - }, - ], - }, - { - name: "issues", - description: "Retrieve recent issues from specific git repository", - options: [ - { - name: "repository-owner", - type: ApplicationCommandOptionType.String, - description: "The owner of the repository", - required: true, - }, - { - name: "repository-name", - type: ApplicationCommandOptionType.String, - description: "The name of the repository", - required: true, - }, - ], - }, - { - name: "activity", - description: "Retrieve activity summary from specific git repository", - options: [ - { - name: "repository-owner", - type: ApplicationCommandOptionType.String, - description: "The owner of the repository", - required: true, - }, - { - name: "repository-name", - type: ApplicationCommandOptionType.String, - description: "The name of the repository", - required: true, - }, - ], - }, - { - name: "n8n", - description: "Run specific workflow from n8n", - options: [ - { - name: "workflow-name", - type: ApplicationCommandOptionType.String, - description: "The name of the workflow to run", - required: true, - }, - ], - }, - ]) - } - } - const startLoadingBar = async ( - interaction: CommandInteraction, - repositoryOwner: string, - repositoryName: string, - ) => { - const loadingMessage = await interaction.reply({ - content: `**Workflow started: ${repositoryOwner} / ${repositoryName}**\n\n:hourglass: In progress...`, - ephemeral: true, - }) - - const loadingBarLength = 10 - let progress = 0 - - const loadingBarInterval = setInterval(() => { - progress += 1 - const progressBar = - ":green_square:".repeat(progress) + - ":white_large_square:".repeat(loadingBarLength - progress) - loadingMessage.edit( - `**Workflow started: ${repositoryOwner} / ${repositoryName}**\n\n:hourglass: In progress...\n\n${progressBar}`, - ) - - if (progress === loadingBarLength) { - clearInterval(loadingBarInterval) - } - }, 1000) - return loadingMessage - } - - if (process.env.HUBOT_N8N_WEBHOOK) { - discordClient.on("interactionCreate", async (interaction) => { - if ( - (interaction.isButton() && interaction.customId.startsWith("debug")) || - (interaction.isCommand() && interaction.commandName === "debug") - ) { - robot.logger.info("adapter in use:", robot.adapter) - - await interaction.reply({ - content: "**Debugger running, check your console ;)**", - ephemeral: true, - components: [ - { - type: 1, - components: [ - { - type: 2, - label: "Run again", - style: 1, - custom_id: "debug", - }, - ], - }, - ], - }) - } - - if ( - interaction.isCommand() && - interaction.commandName === "stale-issues" - ) { - const { channelId } = interaction - const channel = discordClient.channels.cache.get(channelId) - const repositoryOwnerOption = - interaction.options.get("repository-owner") - const repositoryNameOption = interaction.options.get("repository-name") - if ( - repositoryOwnerOption && - typeof repositoryOwnerOption.value === "string" && - repositoryNameOption && - typeof repositoryNameOption.value === "string" - ) { - const repositoryOwner = repositoryOwnerOption.value - const repositoryName = repositoryNameOption.value - - const webhookUrl = process.env.HUBOT_N8N_WEBHOOK - const queryParams = new URLSearchParams({ - repositoryOwner, - repositoryName, - }) - - const loadingMessage = await startLoadingBar( - interaction, - repositoryOwner, - repositoryName, - ) - - const options = { - headers: { - workflowType: "stale-issues", - }, - } - axios - .get(`${webhookUrl}?${queryParams.toString()}`, options) - .then(async (response) => { - await loadingMessage.edit( - `**Workflow: ${repositoryOwner} / ${repositoryName}**\n\n:white_check_mark: Workflow completed!`, - ) - await loadingMessage.delete() - if (channel instanceof TextChannel) { - const thread = await channel.threads.create({ - name: `Stale issues: ${repositoryOwner} ${repositoryName}`, - autoArchiveDuration: 60, - }) - await thread.send(response.data) - } - }) - .catch((error) => { - interaction.followUp(`workflow failed: ${error.message}`) - }) - } - } - - if (interaction.isCommand() && interaction.commandName === "issues") { - const { channelId } = interaction - const channel = discordClient.channels.cache.get(channelId) - const repositoryOwnerOption = - interaction.options.get("repository-owner") - const repositoryNameOption = interaction.options.get("repository-name") - if ( - repositoryOwnerOption && - typeof repositoryOwnerOption.value === "string" && - repositoryNameOption && - typeof repositoryNameOption.value === "string" - ) { - const repositoryOwner = repositoryOwnerOption.value - const repositoryName = repositoryNameOption.value - - const webhookUrl = process.env.HUBOT_N8N_WEBHOOK - const queryParams = new URLSearchParams({ - repositoryOwner, - repositoryName, - }) - - const loadingMessage = await startLoadingBar( - interaction, - repositoryOwner, - repositoryName, - ) - - const options = { - headers: { - workflowType: "issues", - }, - } - axios - .get(`${webhookUrl}?${queryParams.toString()}`, options) - .then(async (response) => { - await loadingMessage.edit( - `**Workflow: ${repositoryOwner} / ${repositoryName}**\n\n:white_check_mark: Workflow completed!`, - ) - await loadingMessage.delete() - - if (channel instanceof TextChannel) { - const thread = await channel.threads.create({ - name: `Issues: ${repositoryOwner} ${repositoryName}`, - autoArchiveDuration: 60, - }) - await thread.send(response.data) - } - }) - .catch((error) => { - interaction.followUp(`workflow failed: ${error.message}`) - }) - } - } - - if (interaction.isCommand() && interaction.commandName === "activity") { - const { channelId } = interaction - const channel = discordClient.channels.cache.get(channelId) - const repositoryOwnerOption = - interaction.options.get("repository-owner") - const repositoryNameOption = interaction.options.get("repository-name") - if ( - repositoryOwnerOption && - typeof repositoryOwnerOption.value === "string" && - repositoryNameOption && - typeof repositoryNameOption.value === "string" - ) { - const repositoryOwner = repositoryOwnerOption.value - const repositoryName = repositoryNameOption.value - - const webhookUrl = process.env.HUBOT_N8N_WEBHOOK - const queryParams = new URLSearchParams({ - repositoryOwner, - repositoryName, - }) - - const loadingMessage = await startLoadingBar( - interaction, - repositoryOwner, - repositoryName, - ) - - const options = { - headers: { - workflowType: "activity", - }, - } - axios - .get(`${webhookUrl}?${queryParams.toString()}`, options) - .then(async (response) => { - await loadingMessage.edit( - `**Workflow: ${repositoryOwner} / ${repositoryName}**\n\n:white_check_mark: Workflow completed!`, - ) - await loadingMessage.delete() - - if (channel instanceof TextChannel) { - const thread = await channel.threads.create({ - name: `Git Activity: ${repositoryOwner} ${repositoryName}`, - autoArchiveDuration: 60, - }) - await thread.send("@here") - await thread.send(response.data) - } - }) - .catch((error) => { - interaction.followUp(`Workflow failed: ${error.message}`) - }) - } - } - - if (interaction.isCommand() && interaction.commandName === "n8n") { - const { channelId } = interaction - const channel = discordClient.channels.cache.get(channelId) - const workflowNameOption = interaction.options.get("workflow-name") - if ( - workflowNameOption && - typeof workflowNameOption.value === "string" - ) { - const workflowName = workflowNameOption.value - - const webhookUrl = process.env.HUBOT_N8N_WEBHOOK - const queryParams = new URLSearchParams({ - workflowName, - }) - - const loadingMessage = await startLoadingBar( - interaction, - workflowName, - "running", - ) - - const options = { - headers: { - workflowType: "exec", - }, - } - axios - .get(`${webhookUrl}?${queryParams.toString()}`, options) - .then(async (response) => { - await loadingMessage.edit( - `**Workflow: ${workflowName}**\n\n:white_check_mark: Workflow completed!`, - ) - await loadingMessage.delete() - - if (channel instanceof TextChannel) { - const thread = await channel.threads.create({ - name: `Exec: ${workflowName}`, - autoArchiveDuration: 60, - }) - await thread.send("@here") - await thread.send(response.data) - } - }) - .catch((error) => { - interaction.followUp(`Workflow failed: ${error.message}`) - }) - } - } - }) - } -} From d5897f415a7009c96321597307916b5c57af6572 Mon Sep 17 00:00:00 2001 From: Erik Zuuring Date: Thu, 14 Mar 2024 16:32:00 +0200 Subject: [PATCH 3/4] Add command deletion This checks if the `n8n` command exists and if so, removes all commands. Let's remove this after merging since it only has to run once to delete existing commands. --- discord-scripts/invite-management.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/discord-scripts/invite-management.ts b/discord-scripts/invite-management.ts index 0354bdf6..b33658da 100644 --- a/discord-scripts/invite-management.ts +++ b/discord-scripts/invite-management.ts @@ -26,6 +26,18 @@ export default async function sendInvite(discordClient: Client, robot: Robot) { const { application } = discordClient if (application) { + + // Remove after using, just to clean all commands if n8n is there + const existingCommands = await application.commands.fetch() + const n8nCommandExists = existingCommands.some(cmd => cmd.name === 'n8n') + + if (n8nCommandExists) { + await Promise.all(existingCommands.map(cmd => application.commands.delete(cmd.id))) + robot.logger.info("All old commands deleted because 'n8n' command was found") + } else { + robot.logger.info("No 'n8n' command found, no commands were deleted") + } + // Check if create-invite command already exists, if not create it const existingInviteCommand = (await application.commands.fetch()).find( (command) => command.name === "create-invite", From 1479bd3149dcb094536d3e01ccf3bcf98b54c329 Mon Sep 17 00:00:00 2001 From: Erik Zuuring Date: Thu, 14 Mar 2024 16:33:22 +0200 Subject: [PATCH 4/4] prettier fixes Prettier fixes --- discord-scripts/invite-management.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/discord-scripts/invite-management.ts b/discord-scripts/invite-management.ts index b33658da..a89f802f 100644 --- a/discord-scripts/invite-management.ts +++ b/discord-scripts/invite-management.ts @@ -26,16 +26,19 @@ export default async function sendInvite(discordClient: Client, robot: Robot) { const { application } = discordClient if (application) { - // Remove after using, just to clean all commands if n8n is there const existingCommands = await application.commands.fetch() - const n8nCommandExists = existingCommands.some(cmd => cmd.name === 'n8n') + const n8nCommandExists = existingCommands.some((cmd) => cmd.name === "n8n") if (n8nCommandExists) { - await Promise.all(existingCommands.map(cmd => application.commands.delete(cmd.id))) - robot.logger.info("All old commands deleted because 'n8n' command was found") + await Promise.all( + existingCommands.map((cmd) => application.commands.delete(cmd.id)), + ) + robot.logger.info( + "All old commands deleted because 'n8n' command was found", + ) } else { - robot.logger.info("No 'n8n' command found, no commands were deleted") + robot.logger.info("No n8n command found, no commands were deleted") } // Check if create-invite command already exists, if not create it