From d23d5d2da0d451e38b5d452e25a52766817f978d Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Sat, 13 May 2023 20:02:19 +0000 Subject: [PATCH] feat(slack): send direct message --- .../backend/src/apps/slack/actions/index.ts | 3 +- .../actions/send-a-direct-message/index.ts | 76 +++++++++++++++++++ .../send-a-direct-message/post-message.ts | 55 ++++++++++++++ .../src/apps/slack/dynamic-data/index.ts | 3 +- .../slack/dynamic-data/list-channels/index.ts | 2 +- .../slack/dynamic-data/list-users/index.ts | 66 ++++++++++++++++ packages/docs/pages/apps/slack/actions.md | 2 + 7 files changed, 204 insertions(+), 3 deletions(-) create mode 100644 packages/backend/src/apps/slack/actions/send-a-direct-message/index.ts create mode 100644 packages/backend/src/apps/slack/actions/send-a-direct-message/post-message.ts create mode 100644 packages/backend/src/apps/slack/dynamic-data/list-users/index.ts diff --git a/packages/backend/src/apps/slack/actions/index.ts b/packages/backend/src/apps/slack/actions/index.ts index 3cfa8fa2..a5e3be8f 100644 --- a/packages/backend/src/apps/slack/actions/index.ts +++ b/packages/backend/src/apps/slack/actions/index.ts @@ -1,5 +1,6 @@ import findMessage from './find-message'; import findUserByEmail from './find-user-by-email'; import sendMessageToChannel from './send-a-message-to-channel'; +import sendDirectMessage from './send-a-direct-message'; -export default [findMessage, findUserByEmail, sendMessageToChannel]; +export default [findMessage, findUserByEmail, sendMessageToChannel, sendDirectMessage]; diff --git a/packages/backend/src/apps/slack/actions/send-a-direct-message/index.ts b/packages/backend/src/apps/slack/actions/send-a-direct-message/index.ts new file mode 100644 index 00000000..38fbbbbb --- /dev/null +++ b/packages/backend/src/apps/slack/actions/send-a-direct-message/index.ts @@ -0,0 +1,76 @@ +import defineAction from '../../../../helpers/define-action'; +import postMessage from './post-message'; + +export default defineAction({ + name: 'Send a direct message', + key: 'sendDirectMessage', + description: 'Sends a direct message to a user or yourself from the Slackbot.', + arguments: [ + { + label: 'To username', + key: 'toUsername', + type: 'dropdown' as const, + required: true, + description: 'Pick a user to send the message to.', + variables: false, + source: { + type: 'query', + name: 'getDynamicData', + arguments: [ + { + name: 'key', + value: 'listUsers', + }, + ], + }, + }, + { + label: 'Message text', + key: 'message', + type: 'string' as const, + required: true, + description: 'The content of your new message.', + variables: true, + }, + { + label: 'Send as a bot?', + key: 'sendAsBot', + type: 'dropdown' as const, + required: false, + value: false, + description: + 'If you choose no, this message will appear to come from you. Direct messages are always sent by bots.', + variables: false, + options: [ + { + label: 'Yes', + value: true, + }, + { + label: 'No', + value: false, + }, + ], + additionalFields: { + type: 'query', + name: 'getDynamicFields', + arguments: [ + { + name: 'key', + value: 'listFieldsAfterSendAsBot', + }, + { + name: 'parameters.sendAsBot', + value: '{parameters.sendAsBot}', + }, + ], + }, + }, + ], + + async run($) { + const message = await postMessage($); + + return message; + }, +}); diff --git a/packages/backend/src/apps/slack/actions/send-a-direct-message/post-message.ts b/packages/backend/src/apps/slack/actions/send-a-direct-message/post-message.ts new file mode 100644 index 00000000..c918f9b6 --- /dev/null +++ b/packages/backend/src/apps/slack/actions/send-a-direct-message/post-message.ts @@ -0,0 +1,55 @@ +import { IGlobalVariable } from '@automatisch/types'; +import { URL } from 'url'; + +type TData = { + channel: string; + text: string; + username?: string; + icon_url?: string; + icon_emoji?: string; +}; + +const postMessage = async ($: IGlobalVariable) => { + const { parameters } = $.step; + const toUsername = parameters.toUsername as string; + const text = parameters.message as string; + const sendAsBot = parameters.sendAsBot as boolean; + const botName = parameters.botName as string; + const botIcon = parameters.botIcon as string; + + const data: TData = { + channel: toUsername, + text, + }; + + if (sendAsBot) { + data.username = botName; + try { + // challenging the input to check if it is a URL! + new URL(botIcon); + data.icon_url = botIcon; + } catch { + data.icon_emoji = botIcon; + } + } + + const customConfig = { + sendAsBot, + }; + + const response = await $.http.post('/chat.postMessage', data, { + additionalProperties: customConfig, + }); + + if (response.data.ok === false) { + throw new Error(JSON.stringify(response.data)); + } + + const message = { + raw: response?.data, + }; + + $.setActionItem(message); +}; + +export default postMessage; diff --git a/packages/backend/src/apps/slack/dynamic-data/index.ts b/packages/backend/src/apps/slack/dynamic-data/index.ts index fae496fc..6963b403 100644 --- a/packages/backend/src/apps/slack/dynamic-data/index.ts +++ b/packages/backend/src/apps/slack/dynamic-data/index.ts @@ -1,3 +1,4 @@ import listChannels from './list-channels'; +import listUsers from './list-users'; -export default [listChannels]; +export default [listChannels, listUsers]; diff --git a/packages/backend/src/apps/slack/dynamic-data/list-channels/index.ts b/packages/backend/src/apps/slack/dynamic-data/list-channels/index.ts index 7553406c..a851903c 100644 --- a/packages/backend/src/apps/slack/dynamic-data/list-channels/index.ts +++ b/packages/backend/src/apps/slack/dynamic-data/list-channels/index.ts @@ -36,7 +36,7 @@ export default { do { const response: TResponse = await $.http.get('/conversations.list', { params: { - types: 'public_channel,private_channel,im', + types: 'public_channel,private_channel', cursor: nextCursor, limit: 1000, } diff --git a/packages/backend/src/apps/slack/dynamic-data/list-users/index.ts b/packages/backend/src/apps/slack/dynamic-data/list-users/index.ts new file mode 100644 index 00000000..5264667b --- /dev/null +++ b/packages/backend/src/apps/slack/dynamic-data/list-users/index.ts @@ -0,0 +1,66 @@ +import { IGlobalVariable, IJSONObject } from '@automatisch/types'; + +type TMember = { + id: string; + profile: { + real_name_normalized: string; + }; +} + +type TUserListResponseData = { + members: TMember[], + response_metadata?: { + next_cursor: string + }; + needed?: string; + error?: string; + ok: boolean; +} + +type TResponse = { + data: TUserListResponseData; +} + +export default { + name: 'List users', + key: 'listUsers', + + async run($: IGlobalVariable) { + const users: { + data: IJSONObject[]; + error: IJSONObject | null; + } = { + data: [], + error: null, + }; + + let nextCursor; + do { + const response: TResponse = await $.http.get('/users.list', { + params: { + cursor: nextCursor, + limit: 1000, + } + }); + + nextCursor = response.data.response_metadata?.next_cursor; + + if (response.data.error === 'missing_scope') { + throw new Error(`Missing "${response.data.needed}" scope while authorizing. Please, reconnect your connection!`); + } + + if (response.data.ok === false) { + throw new Error(JSON.stringify(response.data, null, 2)); + } + + for (const member of response.data.members) { + users.data.push({ + value: member.id as string, + name: member.profile.real_name_normalized as string, + }); + } + } while (nextCursor); + + return users; + }, +}; diff --git a/packages/docs/pages/apps/slack/actions.md b/packages/docs/pages/apps/slack/actions.md index a2b98a84..90de241a 100644 --- a/packages/docs/pages/apps/slack/actions.md +++ b/packages/docs/pages/apps/slack/actions.md @@ -7,6 +7,8 @@ items: desc: Finds a user by email. - name: Send a message to channel desc: Sends a message to a channel you specify. + - name: Send a direct message + desc: Sends a direct message to a user or yourself from the Slackbot. ---