From 00dfad3279bd2a45a8331e734b331f4ab3fce75c Mon Sep 17 00:00:00 2001 From: Michael Kret <88898367+michael-radency@users.noreply.github.com> Date: Thu, 25 Apr 2024 11:41:53 +0300 Subject: [PATCH] fix(Discord Node): When using OAuth2 authentication, check if user is a guild member when sending direct message (#9183) --- .../channel/deleteChannel.operation.ts | 2 +- .../v2/actions/channel/get.operation.ts | 2 +- .../v2/actions/channel/update.operation.ts | 2 +- .../message/deleteMessage.operation.ts | 2 +- .../v2/actions/message/get.operation.ts | 2 +- .../v2/actions/message/getAll.operation.ts | 2 +- .../v2/actions/message/react.operation.ts | 2 +- .../v2/actions/message/send.operation.ts | 40 ++++++++++++++++--- .../nodes/Discord/v2/helpers/utils.ts | 6 +-- .../nodes/Discord/v2/methods/listSearch.ts | 2 +- .../nodes/Discord/v2/transport/discord.api.ts | 4 +- 11 files changed, 48 insertions(+), 18 deletions(-) diff --git a/packages/nodes-base/nodes/Discord/v2/actions/channel/deleteChannel.operation.ts b/packages/nodes-base/nodes/Discord/v2/actions/channel/deleteChannel.operation.ts index 14a601938ecc7..57c405655f10f 100644 --- a/packages/nodes-base/nodes/Discord/v2/actions/channel/deleteChannel.operation.ts +++ b/packages/nodes-base/nodes/Discord/v2/actions/channel/deleteChannel.operation.ts @@ -25,7 +25,7 @@ export const description = updateDisplayOptions(displayOptions, properties); export async function execute( this: IExecuteFunctions, - guildId: string, + _guildId: string, userGuilds: IDataObject[], ): Promise { const returnData: INodeExecutionData[] = []; diff --git a/packages/nodes-base/nodes/Discord/v2/actions/channel/get.operation.ts b/packages/nodes-base/nodes/Discord/v2/actions/channel/get.operation.ts index 5d0a09d4bd948..585b5294d405a 100644 --- a/packages/nodes-base/nodes/Discord/v2/actions/channel/get.operation.ts +++ b/packages/nodes-base/nodes/Discord/v2/actions/channel/get.operation.ts @@ -25,7 +25,7 @@ export const description = updateDisplayOptions(displayOptions, properties); export async function execute( this: IExecuteFunctions, - guildId: string, + _guildId: string, userGuilds: IDataObject[], ): Promise { const returnData: INodeExecutionData[] = []; diff --git a/packages/nodes-base/nodes/Discord/v2/actions/channel/update.operation.ts b/packages/nodes-base/nodes/Discord/v2/actions/channel/update.operation.ts index 670a875ae13aa..0466bd20c3947 100644 --- a/packages/nodes-base/nodes/Discord/v2/actions/channel/update.operation.ts +++ b/packages/nodes-base/nodes/Discord/v2/actions/channel/update.operation.ts @@ -104,7 +104,7 @@ export const description = updateDisplayOptions(displayOptions, properties); export async function execute( this: IExecuteFunctions, - guildId: string, + _guildId: string, userGuilds: IDataObject[], ): Promise { const returnData: INodeExecutionData[] = []; diff --git a/packages/nodes-base/nodes/Discord/v2/actions/message/deleteMessage.operation.ts b/packages/nodes-base/nodes/Discord/v2/actions/message/deleteMessage.operation.ts index 2d605ad08650e..149d9fc9e90ce 100644 --- a/packages/nodes-base/nodes/Discord/v2/actions/message/deleteMessage.operation.ts +++ b/packages/nodes-base/nodes/Discord/v2/actions/message/deleteMessage.operation.ts @@ -25,7 +25,7 @@ export const description = updateDisplayOptions(displayOptions, properties); export async function execute( this: IExecuteFunctions, - guildId: string, + _guildId: string, userGuilds: IDataObject[], ): Promise { const returnData: INodeExecutionData[] = []; diff --git a/packages/nodes-base/nodes/Discord/v2/actions/message/get.operation.ts b/packages/nodes-base/nodes/Discord/v2/actions/message/get.operation.ts index b9366088fa645..e7bb9f6a1871e 100644 --- a/packages/nodes-base/nodes/Discord/v2/actions/message/get.operation.ts +++ b/packages/nodes-base/nodes/Discord/v2/actions/message/get.operation.ts @@ -41,7 +41,7 @@ export const description = updateDisplayOptions(displayOptions, properties); export async function execute( this: IExecuteFunctions, - guildId: string, + _guildId: string, userGuilds: IDataObject[], ): Promise { const returnData: INodeExecutionData[] = []; diff --git a/packages/nodes-base/nodes/Discord/v2/actions/message/getAll.operation.ts b/packages/nodes-base/nodes/Discord/v2/actions/message/getAll.operation.ts index 0378bfc82b83c..957dcf210407f 100644 --- a/packages/nodes-base/nodes/Discord/v2/actions/message/getAll.operation.ts +++ b/packages/nodes-base/nodes/Discord/v2/actions/message/getAll.operation.ts @@ -42,7 +42,7 @@ export const description = updateDisplayOptions(displayOptions, properties); export async function execute( this: IExecuteFunctions, - guildId: string, + _guildId: string, userGuilds: IDataObject[], ): Promise { const returnData: INodeExecutionData[] = []; diff --git a/packages/nodes-base/nodes/Discord/v2/actions/message/react.operation.ts b/packages/nodes-base/nodes/Discord/v2/actions/message/react.operation.ts index a12acad581002..1041c5626a0e6 100644 --- a/packages/nodes-base/nodes/Discord/v2/actions/message/react.operation.ts +++ b/packages/nodes-base/nodes/Discord/v2/actions/message/react.operation.ts @@ -36,7 +36,7 @@ export const description = updateDisplayOptions(displayOptions, properties); export async function execute( this: IExecuteFunctions, - guildId: string, + _guildId: string, userGuilds: IDataObject[], ): Promise { const returnData: INodeExecutionData[] = []; diff --git a/packages/nodes-base/nodes/Discord/v2/actions/message/send.operation.ts b/packages/nodes-base/nodes/Discord/v2/actions/message/send.operation.ts index eb207c53b1ff3..7e761614a6928 100644 --- a/packages/nodes-base/nodes/Discord/v2/actions/message/send.operation.ts +++ b/packages/nodes-base/nodes/Discord/v2/actions/message/send.operation.ts @@ -4,7 +4,7 @@ import type { INodeExecutionData, INodeProperties, } from 'n8n-workflow'; -import { NodeOperationError } from 'n8n-workflow'; +import { NodeApiError, NodeOperationError } from 'n8n-workflow'; import { updateDisplayOptions } from '../../../../../utils/utilities'; import { discordApiMultiPartRequest, discordApiRequest } from '../../transport'; import { @@ -153,7 +153,7 @@ export async function execute( }; if (embeds) { - body.embeds = prepareEmbeds.call(this, embeds, i); + body.embeds = prepareEmbeds.call(this, embeds); } try { @@ -166,11 +166,39 @@ export async function execute( extractValue: true, }) as string; + if (isOAuth2) { + try { + await discordApiRequest.call(this, 'GET', `/guilds/${guildId}/members/${userId}`); + } catch (error) { + if (error instanceof NodeApiError && error.httpCode === '404') { + throw new NodeOperationError( + this.getNode(), + `User with the id ${userId} is not a member of the selected guild`, + { + itemIndex: i, + }, + ); + } + + throw new NodeOperationError(this.getNode(), error, { + itemIndex: i, + }); + } + } + channelId = ( (await discordApiRequest.call(this, 'POST', '/users/@me/channels', { recipient_id: userId, })) as IDataObject ).id as string; + + if (!channelId) { + throw new NodeOperationError( + this.getNode(), + 'Could not create a channel to send direct message to', + { itemIndex: i }, + ); + } } if (sendTo === 'channel') { @@ -179,11 +207,13 @@ export async function execute( }) as string; } - if (!channelId) { - throw new NodeOperationError(this.getNode(), 'Channel ID is required'); + if (isOAuth2 && sendTo !== 'user') { + await checkAccessToChannel.call(this, channelId, userGuilds, i); } - if (isOAuth2) await checkAccessToChannel.call(this, channelId, userGuilds, i); + if (!channelId) { + throw new NodeOperationError(this.getNode(), 'Channel ID is required', { itemIndex: i }); + } let response: IDataObject[] = []; diff --git a/packages/nodes-base/nodes/Discord/v2/helpers/utils.ts b/packages/nodes-base/nodes/Discord/v2/helpers/utils.ts index 8951320a3f757..b3852f9527a4b 100644 --- a/packages/nodes-base/nodes/Discord/v2/helpers/utils.ts +++ b/packages/nodes-base/nodes/Discord/v2/helpers/utils.ts @@ -114,9 +114,9 @@ export function prepareOptions(options: IDataObject, guildId?: string) { return options; } -export function prepareEmbeds(this: IExecuteFunctions, embeds: IDataObject[], i = 0) { +export function prepareEmbeds(this: IExecuteFunctions, embeds: IDataObject[]) { return embeds - .map((embed, index) => { + .map((embed) => { let embedReturnData: IDataObject = {}; if (embed.inputMethod === 'json') { @@ -261,7 +261,7 @@ export async function checkAccessToChannel( if (!guildId) { throw new NodeOperationError( this.getNode(), - `Could not fing server for channel with the id ${channelId}`, + `Could not find server for channel with the id ${channelId}`, { itemIndex, }, diff --git a/packages/nodes-base/nodes/Discord/v2/methods/listSearch.ts b/packages/nodes-base/nodes/Discord/v2/methods/listSearch.ts index 902dfb156360b..abbec430402e0 100644 --- a/packages/nodes-base/nodes/Discord/v2/methods/listSearch.ts +++ b/packages/nodes-base/nodes/Discord/v2/methods/listSearch.ts @@ -124,7 +124,7 @@ export async function categorySearch(this: ILoadOptionsFunctions): Promise { const guildId = await getGuildId.call(this); diff --git a/packages/nodes-base/nodes/Discord/v2/transport/discord.api.ts b/packages/nodes-base/nodes/Discord/v2/transport/discord.api.ts index 153e37650bc32..c7a066ef4d2cc 100644 --- a/packages/nodes-base/nodes/Discord/v2/transport/discord.api.ts +++ b/packages/nodes-base/nodes/Discord/v2/transport/discord.api.ts @@ -48,7 +48,7 @@ export async function discordApiRequest( if (remaining === 0) { await sleep(resetAfter); } else { - await sleep(20); //prevent excing global rate limit of 50 requests per second + await sleep(20); //prevent exceeding global rate limit of 50 requests per second } return response.body || { success: true }; @@ -91,7 +91,7 @@ export async function discordApiMultiPartRequest( if (remaining === 0) { await sleep(resetAfter); } else { - await sleep(20); //prevent excing global rate limit of 50 requests per second + await sleep(20); //prevent exceeding global rate limit of 50 requests per second } return jsonParse(response.body);