diff --git a/packages/nodes-base/nodes/Discord/Discord.node.ts b/packages/nodes-base/nodes/Discord/Discord.node.ts index e2f9c593dd..aa04516f23 100644 --- a/packages/nodes-base/nodes/Discord/Discord.node.ts +++ b/packages/nodes-base/nodes/Discord/Discord.node.ts @@ -11,34 +11,39 @@ import { import { discordApiRequest, - setHeartbeatInterval, } from './GenericFunctions'; import { + auditLogFields, + auditLogOperations, + channelFields, + channelOperations, + directMessageFields, + directMessageOperations, + emojiFields, + emojiOperations, + inviteFields, + inviteOperations, messageFields, messageOperations, + pinnedMessageFields, + pinnedMessageOperations, userFields, userOperations, } from './descriptions'; -import WebSocket = require('ws'); - -import { - clearInterval, -} from 'timers'; - export class Discord implements INodeType { description: INodeTypeDescription = { displayName: 'Discord', name: 'discord', icon: 'file:discord.svg', - group: ['output'], + group: ['transform'], version: 1, subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', description: 'Consume the Discord API', defaults: { name: 'Discord', - color: '#7289da', + color: '\#7289da', }, inputs: ['main'], outputs: ['main'], @@ -54,20 +59,56 @@ export class Discord implements INodeType { name: 'resource', type: 'options', options: [ + { + name: 'Audit Log', + value: 'auditLog', + }, + { + name: 'Channel', + value: 'channel', + }, + { + name: 'Direct Message', + value: 'directMessage', + }, + { + name: 'Emoji', + value: 'emoji', + }, + { + name: 'Invite', + value: 'invite', + }, { name: 'Message', value: 'message', }, + { + name: 'Pinned Message', + value: 'pinnedMessage', + }, { name: 'User', value: 'user', }, ], - default: 'message', + default: 'channel', description: 'Resource to consume', }, + ...auditLogOperations, + ...auditLogFields, + ...channelOperations, + ...channelFields, + ...directMessageOperations, + ...directMessageFields, + ...emojiOperations, + ...emojiFields, + ...inviteOperations, + ...inviteFields, ...messageOperations, ...messageFields, + ...pinnedMessageOperations, + ...pinnedMessageFields, ...userOperations, ...userFields, ], @@ -75,145 +116,471 @@ export class Discord implements INodeType { async execute(this: IExecuteFunctions): Promise { const items = this.getInputData(); + const returnData: IDataObject[] = []; const resource = this.getNodeParameter('resource', 0) as string; const operation = this.getNodeParameter('operation', 0) as string; let responseData; - const returnData: IDataObject[] = []; - - const { url } = await discordApiRequest.call(this, 'GET', '/gateway'); - - const ws = new WebSocket(`${url}?v=8&encoding=json`); - - await new Promise((resolve) => { - ws.on('open', () => { - console.log('*** WEBSOCKET CONNECTION OPEN ***'); - }); - - let heartbeatInterval: NodeJS.Timeout; - - ws.on('message', (data: string) => { - const op = JSON.parse(data).op; - const mapping: { [key: number]: 'hello' | 'acknowledgment' } = { - 10: 'hello', - 11: 'acknowledgment', - }; - - console.log(`--- MESSAGE RECEIVED: ${mapping[op]} ---`); - - if (mapping[op] === 'hello') { - console.log('--- SET HEARTBEAT ---'); - heartbeatInterval = setHeartbeatInterval(ws, data); - resolve(); - } - - // TODO close connection if heartbeat acknowledgment does not arrive - console.log(`--- MESSAGE CONTENTS FOR ${mapping[op]} ---`); - console.log(JSON.parse(data)); - }); - - ws.on('close', () => { - console.log('*** WEBSOCKET CONNECTION CLOSED ***'); - console.log('--- UNSET HEARTBEAT ---'); - clearInterval(heartbeatInterval); - }); - }); for (let i = 0; i < items.length; i++) { - if (resource === 'message') { + if (resource === 'auditLog') { - // ********************************************************************* - // message - // ********************************************************************* + // ********************************************************************** + // auditLog + // ********************************************************************** + + if (operation === 'get') { + + // ---------------------------------------- + // auditLog: get + // ---------------------------------------- + + // https://discord.com/developers/docs/resources/audit-log#get-guild-audit-log + + const guildId = this.getNodeParameter('guildId', i); + + const qs = {} as IDataObject; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(qs, additionalFields); + } + + const endpoint = `/guilds/${guildId}/audit-logs`; + responseData = await discordApiRequest.call(this, 'GET', endpoint, {}, qs); + + } + + } else if (resource === 'channel') { + + // ********************************************************************** + // channel + // ********************************************************************** + + if (operation === 'delete') { + + // ---------------------------------------- + // channel: delete + // ---------------------------------------- + + // https://discord.com/developers/docs/resources/channel#deleteclose-channel + + const channelId = this.getNodeParameter('channelId', i); + + const endpoint = `/channels/${channelId}`; + responseData = await discordApiRequest.call(this, 'DELETE', endpoint, {}, {}); + + } else if (operation === 'get') { + + // ---------------------------------------- + // channel: get + // ---------------------------------------- + + // https://discord.com/developers/docs/resources/channel#get-channel + + const channelId = this.getNodeParameter('channelId', i); + + const endpoint = `/channels/${channelId}`; + responseData = await discordApiRequest.call(this, 'GET', endpoint, {}, {}); + + } else if (operation === 'update') { + + // ---------------------------------------- + // channel: update + // ---------------------------------------- + + // https://discord.com/developers/docs/resources/channel#modify-channel + + const channelId = this.getNodeParameter('channelId', i); + const overwriteId = this.getNodeParameter('overwriteId', i); + + const qs = {} as IDataObject; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(qs, additionalFields); + } + + const endpoint = `/channels/${channelId}/permissions/${overwriteId}`; + responseData = await discordApiRequest.call(this, 'PUT', endpoint, {}, qs); + + } + + } else if (resource === 'directMessage') { + + // ********************************************************************** + // directMessage + // ********************************************************************** if (operation === 'create') { - // ---------------------------------- - // message: create - // ---------------------------------- + // ---------------------------------------- + // directMessage: create + // ---------------------------------------- - const credentials = this.getCredentials('discordOAuth2Api') as { - oauthTokenData: { webhook: { url: string } } + // https://discord.com/developers/docs/resources/user#create-dm + + const endpoint = '/users/@me/channels'; + responseData = await discordApiRequest.call(this, 'POST', endpoint); + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // directMessage: getAll + // ---------------------------------------- + + // https://discord.com/developers/docs/resources/user#get-user-dms + + const endpoint = '/users/@me/channels'; + responseData = await discordApiRequest.call(this, 'GET', endpoint); + + } + + } else if (resource === 'emoji') { + + // ********************************************************************** + // emoji + // ********************************************************************** + + if (operation === 'create') { + + // ---------------------------------------- + // emoji: create + // ---------------------------------------- + + // https://discord.com/developers/docs/resources/emoji#create-guild-emoji + + const guildId = this.getNodeParameter('guildId', i); + + const body = { + name: this.getNodeParameter('name', i), + image: this.getNodeParameter('image', i), + } as IDataObject; + + const endpoint = `/guilds/${guildId}/emojis`; + responseData = await discordApiRequest.call(this, 'POST', endpoint, body); + + } else if (operation === 'delete') { + + // ---------------------------------------- + // emoji: delete + // ---------------------------------------- + + // https://discord.com/developers/docs/resources/emoji#delete-guild-emoji + + const guildId = this.getNodeParameter('guildId', i); + + const body = { + name: this.getNodeParameter('name', i), + } as IDataObject; + + const endpoint = `/guilds/${guildId}/emojis`; + responseData = await discordApiRequest.call(this, 'DELETE', endpoint, body); + + } else if (operation === 'get') { + + // ---------------------------------------- + // emoji: get + // ---------------------------------------- + + // https://discord.com/developers/docs/resources/emoji#get-guild-emoji + + const guildId = this.getNodeParameter('guildId', i); + const emojiId = this.getNodeParameter('emojiId', i); + + const endpoint = `/guilds/${guildId}/emojis/${emojiId}`; + responseData = await discordApiRequest.call(this, 'GET', endpoint); + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // emoji: getAll + // ---------------------------------------- + + // https://discord.com/developers/docs/resources/emoji#list-guild-emojis + + const guildId = this.getNodeParameter('guildId', i); + + const endpoint = `/guilds/${guildId}/emojis`; + responseData = await discordApiRequest.call(this, 'GET', endpoint); + + } else if (operation === 'update') { + + // ---------------------------------------- + // emoji: update + // ---------------------------------------- + + // https://discord.com/developers/docs/resources/emoji#modify-guild-emoji + + const guildId = this.getNodeParameter('guildId', i); + const emojiId = this.getNodeParameter('emojiId', i); + + const body = { + name: this.getNodeParameter('name', i), + } as IDataObject; + + const endpoint = `/guilds/${guildId}/emojis/${emojiId}`; + responseData = await discordApiRequest.call(this, 'PATCH', endpoint, body); + + } + + } else if (resource === 'invite') { + + // ********************************************************************** + // invite + // ********************************************************************** + + if (operation === 'delete') { + + // ---------------------------------------- + // invite: delete + // ---------------------------------------- + + // https://discord.com/developers/docs/resources/invite#delete-invite + + const inviteCode = this.getNodeParameter('inviteCode', i); + + const qs: IDataObject = { + with_counts: this.getNodeParameter('withCounts', i), }; - const webhookUrl = credentials.oauthTokenData.webhook.url; + const endpoint = `/invites/${inviteCode}`; + responseData = await discordApiRequest.call(this, 'DELETE', endpoint, {}, qs); - const body: IDataObject = { + } else if (operation === 'get') { + + // ---------------------------------------- + // invite: get + // ---------------------------------------- + + // https://discord.com/developers/docs/resources/invite#get-invite + + const inviteCode = this.getNodeParameter('inviteCode', i); + + const qs: IDataObject = { + with_counts: this.getNodeParameter('with_counts', i), + }; + + const endpoint = `/invites/${inviteCode}`; + responseData = await discordApiRequest.call(this, 'GET', endpoint, {}, qs); + + } else { + throw new Error(`Unknown operation: ${operation}`); + } + + } else if (resource === 'message') { + + // ********************************************************************** + // message + // ********************************************************************** + + if (operation === 'create') { + + // ---------------------------------------- + // message: create + // ---------------------------------------- + + // https://discord.com/developers/docs/resources/channel#create-message + + const channelId = this.getNodeParameter('channelId', i); + + const qs: IDataObject = { content: this.getNodeParameter('content', i), }; const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; - Object.assign(body, additionalFields); - await discordApiRequest.call(this, 'POST', '', body, {}, webhookUrl); - responseData = { success: true }; + if (Object.keys(additionalFields).length) { + Object.assign(qs, additionalFields); + } - } else if (operation === 'ws') { + const endpoint = `/channels/${channelId}/messages`; + responseData = await discordApiRequest.call(this, 'POST', endpoint, {}, qs); - const { oauthTokenData } = this.getCredentials('discordOAuth2Api') as { - oauthTokenData: { access_token: string } + } else if (operation === 'delete') { + + // ---------------------------------------- + // message: delete + // ---------------------------------------- + + // https://discord.com/developers/docs/resources/channel#delete-message + + const channelId = this.getNodeParameter('channelId', i); + const messageId = this.getNodeParameter('messageId', i); + + const endpoint = `/channels/${channelId}/messages/${messageId}`; + responseData = await discordApiRequest.call(this, 'DELETE', endpoint); + + } else if (operation === 'update') { + + // ---------------------------------------- + // message: update + // ---------------------------------------- + + // https://discord.com/developers/docs/resources/channel#edit-message + + const channelId = this.getNodeParameter('channelId', i); + const messageId = this.getNodeParameter('messageId', i); + + const qs: IDataObject = { + content: this.getNodeParameter('content', i), + embed: this.getNodeParameter('embed', i), }; - const payload = JSON.stringify({ - op: 2, - d: { - token: oauthTokenData.access_token, - intents: 513, - properties: { - $os: 'linux', - $browser: 'firefox', - $device: 'n8n', - }, - }, - }); + const endpoint = `/channels/${channelId}/messages/${messageId}`; + responseData = await discordApiRequest.call(this, 'PATCH', endpoint, {}, qs); - // ws.send(payload); + } else if (operation === 'get') { + + // ---------------------------------------- + // message: get + // ---------------------------------------- + + // https://discord.com/developers/docs/resources/channel#get-channel-message + + const channelId = this.getNodeParameter('channelId', i); + const messageId = this.getNodeParameter('messageId', i); + + const endpoint = `/channels/${channelId}/messages/${messageId}`; + responseData = await discordApiRequest.call(this, 'GET', endpoint); + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // message: getAll + // ---------------------------------------- + + // https://discord.com/developers/docs/resources/channel#get-channel-messages + + const channelId = this.getNodeParameter('channelId', i); + + const qs: IDataObject = { + after: this.getNodeParameter('after', i), + around: this.getNodeParameter('around', i), + before: this.getNodeParameter('before', i), + }; + + const endpoint = `/channels/${channelId}/messages`; + responseData = await discordApiRequest.call(this, 'GET', endpoint, {}, qs); } + } else if (resource === 'pinnedMessage') { - } + // ********************************************************************** + // pinnedMessage + // ********************************************************************** - if (resource === 'user') { + if (operation === 'getAll') { - // ********************************************************************* - // user - // ********************************************************************* + // ---------------------------------------- + // pinnedMessage: getAll + // ---------------------------------------- - if (operation === 'get') { + // https://discord.com/developers/docs/resources/channel#get-pinned-messages - // ---------------------------------- - // user: get - // ---------------------------------- + const channelId = this.getNodeParameter('channelId', i); - const details = this.getNodeParameter('details', i) as 'currentUser' | 'currentUserGuilds' | 'currentserConnections'; - - const endpoints: { [key: string]: string } = { - currentUser: '@/me', - currentUserGuilds: '@/me/guilds', - currentUserConnections: '@/me/connections', + const qs: IDataObject = { }; - responseData = await discordApiRequest.call(this, 'GET', `/users/${endpoints[details]}`); + const endpoint = `/channels/${channelId}/pins`; + responseData = await discordApiRequest.call(this, 'GET', endpoint, {}, qs); + + } else if (operation === 'create') { + + // ---------------------------------------- + // pinnedMessage: create + // ---------------------------------------- + + // https://discord.com/developers/docs/resources/channel#add-pinned-channel-message + + const channelId = this.getNodeParameter('channelId', i); + const messageId = this.getNodeParameter('messageId', i); + + const endpoint = `/channels/${channelId}/pins/${messageId}`; + responseData = await discordApiRequest.call(this, 'PUT', endpoint); + + } else if (operation === 'delete') { + + // ---------------------------------------- + // pinnedMessage: delete + // ---------------------------------------- + + // https://discord.com/developers/docs/resources/channel#delete-pinned-channel-message + + const channelId = this.getNodeParameter('channelId', i); + const messageId = this.getNodeParameter('messageId', i); + + const endpoint = `/channels/${channelId}/pins/${messageId}`; + responseData = await discordApiRequest.call(this, 'DELETE', endpoint); + } + + } else if (resource === 'user') { + + // ********************************************************************** + // user + // ********************************************************************** + + if (operation === 'getCurrentUser') { + + // ---------------------------------------- + // user: getCurrentUser + // ---------------------------------------- + + // https://discord.com/developers/docs/resources/user#get-current-user + + const endpoint = '/users/@me'; + responseData = await discordApiRequest.call(this, 'GET', endpoint); + + } else if (operation === 'getCurrentUserGuilds') { + + // ---------------------------------------- + // user: getCurrentUserGuilds + // ---------------------------------------- + + // https://discord.com/developers/docs/resources/user#get-current-user-guilds + + const endpoint = '/users/@me@guilds'; + responseData = await discordApiRequest.call(this, 'GET', endpoint); + + } else if (operation === 'get') { + + // ---------------------------------------- + // user: get + // ---------------------------------------- + + // https://discord.com/developers/docs/resources/user#get-user + + const userId = this.getNodeParameter('userId', i); + + const endpoint = `/users/${userId}`; + responseData = await discordApiRequest.call(this, 'GET', endpoint); + + } else if (operation === 'updateCurrentUser') { + + // ---------------------------------------- + // user: updateCurrentUser + // ---------------------------------------- + + // https://discord.com/developers/docs/resources/user#modify-current-user + + const endpoint = '/users/@me'; + responseData = await discordApiRequest.call(this, 'PATCH', endpoint); + + } + } - Array.isArray(responseData) - ? returnData.push(...responseData) - : returnData.push(responseData); + Array.isArray(responseData) + ? returnData.push(...responseData) + : returnData.push(responseData); + } - await new Promise(resolve => { - setTimeout(resolve, 3 * 60 * 1000); - }); - - ws.terminate(); - - - // console.log(ws.); - return [this.helpers.returnJsonArray(returnData)]; } }