From d8298ff3a32592dc3bc3ed38bc41218f803d21be Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Thu, 31 Jul 2025 20:28:20 -0400 Subject: [PATCH 01/34] Create Slack v2 Test components Show configuration error when channel is not found and as_user is false Use bot token instead of user token when posting message unless as_user is true --- .tool-versions | 2 +- components/slack_v2_test/README.md | 83 ++ .../add-emoji-reaction/add-emoji-reaction.mjs | 43 + .../approve-workflow/approve-workflow.mjs | 87 ++ .../archive-channel/archive-channel.mjs | 33 + .../actions/common/build-blocks.mjs | 182 ++++ .../actions/common/send-message.mjs | 256 +++++ .../actions/create-channel/create-channel.mjs | 35 + .../create-reminder/create-reminder.mjs | 47 + .../actions/delete-file/delete-file.mjs | 34 + .../actions/delete-message/delete-message.mjs | 40 + .../actions/find-message/find-message.mjs | 83 ++ .../find-user-by-email/find-user-by-email.mjs | 27 + .../actions/get-file/get-file.mjs | 34 + .../invite-user-to-channel.mjs | 40 + .../actions/kick-user/kick-user.mjs | 51 + .../actions/list-channels/list-channels.mjs | 47 + .../actions/list-files/list-files.mjs | 68 ++ .../list-group-members/list-group-members.mjs | 63 ++ .../list-members-in-channel.mjs | 66 ++ .../actions/list-replies/list-replies.mjs | 61 ++ .../actions/list-users/list-users.mjs | 55 + .../reply-to-a-message/reply-to-a-message.mjs | 49 + .../send-block-kit-message.mjs | 43 + .../send-large-message/send-large-message.mjs | 93 ++ .../send-message-advanced.mjs | 70 ++ .../send-message-to-channel.mjs | 40 + .../send-message-to-user-or-group.mjs | 73 ++ .../actions/send-message/send-message.mjs | 51 + .../set-channel-description.mjs | 32 + .../set-channel-topic/set-channel-topic.mjs | 32 + .../actions/set-status/set-status.mjs | 44 + .../update-group-members.mjs | 67 ++ .../actions/update-message/update-message.mjs | 54 + .../actions/update-profile/update-profile.mjs | 87 ++ .../actions/upload-file/upload-file.mjs | 100 ++ .../verify-slack-signature.mjs | 60 ++ components/slack_v2_test/common/constants.mjs | 31 + components/slack_v2_test/package.json | 22 + .../slack_v2_test/slack_v2_test.app.mjs | 967 ++++++++++++++++++ .../slack_v2_test/sources/common/base.mjs | 184 ++++ .../sources/common/constants.mjs | 58 ++ .../new-channel-created.mjs | 32 + .../new-channel-created/test-event.mjs | 44 + .../new-direct-message/new-direct-message.mjs | 53 + .../sources/new-direct-message/test-event.mjs | 28 + .../new-interaction-event-received/README.md | 85 ++ .../new-interaction-event-received.mjs | 105 ++ .../test-event.mjs | 86 ++ .../new-keyword-mention.mjs | 129 +++ .../new-keyword-mention/test-event.mjs | 28 + .../new-message-in-channels.mjs | 99 ++ .../new-message-in-channels/test-event.mjs | 45 + .../new-reaction-added/new-reaction-added.mjs | 109 ++ .../sources/new-reaction-added/test-event.mjs | 193 ++++ .../new-saved-message/new-saved-message.mjs | 32 + .../sources/new-saved-message/test-event.mjs | 37 + .../sources/new-user-added/new-user-added.mjs | 32 + .../sources/new-user-added/test-event.mjs | 48 + .../new-user-mention/new-user-mention.mjs | 117 +++ .../sources/new-user-mention/test-event.mjs | 28 + pnpm-lock.yaml | 8 +- 62 files changed, 4927 insertions(+), 5 deletions(-) create mode 100644 components/slack_v2_test/README.md create mode 100644 components/slack_v2_test/actions/add-emoji-reaction/add-emoji-reaction.mjs create mode 100644 components/slack_v2_test/actions/approve-workflow/approve-workflow.mjs create mode 100644 components/slack_v2_test/actions/archive-channel/archive-channel.mjs create mode 100644 components/slack_v2_test/actions/common/build-blocks.mjs create mode 100644 components/slack_v2_test/actions/common/send-message.mjs create mode 100644 components/slack_v2_test/actions/create-channel/create-channel.mjs create mode 100644 components/slack_v2_test/actions/create-reminder/create-reminder.mjs create mode 100644 components/slack_v2_test/actions/delete-file/delete-file.mjs create mode 100644 components/slack_v2_test/actions/delete-message/delete-message.mjs create mode 100644 components/slack_v2_test/actions/find-message/find-message.mjs create mode 100644 components/slack_v2_test/actions/find-user-by-email/find-user-by-email.mjs create mode 100644 components/slack_v2_test/actions/get-file/get-file.mjs create mode 100644 components/slack_v2_test/actions/invite-user-to-channel/invite-user-to-channel.mjs create mode 100644 components/slack_v2_test/actions/kick-user/kick-user.mjs create mode 100644 components/slack_v2_test/actions/list-channels/list-channels.mjs create mode 100644 components/slack_v2_test/actions/list-files/list-files.mjs create mode 100644 components/slack_v2_test/actions/list-group-members/list-group-members.mjs create mode 100644 components/slack_v2_test/actions/list-members-in-channel/list-members-in-channel.mjs create mode 100644 components/slack_v2_test/actions/list-replies/list-replies.mjs create mode 100644 components/slack_v2_test/actions/list-users/list-users.mjs create mode 100644 components/slack_v2_test/actions/reply-to-a-message/reply-to-a-message.mjs create mode 100644 components/slack_v2_test/actions/send-block-kit-message/send-block-kit-message.mjs create mode 100644 components/slack_v2_test/actions/send-large-message/send-large-message.mjs create mode 100644 components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs create mode 100644 components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs create mode 100644 components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs create mode 100644 components/slack_v2_test/actions/send-message/send-message.mjs create mode 100644 components/slack_v2_test/actions/set-channel-description/set-channel-description.mjs create mode 100644 components/slack_v2_test/actions/set-channel-topic/set-channel-topic.mjs create mode 100644 components/slack_v2_test/actions/set-status/set-status.mjs create mode 100644 components/slack_v2_test/actions/update-group-members/update-group-members.mjs create mode 100644 components/slack_v2_test/actions/update-message/update-message.mjs create mode 100644 components/slack_v2_test/actions/update-profile/update-profile.mjs create mode 100644 components/slack_v2_test/actions/upload-file/upload-file.mjs create mode 100644 components/slack_v2_test/actions/verify-slack-signature/verify-slack-signature.mjs create mode 100644 components/slack_v2_test/common/constants.mjs create mode 100644 components/slack_v2_test/package.json create mode 100644 components/slack_v2_test/slack_v2_test.app.mjs create mode 100644 components/slack_v2_test/sources/common/base.mjs create mode 100644 components/slack_v2_test/sources/common/constants.mjs create mode 100644 components/slack_v2_test/sources/new-channel-created/new-channel-created.mjs create mode 100644 components/slack_v2_test/sources/new-channel-created/test-event.mjs create mode 100644 components/slack_v2_test/sources/new-direct-message/new-direct-message.mjs create mode 100644 components/slack_v2_test/sources/new-direct-message/test-event.mjs create mode 100644 components/slack_v2_test/sources/new-interaction-event-received/README.md create mode 100644 components/slack_v2_test/sources/new-interaction-event-received/new-interaction-event-received.mjs create mode 100644 components/slack_v2_test/sources/new-interaction-event-received/test-event.mjs create mode 100644 components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs create mode 100644 components/slack_v2_test/sources/new-keyword-mention/test-event.mjs create mode 100644 components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs create mode 100644 components/slack_v2_test/sources/new-message-in-channels/test-event.mjs create mode 100644 components/slack_v2_test/sources/new-reaction-added/new-reaction-added.mjs create mode 100644 components/slack_v2_test/sources/new-reaction-added/test-event.mjs create mode 100644 components/slack_v2_test/sources/new-saved-message/new-saved-message.mjs create mode 100644 components/slack_v2_test/sources/new-saved-message/test-event.mjs create mode 100644 components/slack_v2_test/sources/new-user-added/new-user-added.mjs create mode 100644 components/slack_v2_test/sources/new-user-added/test-event.mjs create mode 100644 components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs create mode 100644 components/slack_v2_test/sources/new-user-mention/test-event.mjs diff --git a/.tool-versions b/.tool-versions index 789d54cc38ba4..d9f6abe88e652 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,4 +1,4 @@ -nodejs 20.13.1 +nodejs 18.17.0 pnpm 9.14.2 python 3.11.5 poetry 1.6.1 diff --git a/components/slack_v2_test/README.md b/components/slack_v2_test/README.md new file mode 100644 index 0000000000000..72f31a7d6dcc7 --- /dev/null +++ b/components/slack_v2_test/README.md @@ -0,0 +1,83 @@ +# Overview + +The Pipedream Slack app enables you to build event-driven workflows that interact with the Slack API. Once you authorize the Pipedream app's access to your workspace, you can use [Pipedream workflows](/workflows/) to perform common Slack [actions](#workflow-actions) or [write your own code](/code/) against the Slack API. + +The Pipedream Slack app is not a typical app. You don't interact with it directly as a bot, and it doesn't add custom functionality to your workspace out of the box. It makes it easier to automate anything you'd typically use the Slack API for, using Pipedream workflows. + +- Automate posting updates to your team channels +- Create a bot to answer common questions +- Integrate with your existing tools and services +- And much more! + +# Getting Started + +## Should I use the Slack or Slack Bot app on Pipedream? + +The Slack app is the easiest and most convenient option to get started. It installs the official Pipedream bot into your Slack workspace with just a few clicks. + +However, if you'd like to use your own bot registered with the [Slack API](https://api.slack.com), you can use the [Slack Bot app](https://pipedream.com/apps/slack-bot) instead. + +The Slack Bot requires a bot token to allow your Pipedream workflows to authenticate as your bot. The extra setup steps allow you to list your custom bot on the Slack Marketplace or install the bot on other workspaces as your bot's name instead of as Pipedream. + +## Accounts + +1. Visit [https://pipedream.com/accounts](https://pipedream.com/accounts). +2. Click on the **Click Here To Connect An App** button in the top-right. +3. Search for "Slack" among the list of apps and select it. +4. This will open a new window asking you to allow Pipedream access to your Slack workspace. Choose the right workspace where you'd like to install the app, then click **Allow**. +5. That's it! You can now use this Slack account in any [actions](#workflow-actions) or [link it to any code step](/connected-accounts/#connecting-accounts). + +## Within a workflow + +1. [Create a new workflow](https://pipedream.com/new). +2. Select your trigger (HTTP, Cron, etc.). +3. Click the **+** button below the trigger step and search for "Slack". +4. Select the **Send a Message** action. +5. Click the **Connect Account** button near the top of the step. This will prompt you to select any existing Slack accounts you've previously authenticated with Pipedream, or you can select a **New** account. Clicking **New** opens a new window asking you to allow Pipedream access to your Slack workspace. Choose the right workspace where you'd like to install the app, then click **Allow**. +6. After allowing access, you can connect to the Slack API using any of the Slack actions within a Pipedream workflow. + +# Example Use Cases + +- **Automated Standup Reports**: Trigger a workflow on Pipedream to collect standup updates from team members within a Slack channel at a scheduled time. The workflow compiles updates into a formatted report and posts it to a designated channel or sends it via email using an app like SendGrid. + +- **Customer Support Ticketing**: Use Pipedream to monitor a Slack support channel for new messages. On detecting a message, the workflow creates a ticket in a customer support platform like Zendesk or Jira. It can also format and forward critical information back to the Slack channel to keep the team updated. + +- **Real-time CRM Updates**: Configure a Pipedream workflow to listen for specific trigger words in sales-related Slack channels. When mentioned, the workflow fetches corresponding data from a CRM tool like Salesforce and posts the latest deal status or customer information in the Slack conversation for quick reference. + + +# Troubleshooting + +## Error Responses + +Slack's API will always return JSON, regardless if the request was successfully processed or not. + +Each JSON response includes an `ok` boolean property indicating whether the action succeeded or failed. + +Example of a successful response: + +```json +{ + "ok": true +} +``` + +If the `ok` property is false, Slack will also include an `error` property with a short machine-readable code that describes the error. + +Example of a failure: +```json +{ + "ok": false, + "error": "invalid_parameters" +} +``` + +Additionally, if the action is successful, there's still a chance of a `warning` property in the response. This may contain a comma-separated list of warning codes. + +Example of a successful response, but with warnings: + +```json +{ + "ok": true, + "warnings": "invalid_character_set" +} +``` diff --git a/components/slack_v2_test/actions/add-emoji-reaction/add-emoji-reaction.mjs b/components/slack_v2_test/actions/add-emoji-reaction/add-emoji-reaction.mjs new file mode 100644 index 0000000000000..9e2fb6e5860e9 --- /dev/null +++ b/components/slack_v2_test/actions/add-emoji-reaction/add-emoji-reaction.mjs @@ -0,0 +1,43 @@ +import slack from "../../slack_v2_test.app.mjs"; + +export default { + key: "slack_v2_test-add-emoji-reaction", + name: "Add Emoji Reaction", + description: "Add an emoji reaction to a message. [See the documentation](https://api.slack.com/methods/reactions.add)", + version: "0.0.1", + type: "action", + props: { + slack, + conversation: { + propDefinition: [ + slack, + "conversation", + ], + description: "Channel where the message to add reaction to was posted.", + }, + timestamp: { + propDefinition: [ + slack, + "messageTs", + ], + description: "Timestamp of the message to add reaction to. e.g. `1403051575.000407`.", + }, + icon_emoji: { + propDefinition: [ + slack, + "icon_emoji", + ], + description: "Provide an emoji to use as the icon for this reaction. E.g. `fire`", + optional: false, + }, + }, + async run({ $ }) { + const response = await this.slack.addReactions({ + channel: this.conversation, + timestamp: this.timestamp, + name: this.icon_emoji, + }); + $.export("$summary", `Successfully added ${this.icon_emoji} emoji reaction.`); + return response; + }, +}; diff --git a/components/slack_v2_test/actions/approve-workflow/approve-workflow.mjs b/components/slack_v2_test/actions/approve-workflow/approve-workflow.mjs new file mode 100644 index 0000000000000..c6d730595742d --- /dev/null +++ b/components/slack_v2_test/actions/approve-workflow/approve-workflow.mjs @@ -0,0 +1,87 @@ +import slack from "../../slack_v2_test.app.mjs"; +import constants from "../../common/constants.mjs"; + +export default { + key: "slack_v2_test-approve-workflow", + name: "Approve Workflow", + description: "Suspend the workflow until approved by a Slack message. [See the documentation](https://pipedream.com/docs/code/nodejs/rerun#flowsuspend)", + version: "0.1.0", + type: "action", + props: { + slack, + channelType: { + type: "string", + label: "Channel Type", + description: "The type of channel to send to. User/Direct Message (im), Group (mpim), Private Channel or Public Channel", + async options() { + return constants.CHANNEL_TYPE_OPTIONS; + }, + }, + conversation: { + propDefinition: [ + slack, + "conversation", + (c) => ({ + types: c.channelType === "Channels" + ? [ + constants.CHANNEL_TYPE.PUBLIC, + constants.CHANNEL_TYPE.PRIVATE, + ] + : [ + c.channelType, + ], + }), + ], + }, + message: { + type: "string", + label: "Message", + description: "Text to include with the Approve and Cancel Buttons", + }, + }, + async run({ $ }) { + const { + resume_url, cancel_url, + } = $.flow.suspend(); + + const response = await this.slack.postChatMessage({ + text: "Click here to approve or cancel workflow", + blocks: [ + { + type: "section", + text: { + type: "mrkdwn", + text: this.message, + }, + }, + { + type: "actions", + elements: [ + { + type: "button", + text: { + type: "plain_text", + text: "Approve", + }, + style: "primary", + url: resume_url, + }, + { + type: "button", + text: { + type: "plain_text", + text: "Cancel", + }, + style: "danger", + url: cancel_url, + }, + ], + }, + ], + channel: this.conversation, + }); + + $.export("$summary", "Successfully sent message"); + return response; + }, +}; diff --git a/components/slack_v2_test/actions/archive-channel/archive-channel.mjs b/components/slack_v2_test/actions/archive-channel/archive-channel.mjs new file mode 100644 index 0000000000000..783c2d93f97f7 --- /dev/null +++ b/components/slack_v2_test/actions/archive-channel/archive-channel.mjs @@ -0,0 +1,33 @@ +import slack from "../../slack_v2_test.app.mjs"; +import constants from "../../common/constants.mjs"; + +export default { + key: "slack_v2_test-archive-channel", + name: "Archive Channel", + description: "Archive a channel. [See the documentation](https://api.slack.com/methods/conversations.archive)", + version: "0.0.1", + type: "action", + props: { + slack, + conversation: { + propDefinition: [ + slack, + "conversation", + () => ({ + types: [ + constants.CHANNEL_TYPE.PUBLIC, + constants.CHANNEL_TYPE.PRIVATE, + constants.CHANNEL_TYPE.MPIM, + ], + }), + ], + }, + }, + async run({ $ }) { + const response = await this.slack.archiveConversations({ + channel: this.conversation, + }); + $.export("$summary", "Successfully archived channel."); + return response; + }, +}; diff --git a/components/slack_v2_test/actions/common/build-blocks.mjs b/components/slack_v2_test/actions/common/build-blocks.mjs new file mode 100644 index 0000000000000..fdbc9a70e4b17 --- /dev/null +++ b/components/slack_v2_test/actions/common/build-blocks.mjs @@ -0,0 +1,182 @@ +import common from "./send-message.mjs"; + +export default { + props: { + passArrayOrConfigure: { + type: "string", + label: "Add Blocks - Reference Existing Blocks Array or Configure Manually?", + description: "Would you like to reference an array of blocks from a previous step (for example, `{{steps.blocks.$return_value}}`), or configure them in this action?", + options: [ + { + label: "Reference an array of blocks", + value: "array", + }, + { + label: "Configure blocks individually (maximum 5 blocks)", + value: "configure", + }, + ], + optional: true, + reloadProps: true, + }, + }, + methods: { + // This adds a visual separator in the props form between each block + separator() { + return ` + + --- + + `; + }, + createBlockProp(type, label, description) { + return { + type, + label, + description: `${description} ${this.separator()}`, + }; + }, + createBlock(type, text) { + if (type === "section") { + return { + type: "section", + text: { + type: "mrkdwn", + text, + }, + }; + } else if (type === "context") { + const elements = Array.isArray(text) + ? text.map((t) => ({ + type: "mrkdwn", + text: t, + })) + : [ + { + type: "mrkdwn", + text, + }, + ]; + return { + type: "context", + elements, + }; + } else if (type === "link_button") { + const buttons = Object.keys(text).map((buttonText) => ({ + type: "button", + text: { + type: "plain_text", + text: buttonText, + emoji: true, + }, + url: text[buttonText], // Access the URL using buttonText as the key + action_id: `actionId-${Math.random().toString(36) + .substr(2, 9)}`, // Generates a random action_id + })); + + return { + type: "actions", + elements: buttons, + }; + } + }, + }, + async additionalProps(existingProps) { + await common.additionalProps.call(this, existingProps); + const props = {}; + const sectionDescription = "Add a **section** block to your message and configure with plain text or mrkdwn. See [Slack's docs](https://api.slack.com/reference/block-kit/blocks?ref=bk#section) for more info."; + const contextDescription = "Add a **context** block to your message and configure with plain text or mrkdwn. Define multiple items if you'd like multiple elements in the context block. See [Slack's docs](https://api.slack.com/reference/block-kit/blocks?ref=bk#context) for more info."; + const linkButtonDescription = "Add a **link button** to your message. Enter the button text as the key and the link URL as the value. Configure multiple buttons in the array to render them inline, or add additional Button Link blocks to render them vertically. See [Slack's docs](https://api.slack.com/reference/block-kit/blocks?ref=bk#actions) for more info."; + const propsSection = this.createBlockProp("string", "Section Block Text", sectionDescription); + const propsContext = this.createBlockProp("string[]", "Context Block Text", contextDescription); + const propsLinkButton = this.createBlockProp("object", "Link Button", linkButtonDescription); + + if (!this.passArrayOrConfigure) { + return props; + } + if (this.passArrayOrConfigure == "array") { + props.blocks = { + type: common.props.slack.propDefinitions.blocks.type, + label: common.props.slack.propDefinitions.blocks.label, + description: common.props.slack.propDefinitions.blocks.description, + }; + } else { + props.blockType = { + type: "string", + label: "Block Type", + description: "Select the type of block to add. Refer to [Slack's docs](https://api.slack.com/reference/block-kit/blocks) for more info.", + options: [ + { + label: "Section", + value: "section", + }, + { + label: "Context", + value: "context", + }, + { + label: "Link Button", + value: "link_button", + }, + ], + reloadProps: true, + };} + let currentBlockType = this.blockType; + for (let i = 1; i <= 5; i++) { + if (currentBlockType === "section") { + props[`section${i}`] = propsSection; + } else if (currentBlockType === "context") { + props[`context${i}`] = propsContext; + } else if (currentBlockType === "link_button") { + props[`linkButton${i}`] = propsLinkButton; + } + + if (i < 5 && currentBlockType) { // Check if currentBlockType is set before adding nextBlockType + props[`nextBlockType${i}`] = { + type: "string", + label: "Next Block Type", + options: [ + { + label: "Section", + value: "section", + }, + { + label: "Context", + value: "context", + }, + { + label: "Link Button", + value: "link_button", + }, + ], + optional: true, + reloadProps: true, + }; + currentBlockType = this[`nextBlockType${i}`]; + } + } + return props; + }, + async run() { + let blocks = []; + if (this.passArrayOrConfigure == "array") { + blocks = this.blocks; + } else { + for (let i = 1; i <= 5; i++) { + if (this[`section${i}`]) { + blocks.push(this.createBlock("section", this[`section${i}`])); + } + + if (this[`context${i}`]) { + blocks.push(this.createBlock("context", this[`context${i}`])); + } + + if (this[`linkButton${i}`]) { + blocks.push(this.createBlock("link_button", this[`linkButton${i}`])); + } + } + } + return blocks; + }, +}; + diff --git a/components/slack_v2_test/actions/common/send-message.mjs b/components/slack_v2_test/actions/common/send-message.mjs new file mode 100644 index 0000000000000..eb72f6065a339 --- /dev/null +++ b/components/slack_v2_test/actions/common/send-message.mjs @@ -0,0 +1,256 @@ +import slack from "../../slack_v2_test.app.mjs"; + +export default { + props: { + slack, + as_user: { + propDefinition: [ + slack, + "as_user", + ], + }, + post_at: { + propDefinition: [ + slack, + "post_at", + ], + }, + include_sent_via_pipedream_flag: { + type: "boolean", + optional: true, + default: true, + label: "Include link to Pipedream", + description: "Defaults to `true`, includes a link to Pipedream at the end of your Slack message.", + }, + customizeBotSettings: { + type: "boolean", + label: "Customize Bot Settings", + description: "Customize the username and/or icon of the Bot", + optional: true, + reloadProps: true, + }, + username: { + propDefinition: [ + slack, + "username", + ], + hidden: true, + }, + icon_emoji: { + propDefinition: [ + slack, + "icon_emoji", + ], + hidden: true, + }, + icon_url: { + propDefinition: [ + slack, + "icon_url", + ], + hidden: true, + }, + replyToThread: { + type: "boolean", + label: "Reply to Thread", + description: "Reply to an existing thread", + optional: true, + reloadProps: true, + }, + thread_ts: { + propDefinition: [ + slack, + "messageTs", + ], + description: "Provide another message's `ts` value to make this message a reply (e.g., if triggering on new Slack messages, enter `{{event.ts}}`). Avoid using a reply's `ts` value; use its parent instead. e.g. `1403051575.000407`.", + optional: true, + hidden: true, + }, + thread_broadcast: { + propDefinition: [ + slack, + "thread_broadcast", + ], + hidden: true, + }, + addMessageMetadata: { + type: "boolean", + label: "Add Message Metadata", + description: "Set the metadata event type and payload", + optional: true, + reloadProps: true, + }, + metadata_event_type: { + propDefinition: [ + slack, + "metadata_event_type", + ], + hidden: true, + }, + metadata_event_payload: { + propDefinition: [ + slack, + "metadata_event_payload", + ], + hidden: true, + }, + configureUnfurlSettings: { + type: "boolean", + label: "Configure Unfurl Settings", + description: "Configure settings for unfurling links and media", + optional: true, + reloadProps: true, + }, + unfurl_links: { + propDefinition: [ + slack, + "unfurl_links", + ], + hidden: true, + }, + unfurl_media: { + propDefinition: [ + slack, + "unfurl_media", + ], + hidden: true, + }, + }, + async additionalProps(props) { + if (this.conversation && this.replyToThread) { + props.thread_ts.hidden = false; + props.thread_broadcast.hidden = false; + } + if (this.customizeBotSettings) { + props.username.hidden = false; + props.icon_emoji.hidden = false; + props.icon_url.hidden = false; + } + if (this.addMessageMetadata) { + props.metadata_event_type.hidden = false; + props.metadata_event_payload.hidden = false; + } + if (this.configureUnfurlSettings) { + props.unfurl_links.hidden = false; + props.unfurl_media.hidden = false; + } + return {}; + }, + methods: { + _makeSentViaPipedreamBlock() { + const workflowId = process.env.PIPEDREAM_WORKFLOW_ID; + const baseLink = "https://pipedream.com"; + const linkText = !workflowId + ? "Pipedream Connect" + : "Pipedream"; + + const link = !workflowId + ? `${baseLink}/connect` + : `${baseLink}/@/${workflowId}?o=a&a=slack`; + + return { + "type": "context", + "elements": [ + { + "type": "mrkdwn", + "text": `Sent via <${link}|${linkText}>`, + }, + ], + }; + }, + _makeTextBlock(mrkdwn = true) { + const { text } = this; + let serializedText = text; + // The Slack SDK expects the value of text's "text" property to be a string. If this.text is + // anything other than string, number, or boolean, then encode it as a JSON string. + if (typeof text !== "string" && typeof text !== "number" && typeof text !== "boolean") { + serializedText = JSON.stringify(text); + } + return { + "type": "section", + "text": { + "type": mrkdwn + ? "mrkdwn" + : "plain_text", + "text": serializedText, + }, + }; + }, + getChannelId() { + return this.conversation ?? this.reply_channel; + }, + }, + async run({ $ }) { + let blocks = this.blocks; + + if (!blocks) { + blocks = [ + this._makeTextBlock(this.mrkdwn), + ]; + } else if (typeof blocks === "string") { + blocks = JSON.parse(blocks); + } + + if (this.include_sent_via_pipedream_flag) { + const sentViaPipedreamText = this._makeSentViaPipedreamBlock(); + blocks.push(sentViaPipedreamText); + } + + let metadataEventPayload; + + if (this.metadata_event_type) { + + if (typeof this.metadata_event_payload === "string") { + try { + metadataEventPayload = JSON.parse(this.metadata_event_payload); + } catch (error) { + throw new Error(`Invalid JSON in metadata_event_payload: ${error.message}`); + } + } + + this.metadata = { + event_type: this.metadata_event_type, + event_payload: metadataEventPayload, + }; + } + + const obj = { + text: this.text, + channel: await this.getChannelId(), + attachments: this.attachments, + unfurl_links: this.unfurl_links, + unfurl_media: this.unfurl_media, + parse: this.parse, + as_user: this.as_user, + username: this.username, + icon_emoji: this.icon_emoji, + icon_url: this.icon_url, + mrkdwn: this.mrkdwn, + blocks, + link_names: this.link_names, + reply_broadcast: this.thread_broadcast, + thread_ts: this.thread_ts, + metadata: this.metadata || null, + }; + + if (this.post_at) { + obj.post_at = Math.floor(new Date(this.post_at).getTime() / 1000); + return await this.slack.scheduleMessage(obj); + } + const resp = await this.slack.postChatMessage(obj); + const { channel } = await this.slack.conversationsInfo({ + channel: resp.channel, + }); + let channelName = `#${channel?.name}`; + if (channel.is_im) { + const { profile } = await this.slack.getUserProfile({ + user: channel.user, + }); + channelName = `@${profile.real_name}`; + } else if (channel.is_mpim) { + channelName = `@${channel.purpose.value}`; + } + $.export("$summary", `Successfully sent a message to ${channelName}`); + return resp; + }, +}; diff --git a/components/slack_v2_test/actions/create-channel/create-channel.mjs b/components/slack_v2_test/actions/create-channel/create-channel.mjs new file mode 100644 index 0000000000000..b3e28d5fce400 --- /dev/null +++ b/components/slack_v2_test/actions/create-channel/create-channel.mjs @@ -0,0 +1,35 @@ +import slack from "../../slack_v2_test.app.mjs"; + +export default { + key: "slack_v2_test-create-channel", + name: "Create a Channel", + description: "Create a new channel. [See the documentation](https://api.slack.com/methods/conversations.create)", + version: "0.0.1", + type: "action", + props: { + slack, + channelName: { + label: "Channel name", + description: "Name of the public or private channel to create", + type: "string", + }, + isPrivate: { + label: "Is private?", + type: "boolean", + description: "`false` by default. Pass `true` to create a private channel instead of a public one.", + default: false, + optional: true, + }, + }, + async run({ $ }) { + // parse name + const name = this.channelName.replace(/\s+/g, "-").toLowerCase(); + + const response = await this.slack.createConversations({ + name, + is_private: this.isPrivate, + }); + $.export("$summary", `Successfully created channel ${this.channelName}`); + return response; + }, +}; diff --git a/components/slack_v2_test/actions/create-reminder/create-reminder.mjs b/components/slack_v2_test/actions/create-reminder/create-reminder.mjs new file mode 100644 index 0000000000000..dc0d86881ad62 --- /dev/null +++ b/components/slack_v2_test/actions/create-reminder/create-reminder.mjs @@ -0,0 +1,47 @@ +import slack from "../../slack_v2_test.app.mjs"; + +export default { + key: "slack_v2_test-create-reminder", + name: "Create Reminder", + description: "Create a reminder. [See the documentation](https://api.slack.com/methods/reminders.add)", + version: "0.0.1", + type: "action", + props: { + slack, + text: { + propDefinition: [ + slack, + "text", + ], + }, + timestamp: { + type: "string", + label: "Timestamp", + description: "When this reminder should happen: the Unix timestamp (up to five years from now), the number of seconds until the reminder (if within 24 hours), or a natural language description (Ex. in 15 minutes, or every Thursday)", + }, + team_id: { + propDefinition: [ + slack, + "team", + ], + optional: true, + }, + user: { + propDefinition: [ + slack, + "user", + ], + optional: true, + }, + }, + async run({ $ }) { + const response = await this.slack.addReminders({ + text: this.text, + team_id: this.team_id, + time: this.timestamp, + user: this.user, + }); + $.export("$summary", `Successfully created reminder with ID ${response.reminder.id}`); + return response; + }, +}; diff --git a/components/slack_v2_test/actions/delete-file/delete-file.mjs b/components/slack_v2_test/actions/delete-file/delete-file.mjs new file mode 100644 index 0000000000000..f71de46aa266e --- /dev/null +++ b/components/slack_v2_test/actions/delete-file/delete-file.mjs @@ -0,0 +1,34 @@ +import slack from "../../slack_v2_test.app.mjs"; + +export default { + key: "slack_v2_test-delete-file", + name: "Delete File", + description: "Delete a file. [See the documentation](https://api.slack.com/methods/files.delete)", + version: "0.0.1", + type: "action", + props: { + slack, + conversation: { + propDefinition: [ + slack, + "conversation", + ], + }, + file: { + propDefinition: [ + slack, + "file", + (c) => ({ + channel: c.conversation, + }), + ], + }, + }, + async run({ $ }) { + const response = await this.slack.deleteFiles({ + file: this.file, + }); + $.export("$summary", `Successfully deleted file with ID ${this.file}`); + return response; + }, +}; diff --git a/components/slack_v2_test/actions/delete-message/delete-message.mjs b/components/slack_v2_test/actions/delete-message/delete-message.mjs new file mode 100644 index 0000000000000..896856940d54b --- /dev/null +++ b/components/slack_v2_test/actions/delete-message/delete-message.mjs @@ -0,0 +1,40 @@ +import slack from "../../slack_v2_test.app.mjs"; + +export default { + key: "slack_v2_test-delete-message", + name: "Delete Message", + description: "Delete a message. [See the documentation](https://api.slack.com/methods/chat.delete)", + version: "0.0.1", + type: "action", + props: { + slack, + conversation: { + propDefinition: [ + slack, + "conversation", + ], + }, + timestamp: { + propDefinition: [ + slack, + "messageTs", + ], + }, + as_user: { + propDefinition: [ + slack, + "as_user", + ], + description: "Pass true to update the message as the authed user. Bot users in this context are considered authed users.", + }, + }, + async run({ $ }) { + const response = await this.slack.deleteMessage({ + channel: this.conversation, + ts: this.timestamp, + as_user: this.as_user, + }); + $.export("$summary", "Successfully deleted message."); + return response; + }, +}; diff --git a/components/slack_v2_test/actions/find-message/find-message.mjs b/components/slack_v2_test/actions/find-message/find-message.mjs new file mode 100644 index 0000000000000..3e6d4d3efb165 --- /dev/null +++ b/components/slack_v2_test/actions/find-message/find-message.mjs @@ -0,0 +1,83 @@ +import slack from "../../slack_v2_test.app.mjs"; + +export default { + key: "slack_v2_test-find-message", + name: "Find Message", + description: "Find a Slack message. [See the documentation](https://api.slack.com/methods/search.messages)", + version: "0.0.1", + type: "action", + props: { + slack, + query: { + propDefinition: [ + slack, + "query", + ], + }, + teamId: { + propDefinition: [ + slack, + "team", + ], + optional: true, + }, + maxResults: { + type: "integer", + label: "Max Results", + description: "The maximum number of messages to return", + default: 100, + optional: true, + }, + sort: { + type: "string", + label: "Sort", + description: "Return matches sorted by either `score` or `timestamp`", + options: [ + "score", + "timestamp", + ], + optional: true, + }, + sortDirection: { + type: "string", + label: "Sort Direction", + description: "Sort ascending (asc) or descending (desc)`", + options: [ + "desc", + "asc", + ], + optional: true, + }, + }, + async run({ $ }) { + const matches = []; + const params = { + query: this.query, + team_id: this.teamId, + sort: this.sort, + sort_dir: this.sortDirection, + page: 1, + }; + let hasMore; + + do { + const { messages } = await this.slack.searchMessages(params); + matches.push(...messages.matches); + if (matches.length >= this.maxResults) { + break; + } + hasMore = messages.matches?.length; + params.page++; + } while (hasMore); + + if (matches.length > this.maxResults) { + matches.length = this.maxResults; + } + + $.export("$summary", `Found ${matches.length} matching message${matches.length === 1 + ? "" + : "s"}`); + + return matches; + }, +}; diff --git a/components/slack_v2_test/actions/find-user-by-email/find-user-by-email.mjs b/components/slack_v2_test/actions/find-user-by-email/find-user-by-email.mjs new file mode 100644 index 0000000000000..6043183bdc0c9 --- /dev/null +++ b/components/slack_v2_test/actions/find-user-by-email/find-user-by-email.mjs @@ -0,0 +1,27 @@ +import slack from "../../slack_v2_test.app.mjs"; + +export default { + key: "slack_v2_test-find-user-by-email", + name: "Find User by Email", + description: "Find a user by matching against their email. [See the documentation](https://api.slack.com/methods/users.lookupByEmail)", + version: "0.0.1", + type: "action", + props: { + slack, + email: { + propDefinition: [ + slack, + "email", + ], + }, + }, + async run({ $ }) { + const response = await this.slack.lookupUserByEmail({ + email: this.email, + }); + if (response.ok) { + $.export("$summary", `Successfully found user with ID ${response.user.id}`); + } + return response; + }, +}; diff --git a/components/slack_v2_test/actions/get-file/get-file.mjs b/components/slack_v2_test/actions/get-file/get-file.mjs new file mode 100644 index 0000000000000..015d58b4d6ee6 --- /dev/null +++ b/components/slack_v2_test/actions/get-file/get-file.mjs @@ -0,0 +1,34 @@ +import slack from "../../slack_v2_test.app.mjs"; + +export default { + key: "slack_v2_test-get-file", + name: "Get File", + description: "Return information about a file. [See the documentation](https://api.slack.com/methods/files.info)", + version: "0.0.1", + type: "action", + props: { + slack, + conversation: { + propDefinition: [ + slack, + "conversation", + ], + }, + file: { + propDefinition: [ + slack, + "file", + (c) => ({ + channel: c.conversation, + }), + ], + }, + }, + async run({ $ }) { + const response = await this.slack.getFileInfo({ + file: this.file, + }); + $.export("$summary", `Successfully retrieved file with ID ${this.file}`); + return response; + }, +}; diff --git a/components/slack_v2_test/actions/invite-user-to-channel/invite-user-to-channel.mjs b/components/slack_v2_test/actions/invite-user-to-channel/invite-user-to-channel.mjs new file mode 100644 index 0000000000000..2b6074373c0c7 --- /dev/null +++ b/components/slack_v2_test/actions/invite-user-to-channel/invite-user-to-channel.mjs @@ -0,0 +1,40 @@ +import slack from "../../slack_v2_test.app.mjs"; + +export default { + key: "slack_v2_test-invite-user-to-channel", + name: "Invite User to Channel", + description: "Invite a user to an existing channel. [See the documentation](https://api.slack.com/methods/conversations.invite)", + version: "0.0.1", + type: "action", + props: { + slack, + conversation: { + propDefinition: [ + slack, + "conversation", + ], + }, + user: { + propDefinition: [ + slack, + "user", + ], + }, + }, + async run({ $ }) { + try { + const response = await this.slack.inviteToConversation({ + channel: this.conversation, + users: this.user, + }); + $.export("$summary", `Successfully invited user ${this.user} to channel with ID ${this.conversation}`); + return response; + } catch (error) { + if (error.includes("already_in_channel")) { + $.export("$summary", `The user ${this.user} is already in the channel`); + return; + } + throw error; + } + }, +}; diff --git a/components/slack_v2_test/actions/kick-user/kick-user.mjs b/components/slack_v2_test/actions/kick-user/kick-user.mjs new file mode 100644 index 0000000000000..4d4e87ff6a25d --- /dev/null +++ b/components/slack_v2_test/actions/kick-user/kick-user.mjs @@ -0,0 +1,51 @@ +import slack from "../../slack_v2_test.app.mjs"; +import constants from "../../common/constants.mjs"; + +export default { + key: "slack_v2_test-kick-user", + name: "Kick User", + description: "Remove a user from a conversation. [See the documentation](https://api.slack.com/methods/conversations.kick)", + version: "0.0.1", + type: "action", + props: { + slack, + conversation: { + propDefinition: [ + slack, + "conversation", + () => ({ + types: [ + constants.CHANNEL_TYPE.PUBLIC, + constants.CHANNEL_TYPE.PRIVATE, + constants.CHANNEL_TYPE.MPIM, + ], + }), + ], + }, + user: { + propDefinition: [ + slack, + "user", + (c) => ({ + channelId: c.conversation, + }), + ], + }, + }, + async run({ $ }) { + try { + const response = await this.slack.kickUserFromConversation({ + channel: this.conversation, + user: this.user, + }); + $.export("$summary", `Successfully kicked user ${this.user} from channel with ID ${this.conversation}`); + return response; + } catch (error) { + if (error.includes("not_in_channel")) { + $.export("$summary", `The user ${this.user} is not in the channel`); + return; + } + throw error; + } + }, +}; diff --git a/components/slack_v2_test/actions/list-channels/list-channels.mjs b/components/slack_v2_test/actions/list-channels/list-channels.mjs new file mode 100644 index 0000000000000..131d0ec755152 --- /dev/null +++ b/components/slack_v2_test/actions/list-channels/list-channels.mjs @@ -0,0 +1,47 @@ +import slack from "../../slack_v2_test.app.mjs"; + +export default { + key: "slack_v2_test-list-channels", + name: "List Channels", + description: "Return a list of all channels in a workspace. [See the documentation](https://api.slack.com/methods/conversations.list)", + version: "0.0.1", + type: "action", + props: { + slack, + pageSize: { + propDefinition: [ + slack, + "pageSize", + ], + }, + numPages: { + propDefinition: [ + slack, + "numPages", + ], + }, + }, + async run({ $ }) { + const allChannels = []; + const params = { + limit: this.pageSize, + }; + let page = 0; + + do { + const { + channels, response_metadata: { next_cursor: nextCursor }, + } = await this.slack.conversationsList(params); + allChannels.push(...channels); + params.cursor = nextCursor; + page++; + } while (params.cursor && page < this.numPages); + + $.export("$summary", `Successfully found ${allChannels.length} channel${allChannels.length === 1 + ? "" + : "s"}`); + return { + channels: allChannels, + }; + }, +}; diff --git a/components/slack_v2_test/actions/list-files/list-files.mjs b/components/slack_v2_test/actions/list-files/list-files.mjs new file mode 100644 index 0000000000000..05a4057c10e83 --- /dev/null +++ b/components/slack_v2_test/actions/list-files/list-files.mjs @@ -0,0 +1,68 @@ +import slack from "../../slack_v2_test.app.mjs"; + +export default { + key: "slack_v2_test-list-files", + name: "List Files", + description: "Return a list of files within a team. [See the documentation](https://api.slack.com/methods/files.list)", + version: "0.0.1", + type: "action", + props: { + slack, + conversation: { + propDefinition: [ + slack, + "conversation", + ], + }, + team_id: { + propDefinition: [ + slack, + "team", + ], + optional: true, + }, + user: { + propDefinition: [ + slack, + "user", + ], + optional: true, + }, + pageSize: { + propDefinition: [ + slack, + "pageSize", + ], + }, + numPages: { + propDefinition: [ + slack, + "numPages", + ], + }, + }, + async run({ $ }) { + const allFiles = []; + const params = { + channel: this.conversation, + user: this.user, + team_id: this.team_id, + page: 1, + count: this.pageSize, + }; + let hasMore; + + do { + const { files } = await this.slack.listFiles(params); + allFiles.push(...files); + hasMore = files.length; + params.page++; + } while (hasMore && params.page <= this.numPages); + + $.export("$summary", `Successfully retrieved ${allFiles.length} file${allFiles.length === 1 + ? "" + : "s"}`); + + return allFiles; + }, +}; diff --git a/components/slack_v2_test/actions/list-group-members/list-group-members.mjs b/components/slack_v2_test/actions/list-group-members/list-group-members.mjs new file mode 100644 index 0000000000000..8ed45a9ca396e --- /dev/null +++ b/components/slack_v2_test/actions/list-group-members/list-group-members.mjs @@ -0,0 +1,63 @@ +import slack from "../../slack_v2_test.app.mjs"; + +export default { + key: "slack_v2_test-list-group-members", + name: "List Group Members", + description: "List all users in a User Group. [See the documentation](https://api.slack.com/methods/usergroups.users.list)", + version: "0.0.1", + type: "action", + props: { + slack, + userGroup: { + propDefinition: [ + slack, + "userGroup", + ], + }, + team: { + propDefinition: [ + slack, + "team", + ], + optional: true, + description: "Encoded team id where the user group exists, required if org token is used.", + }, + pageSize: { + propDefinition: [ + slack, + "pageSize", + ], + }, + numPages: { + propDefinition: [ + slack, + "numPages", + ], + }, + }, + async run({ $ }) { + const members = []; + const params = { + usergroup: this.userGroup, + team_id: this.team, + limit: this.pageSize, + }; + let page = 0; + + do { + const { + users, response_metadata: { next_cursor: nextCursor }, + } = await this.slack.listGroupMembers(params); + members.push(...users); + params.cursor = nextCursor; + page++; + } while (params.cursor && page < this.numPages); + + if (members?.length) { + $.export("$summary", `Successfully retrieved ${members.length} user${members.length === 1 + ? "" + : "s"}`); + } + return members; + }, +}; diff --git a/components/slack_v2_test/actions/list-members-in-channel/list-members-in-channel.mjs b/components/slack_v2_test/actions/list-members-in-channel/list-members-in-channel.mjs new file mode 100644 index 0000000000000..70500cc532e59 --- /dev/null +++ b/components/slack_v2_test/actions/list-members-in-channel/list-members-in-channel.mjs @@ -0,0 +1,66 @@ +import slack from "../../slack_v2_test.app.mjs"; + +export default { + key: "slack_v2_test-list-members-in-channel", + name: "List Members in Channel", + description: "Retrieve members of a channel. [See the documentation](https://api.slack.com/methods/conversations.members)", + version: "0.0.1", + type: "action", + props: { + slack, + conversation: { + propDefinition: [ + slack, + "conversation", + ], + }, + returnUsernames: { + type: "boolean", + label: "Return Usernames", + description: "Optionally, return usernames in addition to IDs", + optional: true, + }, + pageSize: { + propDefinition: [ + slack, + "pageSize", + ], + }, + numPages: { + propDefinition: [ + slack, + "numPages", + ], + }, + }, + async run({ $ }) { + let channelMembers = []; + const params = { + channel: this.conversation, + limit: this.pageSize, + }; + let page = 0; + + do { + const { + members, response_metadata: { next_cursor: nextCursor }, + } = await this.slack.listChannelMembers(params); + channelMembers.push(...members); + params.cursor = nextCursor; + page++; + } while (params.cursor && page < this.numPages); + + if (this.returnUsernames) { + const usernames = await this.slack.userNameLookup(channelMembers); + channelMembers = channelMembers?.map((id) => ({ + id, + username: usernames[id], + })) || []; + } + + $.export("$summary", `Successfully retrieved ${channelMembers.length} member${channelMembers.length === 1 + ? "" + : "s"}`); + return channelMembers; + }, +}; diff --git a/components/slack_v2_test/actions/list-replies/list-replies.mjs b/components/slack_v2_test/actions/list-replies/list-replies.mjs new file mode 100644 index 0000000000000..48102a4d0e57b --- /dev/null +++ b/components/slack_v2_test/actions/list-replies/list-replies.mjs @@ -0,0 +1,61 @@ +import slack from "../../slack_v2_test.app.mjs"; + +export default { + key: "slack_v2_test-list-replies", + name: "List Replies", + description: "Retrieve a thread of messages posted to a conversation. [See the documentation](https://api.slack.com/methods/conversations.replies)", + version: "0.0.1", + type: "action", + props: { + slack, + conversation: { + propDefinition: [ + slack, + "conversation", + ], + }, + timestamp: { + propDefinition: [ + slack, + "messageTs", + ], + }, + pageSize: { + propDefinition: [ + slack, + "pageSize", + ], + }, + numPages: { + propDefinition: [ + slack, + "numPages", + ], + }, + }, + async run({ $ }) { + const replies = []; + const params = { + channel: this.conversation, + ts: this.timestamp, + limit: this.pageSize, + }; + let page = 0; + + do { + const { + messages, response_metadata: { next_cursor: nextCursor }, + } = await this.slack.getConversationReplies(params); + replies.push(...messages); + params.cursor = nextCursor; + page++; + } while (params.cursor && page < this.numPages); + + $.export("$summary", `Successfully retrieved ${replies.length} reply message${replies.length === 1 + ? "" + : "s"}`); + return { + messages: replies, + }; + }, +}; diff --git a/components/slack_v2_test/actions/list-users/list-users.mjs b/components/slack_v2_test/actions/list-users/list-users.mjs new file mode 100644 index 0000000000000..bb6ea138f0bf0 --- /dev/null +++ b/components/slack_v2_test/actions/list-users/list-users.mjs @@ -0,0 +1,55 @@ +import slack from "../../slack_v2_test.app.mjs"; + +export default { + key: "slack_v2_test-list-users", + name: "List Users", + description: "Return a list of all users in a workspace. [See the documentation](https://api.slack.com/methods/users.list)", + version: "0.0.1", + type: "action", + props: { + slack, + teamId: { + propDefinition: [ + slack, + "team", + ], + optional: true, + }, + pageSize: { + propDefinition: [ + slack, + "pageSize", + ], + }, + numPages: { + propDefinition: [ + slack, + "numPages", + ], + }, + }, + async run({ $ }) { + const users = []; + const params = { + team_id: this.teamId, + limit: this.pageSize, + }; + let page = 0; + + do { + const { + members, response_metadata: { next_cursor: nextCursor }, + } = await this.slack.usersList(params); + users.push(...members); + params.cursor = nextCursor; + page++; + } while (params.cursor && page < this.numPages); + + $.export("$summary", `Successfully retrieved ${users.length} user${users.length === 1 + ? "" + : "s"}`); + return { + members: users, + }; + }, +}; diff --git a/components/slack_v2_test/actions/reply-to-a-message/reply-to-a-message.mjs b/components/slack_v2_test/actions/reply-to-a-message/reply-to-a-message.mjs new file mode 100644 index 0000000000000..ea0fc62e40c3c --- /dev/null +++ b/components/slack_v2_test/actions/reply-to-a-message/reply-to-a-message.mjs @@ -0,0 +1,49 @@ +import slack from "../../slack_v2_test.app.mjs"; +import common from "../common/send-message.mjs"; + +export default { + ...common, + key: "slack_v2_test-reply-to-a-message", + name: "Reply to a Message Thread", + description: "Send a message as a threaded reply. See [postMessage](https://api.slack.com/methods/chat.postMessage) or [scheduleMessage](https://api.slack.com/methods/chat.scheduleMessage) docs here", + version: "0.1.1", + type: "action", + props: { + slack: common.props.slack, + conversation: { + propDefinition: [ + slack, + "conversation", + ], + }, + text: { + propDefinition: [ + slack, + "text", + ], + }, + mrkdwn: { + propDefinition: [ + slack, + "mrkdwn", + ], + }, + ...common.props, + replyToThread: { + ...common.props.replyToThread, + hidden: true, + }, + thread_ts: { + propDefinition: [ + slack, + "messageTs", + ], + }, + thread_broadcast: { + propDefinition: [ + slack, + "thread_broadcast", + ], + }, + }, +}; diff --git a/components/slack_v2_test/actions/send-block-kit-message/send-block-kit-message.mjs b/components/slack_v2_test/actions/send-block-kit-message/send-block-kit-message.mjs new file mode 100644 index 0000000000000..a1558a0fbdbad --- /dev/null +++ b/components/slack_v2_test/actions/send-block-kit-message/send-block-kit-message.mjs @@ -0,0 +1,43 @@ +import buildBlocks from "../common/build-blocks.mjs"; +import common from "../common/send-message.mjs"; + +export default { + ...common, + ...buildBlocks, + key: "slack_v2_test-send-block-kit-message", + name: "Build and Send a Block Kit Message", + description: "Configure custom blocks and send to a channel, group, or user. [See the documentation](https://api.slack.com/tools/block-kit-builder).", + version: "0.5.1", + type: "action", + props: { + slack: common.props.slack, + conversation: { + propDefinition: [ + common.props.slack, + "conversation", + ], + }, + text: { + type: "string", + label: "Notification Text", + description: "Optionally provide a string for Slack to display as the new message notification (if you do not provide this, notification will be blank).", + optional: true, + }, + ...common.props, + ...buildBlocks.props, + }, + methods: { + ...common.methods, + ...buildBlocks.methods, + async getGeneratedBlocks() { + return await buildBlocks.run.call(this); // call buildBlocks.run with the current context + }, + }, + async run({ $ }) { + this.blocks = await this.getGeneratedBlocks(); // set the blocks prop for common.run to use + const resp = await common.run.call(this, { + $, + }); // call common.run with the current context + return resp; + }, +}; diff --git a/components/slack_v2_test/actions/send-large-message/send-large-message.mjs b/components/slack_v2_test/actions/send-large-message/send-large-message.mjs new file mode 100644 index 0000000000000..31d391f59bf1a --- /dev/null +++ b/components/slack_v2_test/actions/send-large-message/send-large-message.mjs @@ -0,0 +1,93 @@ +import common from "../common/send-message.mjs"; + +export default { + ...common, + key: "slack_v2_test-send-large-message", + name: "Send a Large Message (3000+ characters)", + description: "Send a large message (more than 3000 characters) to a channel, group or user. See [postMessage](https://api.slack.com/methods/chat.postMessage) or [scheduleMessage](https://api.slack.com/methods/chat.scheduleMessage) docs here", + version: "0.1.1", + type: "action", + props: { + slack: common.props.slack, + conversation: { + propDefinition: [ + common.props.slack, + "conversation", + ], + }, + text: { + propDefinition: [ + common.props.slack, + "text", + ], + }, + mrkdwn: { + propDefinition: [ + common.props.slack, + "mrkdwn", + ], + }, + ...common.props, + }, + async run({ $ }) { + if (this.include_sent_via_pipedream_flag) { + const sentViaPipedreamText = this._makeSentViaPipedreamBlock(); + this.text += `\n\n\n${sentViaPipedreamText.elements[0].text}`; + } + + let metadataEventPayload; + + if (this.metadata_event_type) { + if (typeof this.metadata_event_payload === "string") { + try { + metadataEventPayload = JSON.parse(this.metadata_event_payload); + } catch (error) { + throw new Error(`Invalid JSON in metadata_event_payload: ${error.message}`); + } + } + + this.metadata = { + event_type: this.metadata_event_type, + event_payload: metadataEventPayload, + }; + } + + const obj = { + text: this.text, + channel: this.conversation, + as_user: this.as_user, + username: this.username, + icon_emoji: this.icon_emoji, + icon_url: this.icon_url, + mrkdwn: this.mrkdwn, + metadata: this.metadata || null, + reply_broadcast: this.thread_broadcast, + thread_ts: this.thread_ts, + unfurl_links: this.unfurl_links, + unfurl_media: this.unfurl_media, + }; + + let response; + if (this.post_at) { + obj.post_at = this.post_at; + response = await this.slack.scheduleMessage(obj); + } else { + response = await this.slack.postChatMessage(obj); + } + + const { channel } = await this.slack.conversationsInfo({ + channel: response.channel, + }); + let channelName = `#${channel?.name}`; + if (channel.is_im) { + const { profile } = await this.slack.getUserProfile({ + user: channel.user, + }); + channelName = `@${profile.real_name}`; + } else if (channel.is_mpim) { + channelName = `@${channel.purpose.value}`; + } + $.export("$summary", `Successfully sent a message to ${channelName}`); + return response; + }, +}; diff --git a/components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs b/components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs new file mode 100644 index 0000000000000..39bd8871f7fd7 --- /dev/null +++ b/components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs @@ -0,0 +1,70 @@ +import common from "../common/send-message.mjs"; +import buildBlocks from "../common/build-blocks.mjs"; + +export default { + ...common, + ...buildBlocks, + key: "slack_v2_test-send-message-advanced", + name: "Send Message (Advanced)", + description: "Customize advanced setttings and send a message to a channel, group or user. See [postMessage](https://api.slack.com/methods/chat.postMessage) or [scheduleMessage](https://api.slack.com/methods/chat.scheduleMessage) docs here", + version: "0.1.1", + type: "action", + props: { + slack: common.props.slack, + conversation: { + propDefinition: [ + common.props.slack, + "conversation", + ], + }, + text: { + propDefinition: [ + common.props.slack, + "text", + ], + description: "If you're using `blocks`, this is used as a fallback string to display in notifications. If you aren't, this is the main body text of the message. It can be formatted as plain text, or with mrkdwn.", + }, + mrkdwn: { + propDefinition: [ + common.props.slack, + "mrkdwn", + ], + }, + attachments: { + propDefinition: [ + common.props.slack, + "attachments", + ], + }, + parse: { + propDefinition: [ + common.props.slack, + "parse", + ], + }, + link_names: { + propDefinition: [ + common.props.slack, + "link_names", + ], + }, + ...common.props, + ...buildBlocks.props, + }, + methods: { + ...common.methods, + ...buildBlocks.methods, + async getGeneratedBlocks() { + return await buildBlocks.run.call(this); // call buildBlocks.run with the current context + }, + }, + async run({ $ }) { + if (this.passArrayOrConfigure) { + this.blocks = await this.getGeneratedBlocks(); // set the blocks prop for common.run to use + } + const resp = await common.run.call(this, { + $, + }); // call common.run with the current context + return resp; + }, +}; diff --git a/components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs b/components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs new file mode 100644 index 0000000000000..2b538d64d4f83 --- /dev/null +++ b/components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs @@ -0,0 +1,40 @@ +import common from "../common/send-message.mjs"; +import constants from "../../common/constants.mjs"; + +export default { + ...common, + key: "slack_v2_test-send-message-to-channel", + name: "Send Message to Channel", + description: "Send a message to a public or private channel. [See the documentation](https://api.slack.com/methods/chat.postMessage)", + version: "0.1.1", + type: "action", + props: { + slack: common.props.slack, + conversation: { + propDefinition: [ + common.props.slack, + "conversation", + () => ({ + types: [ + constants.CHANNEL_TYPE.PUBLIC, + constants.CHANNEL_TYPE.PRIVATE, + ], + }), + ], + description: "Select a public or private channel", + }, + text: { + propDefinition: [ + common.props.slack, + "text", + ], + }, + mrkdwn: { + propDefinition: [ + common.props.slack, + "mrkdwn", + ], + }, + ...common.props, + }, +}; diff --git a/components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs b/components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs new file mode 100644 index 0000000000000..a9425a0bb6dd4 --- /dev/null +++ b/components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs @@ -0,0 +1,73 @@ +import common from "../common/send-message.mjs"; +import constants from "../../common/constants.mjs"; +import { ConfigurationError } from "@pipedream/platform"; + +export default { + ...common, + key: "slack_v2_test-send-message-to-user-or-group", + name: "Send Message to User or Group", + description: "Send a message to a user or group. [See the documentation](https://api.slack.com/methods/chat.postMessage)", + version: "0.1.1", + type: "action", + props: { + slack: common.props.slack, + users: { + propDefinition: [ + common.props.slack, + "user", + ], + type: "string[]", + label: "Users", + description: "Select the user(s) to message", + optional: true, + }, + conversation: { + propDefinition: [ + common.props.slack, + "conversation", + () => ({ + types: [ + constants.CHANNEL_TYPE.MPIM, + ], + }), + ], + description: "Select the group to message", + optional: true, + }, + text: { + propDefinition: [ + common.props.slack, + "text", + ], + }, + mrkdwn: { + propDefinition: [ + common.props.slack, + "mrkdwn", + ], + }, + ...common.props, + }, + methods: { + ...common.methods, + openConversation(args = {}) { + return this.slack.makeRequest({ + method: "conversations.open", + ...args, + }); + }, + async getChannelId() { + if (!this.conversation && !this.users?.length) { + throw new ConfigurationError("Must select a group or user(s) to message"); + } + + if (this.conversation) { + return this.conversation; + } + const { channel: { id } } = await this.openConversation({ + users: this.users.join(), + }); + return id; + }, + }, +}; diff --git a/components/slack_v2_test/actions/send-message/send-message.mjs b/components/slack_v2_test/actions/send-message/send-message.mjs new file mode 100644 index 0000000000000..1250fa6042e37 --- /dev/null +++ b/components/slack_v2_test/actions/send-message/send-message.mjs @@ -0,0 +1,51 @@ +import common from "../common/send-message.mjs"; +import constants from "../../common/constants.mjs"; + +export default { + ...common, + key: "slack_v2_test-send-message", + name: "Send Message", + description: "Send a message to a user, group, private channel or public channel. [See the documentation](https://api.slack.com/methods/chat.postMessage)", + version: "0.1.1", + type: "action", + props: { + slack: common.props.slack, + channelType: { + type: "string", + label: "Channel Type", + description: "The type of channel to send to. User/Direct Message (im), Group (mpim), Private Channel or Public Channel", + async options() { + return constants.CHANNEL_TYPE_OPTIONS; + }, + }, + conversation: { + propDefinition: [ + common.props.slack, + "conversation", + (c) => ({ + types: c.channelType === "Channels" + ? [ + constants.CHANNEL_TYPE.PUBLIC, + constants.CHANNEL_TYPE.PRIVATE, + ] + : [ + c.channelType, + ], + }), + ], + }, + text: { + propDefinition: [ + common.props.slack, + "text", + ], + }, + mrkdwn: { + propDefinition: [ + common.props.slack, + "mrkdwn", + ], + }, + ...common.props, + }, +}; diff --git a/components/slack_v2_test/actions/set-channel-description/set-channel-description.mjs b/components/slack_v2_test/actions/set-channel-description/set-channel-description.mjs new file mode 100644 index 0000000000000..8a4bbb793b73a --- /dev/null +++ b/components/slack_v2_test/actions/set-channel-description/set-channel-description.mjs @@ -0,0 +1,32 @@ +import slack from "../../slack_v2_test.app.mjs"; + +export default { + key: "slack_v2_test-set-channel-description", + name: "Set Channel Description", + description: "Change the description or purpose of a channel. [See the documentation](https://api.slack.com/methods/conversations.setPurpose)", + version: "0.0.1", + type: "action", + props: { + slack, + conversation: { + propDefinition: [ + slack, + "conversation", + ], + }, + purpose: { + propDefinition: [ + slack, + "purpose", + ], + }, + }, + async run({ $ }) { + const response = await this.slack.setChannelDescription({ + channel: this.conversation, + purpose: this.purpose, + }); + $.export("$summary", `Successfully set description for channel with ID ${this.conversation}`); + return response; + }, +}; diff --git a/components/slack_v2_test/actions/set-channel-topic/set-channel-topic.mjs b/components/slack_v2_test/actions/set-channel-topic/set-channel-topic.mjs new file mode 100644 index 0000000000000..f2b0f43b9afc0 --- /dev/null +++ b/components/slack_v2_test/actions/set-channel-topic/set-channel-topic.mjs @@ -0,0 +1,32 @@ +import slack from "../../slack_v2_test.app.mjs"; + +export default { + key: "slack_v2_test-set-channel-topic", + name: "Set Channel Topic", + description: "Set the topic on a selected channel. [See the documentation](https://api.slack.com/methods/conversations.setTopic)", + version: "0.0.1", + type: "action", + props: { + slack, + conversation: { + propDefinition: [ + slack, + "conversation", + ], + }, + topic: { + propDefinition: [ + slack, + "topic", + ], + }, + }, + async run({ $ }) { + const response = await this.slack.setChannelTopic({ + channel: this.conversation, + topic: this.topic, + }); + $.export("$summary", `Successfully set topic for channel with ID ${this.conversation}`); + return response; + }, +}; diff --git a/components/slack_v2_test/actions/set-status/set-status.mjs b/components/slack_v2_test/actions/set-status/set-status.mjs new file mode 100644 index 0000000000000..9bb3dcbf358d2 --- /dev/null +++ b/components/slack_v2_test/actions/set-status/set-status.mjs @@ -0,0 +1,44 @@ +import slack from "../../slack_v2_test.app.mjs"; + +export default { + key: "slack_v2_test-set-status", + name: "Set Status", + description: "Set the current status for a user. [See the documentation](https://api.slack.com/methods/users.profile.set)", + version: "0.0.1", + type: "action", + props: { + slack, + statusText: { + type: "string", + label: "Status Text", + description: "The displayed text", + }, + statusEmoji: { + propDefinition: [ + slack, + "icon_emoji", + ], + label: "Status Emoji", + description: "The emoji to display with the status", + optional: true, + }, + statusExpiration: { + type: "string", + label: "Status Expiration", + description: "The datetime of when the status will expire in ISO 8601 format. (Example: `2014-01-01T00:00:00Z`)", + optional: true, + }, + }, + async run({ $ }) { + const response = await this.slack.updateProfile({ + profile: { + status_text: this.statusText, + status_emoji: this.statusEmoji && `:${this.statusEmoji}:`, + status_expiration: this.statusExpiration + && Math.floor(new Date(this.statusExpiration).getTime() / 1000), + }, + }); + $.export("$summary", "Successfully updated status."); + return response; + }, +}; diff --git a/components/slack_v2_test/actions/update-group-members/update-group-members.mjs b/components/slack_v2_test/actions/update-group-members/update-group-members.mjs new file mode 100644 index 0000000000000..bfe361f52ee64 --- /dev/null +++ b/components/slack_v2_test/actions/update-group-members/update-group-members.mjs @@ -0,0 +1,67 @@ +import slack from "../../slack_v2_test.app.mjs"; + +export default { + key: "slack_v2_test-update-group-members", + name: "Update Groups Members", + description: "Update the list of users for a User Group. [See the documentation](https://api.slack.com/methods/usergroups.users.update)", + version: "0.0.1", + type: "action", + props: { + slack, + userGroup: { + propDefinition: [ + slack, + "userGroup", + ], + }, + usersToAdd: { + propDefinition: [ + slack, + "user", + ], + type: "string[]", + label: "Users to Add", + description: "A list of encoded user IDs that represent the users to add to the group.", + optional: true, + }, + usersToRemove: { + propDefinition: [ + slack, + "user", + ], + type: "string[]", + label: "Users to Remove", + description: "A list of encoded user IDs that represent the users to remove from the group.", + optional: true, + }, + team: { + propDefinition: [ + slack, + "team", + ], + optional: true, + description: "Encoded team id where the user group exists, required if org token is used.", + }, + }, + async run({ $ }) { + const { + userGroup, + usersToAdd, + usersToRemove, + team, + } = this; + let { users } = await this.slack.listGroupMembers({ + usergroup: userGroup, + team_id: team, + }); + users = users.filter((user) => !usersToRemove.includes(user)); + users.push(...usersToAdd); + const response = await this.slack.updateGroupMembers({ + usergroup: userGroup, + users, + team_id: team, + }); + $.export("$summary", `Successfully updated members of group with ID ${this.userGroup}`); + return response; + }, +}; diff --git a/components/slack_v2_test/actions/update-message/update-message.mjs b/components/slack_v2_test/actions/update-message/update-message.mjs new file mode 100644 index 0000000000000..234131b1c3cbc --- /dev/null +++ b/components/slack_v2_test/actions/update-message/update-message.mjs @@ -0,0 +1,54 @@ +import slack from "../../slack_v2_test.app.mjs"; + +export default { + key: "slack_v2_test-update-message", + name: "Update Message", + description: "Update a message. [See the documentation](https://api.slack.com/methods/chat.update)", + version: "0.1.1", + type: "action", + props: { + slack, + conversation: { + propDefinition: [ + slack, + "conversation", + ], + }, + timestamp: { + propDefinition: [ + slack, + "messageTs", + ], + }, + text: { + propDefinition: [ + slack, + "text", + ], + }, + as_user: { + propDefinition: [ + slack, + "as_user", + ], + description: "Pass true to update the message as the authed user. Bot users in this context are considered authed users.", + }, + attachments: { + propDefinition: [ + slack, + "attachments", + ], + }, + }, + async run({ $ }) { + const response = await this.slack.updateMessage({ + ts: this.timestamp, + text: this.text, + channel: this.conversation, + as_user: this.as_user, + attachments: this.attachments, + }); + $.export("$summary", "Successfully updated message"); + return response; + }, +}; diff --git a/components/slack_v2_test/actions/update-profile/update-profile.mjs b/components/slack_v2_test/actions/update-profile/update-profile.mjs new file mode 100644 index 0000000000000..36b7eee62cad3 --- /dev/null +++ b/components/slack_v2_test/actions/update-profile/update-profile.mjs @@ -0,0 +1,87 @@ +import slack from "../../slack_v2_test.app.mjs"; +import { ConfigurationError } from "@pipedream/platform"; + +export default { + key: "slack_v2_test-update-profile", + name: "Update Profile", + description: "Update basic profile field such as name or title. [See the documentation](https://api.slack.com/methods/users.profile.set)", + version: "0.0.1", + type: "action", + props: { + slack, + displayName: { + type: "string", + label: "Display Name", + description: "The display name the user has chosen to identify themselves by in their workspace profile", + optional: true, + }, + firstName: { + type: "string", + label: "First Name", + description: "The user's first name", + optional: true, + }, + lastName: { + type: "string", + label: "Last Name", + description: "The user's last name", + optional: true, + }, + phone: { + type: "string", + label: "Phone", + description: "The user's phone number, in any format", + optional: true, + }, + pronouns: { + type: "string", + label: "Pronouns", + description: "The user's pronouns", + optional: true, + }, + title: { + type: "string", + label: "Title", + description: "The user's title", + optional: true, + }, + email: { + type: "string", + label: "Email", + description: "The user's email address. You cannot update your own email using this method. This field can only be changed by admins for users on paid teams.", + optional: true, + }, + user: { + propDefinition: [ + slack, + "user", + ], + description: "ID of user to change. This argument may only be specified by admins on paid teams.", + optional: true, + }, + }, + async run({ $ }) { + if (!this.displayName + && !this.firstName + && !this.lastName + && !this.phone + && !this.pronouns + && !this.title + ) { + throw new ConfigurationError("Please provide at least one value to update"); + } + const response = await this.slack.updateProfile({ + profile: { + display_name: this.displayName, + first_name: this.firstName, + last_name: this.lastName, + phone: this.phone, + pronouns: this.pronouns, + title: this.title, + }, + user: this.user, + }); + $.export("$summary", "Successfully updated profile"); + return response; + }, +}; diff --git a/components/slack_v2_test/actions/upload-file/upload-file.mjs b/components/slack_v2_test/actions/upload-file/upload-file.mjs new file mode 100644 index 0000000000000..76f836792edba --- /dev/null +++ b/components/slack_v2_test/actions/upload-file/upload-file.mjs @@ -0,0 +1,100 @@ +import { + ConfigurationError, axios, getFileStreamAndMetadata, +} from "@pipedream/platform"; +import FormData from "form-data"; +import slack from "../../slack_v2_test.app.mjs"; + +export default { + key: "slack_v2_test-upload-file", + name: "Upload File", + description: "Upload a file. [See the documentation](https://api.slack.com/messaging/files#uploading_files)", + version: "0.1.1", + type: "action", + props: { + slack, + conversation: { + propDefinition: [ + slack, + "conversation", + ], + }, + content: { + propDefinition: [ + slack, + "content", + ], + }, + initialComment: { + description: "Will be added as an initial comment before the image", + propDefinition: [ + slack, + "initial_comment", + ], + optional: true, + }, + syncDir: { + type: "dir", + accessMode: "read", + sync: true, + optional: true, + }, + }, + async run({ $ }) { + const { + stream, metadata, + } = await getFileStreamAndMetadata(this.content); + + const filename = this.content.split("/").pop(); + + // Get an upload URL from Slack + const getUploadUrlResponse = await this.slack.getUploadUrl({ + filename, + length: metadata.size, + }); + + if (!getUploadUrlResponse.ok) { + throw new ConfigurationError(`Error getting upload URL: ${JSON.stringify(getUploadUrlResponse)}`); + } + + const { + upload_url: uploadUrl, file_id: fileId, + } = getUploadUrlResponse; + + // Upload the file to the provided URL + const formData = new FormData(); + formData.append("file", stream, { + contentType: metadata.contentType, + knownLength: metadata.size, + filename: metadata.name, + }); + formData.append("filename", filename); + + await axios($, { + url: uploadUrl, + data: formData, + method: "POST", + headers: { + ...formData.getHeaders(), + Authorization: `Bearer ${this.slack.getToken()}`, + }, + }); + + // Complete the file upload process in Slack + const completeUploadResponse = await this.slack.completeUpload({ + channel_id: this.conversation, + initial_comment: this.initialComment, + files: [ + { + id: fileId, + }, + ], + }); + + if (!completeUploadResponse.ok) { + throw new Error(`Error completing upload: ${JSON.stringify(completeUploadResponse)}`); + } + + $.export("$summary", "Successfully uploaded file"); + return completeUploadResponse; + }, +}; diff --git a/components/slack_v2_test/actions/verify-slack-signature/verify-slack-signature.mjs b/components/slack_v2_test/actions/verify-slack-signature/verify-slack-signature.mjs new file mode 100644 index 0000000000000..a7588c4b06c03 --- /dev/null +++ b/components/slack_v2_test/actions/verify-slack-signature/verify-slack-signature.mjs @@ -0,0 +1,60 @@ +import crypto from "crypto"; +import slack from "../../slack_v2_test.app.mjs"; + +export default { + key: "slack_v2_test-verify-slack-signature", + name: "Verify Slack Signature", + description: "Verifying requests from Slack, slack signs its requests using a secret that's unique to your app. [See the documentation](https://api.slack.com/authentication/verifying-requests-from-slack)", + version: "0.0.1", + type: "action", + props: { + slack, + slackSigningSecret: { + type: "string", + label: "Signing Secret", + description: "Slack [Signing Secret](https://api.slack.com/authentication/verifying-requests-from-slack#:~:text=Slack%20Signing%20Secret%2C%20available%20in%20the%20app%20admin%20panel%20under%20Basic%20Info.), available in the app admin panel under Basic Info.", + secret: true, + }, + slackSignature: { + type: "string", + label: "X-Slack-Signature", + description: "Slack signature (from X-Slack-Signature header).", + }, + slackRequestTimestamp: { + type: "string", + label: "X-Slack-Request-Timestamp", + description: "Slack request timestamp (from X-Slack-Request-Timestamp header).", + }, + requestBody: { + type: "any", + label: "Request Body", + description: "The body of the request to be verified.", + }, + }, + async run({ $ }) { + const { + slackSignature, + slackRequestTimestamp, + requestBody, + slackSigningSecret, + } = this; + const requestBodyStr = typeof (requestBody) === "string" ? + requestBody : + JSON.stringify(requestBody); + const sigBaseString = `v0:${slackRequestTimestamp}:${requestBodyStr}`; + const sha256Hex = crypto.createHmac("sha256", slackSigningSecret) + .update(sigBaseString, "utf8") + .digest("hex"); + const mySignature = `v0=${sha256Hex}`; + if (crypto.timingSafeEqual(Buffer.from(mySignature, "utf8"), Buffer.from(slackSignature, "utf8"))) { + $.export("$summary", `Successfully verified the request with "${slackSignature}" signature`); + return { + success: true, + }; + } + $.export("$summary", "Slack signature mismatch with provided properties, it may be a configuration issue."); + return { + success: false, + }; + }, +}; diff --git a/components/slack_v2_test/common/constants.mjs b/components/slack_v2_test/common/constants.mjs new file mode 100644 index 0000000000000..7d64f48d573d7 --- /dev/null +++ b/components/slack_v2_test/common/constants.mjs @@ -0,0 +1,31 @@ +const MAX_RESOURCES = 800; +const LIMIT = 250; + +const CHANNEL_TYPE = { + PUBLIC: "public_channel", + PRIVATE: "private_channel", + MPIM: "mpim", + IM: "im", +}; + +const CHANNEL_TYPE_OPTIONS = [ + { + label: "Channels", + value: "Channels", + }, + { + label: "Group", + value: CHANNEL_TYPE.MPIM, + }, + { + label: "User / Direct Message", + value: CHANNEL_TYPE.IM, + }, +]; + +export default { + MAX_RESOURCES, + LIMIT, + CHANNEL_TYPE, + CHANNEL_TYPE_OPTIONS, +}; diff --git a/components/slack_v2_test/package.json b/components/slack_v2_test/package.json new file mode 100644 index 0000000000000..aa112b7289df7 --- /dev/null +++ b/components/slack_v2_test/package.json @@ -0,0 +1,22 @@ +{ + "name": "@pipedream/slack_v2_test", + "version": "0.10.2", + "description": "Pipedream Slack v2 Test Components", + "main": "slack_v2_test.app.mjs", + "keywords": [ + "pipedream", + "slack_v2_test" + ], + "homepage": "https://pipedream.com/apps/slack_v2_test", + "author": "Pipedream (https://pipedream.com/)", + "gitHead": "e12480b94cc03bed4808ebc6b13e7fdb3a1ba535", + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.1.0", + "@slack/web-api": "^7.9.0", + "async-retry": "^1.3.3", + "lodash": "^4.17.21" + } +} diff --git a/components/slack_v2_test/slack_v2_test.app.mjs b/components/slack_v2_test/slack_v2_test.app.mjs new file mode 100644 index 0000000000000..7e72734739261 --- /dev/null +++ b/components/slack_v2_test/slack_v2_test.app.mjs @@ -0,0 +1,967 @@ +import { WebClient } from "@slack/web-api"; +import constants from "./common/constants.mjs"; +import get from "lodash/get.js"; +import retry from "async-retry"; +import { ConfigurationError } from "@pipedream/platform"; + +export default { + type: "app", + app: "slack_v2_test", + propDefinitions: { + user: { + type: "string", + label: "User", + description: "Select a user", + async options({ + prevContext, channelId, + }) { + const types = [ + "im", + ]; + let conversationsResp + = await this.availableConversations(types.join(), prevContext.cursor, true); + if (channelId) { + const { members } = await this.listChannelMembers({ + channel: channelId, + throwRateLimitError: true, + }); + conversationsResp.conversations = conversationsResp.conversations + .filter((c) => members.includes(c.user || c.id)); + } + const userIds = conversationsResp.conversations.map(({ user }) => user); + const userNames = await this.userNameLookup(userIds); + return { + options: conversationsResp.conversations.map((c) => ({ + label: `@${userNames[c.user]}`, + value: c.user || c.id, + })), + context: { + cursor: conversationsResp.cursor, + }, + }; + }, + }, + group: { + type: "string", + label: "Group", + description: "Select a group", + async options({ prevContext }) { + let { cursor } = prevContext; + const types = [ + "mpim", + ]; + const resp = await this.availableConversations(types.join(), cursor, true); + return { + options: resp.conversations.map((c) => { + return { + label: c.purpose.value, + value: c.id, + }; + }), + context: { + cursor: resp.cursor, + }, + }; + }, + }, + userGroup: { + type: "string", + label: "User Group", + description: "The encoded ID of the User Group.", + async options() { + const { usergroups } = await this.usergroupsList({ + throwRateLimitError: true, + }); + return usergroups.map((c) => ({ + label: c.name, + value: c.id, + })); + }, + }, + reminder: { + type: "string", + label: "Reminder", + description: "Select a reminder", + async options() { + const { reminders } = await this.remindersList({ + throwRateLimitError: true, + }); + return reminders.map((c) => ({ + label: c.text, + value: c.id, + })); + }, + }, + conversation: { + type: "string", + label: "Channel", + description: "Select a public or private channel, or a user or group", + async options({ + prevContext, types, + }) { + let { cursor } = prevContext; + if (prevContext?.types) { + types = prevContext.types; + } + if (types == null) { + const { response_metadata: { scopes } } = await this.authTest({ + throwRateLimitError: true, + }); + types = [ + "public_channel", + ]; + if (scopes.includes("groups:read")) { + types.push("private_channel"); + } + if (scopes.includes("mpim:read")) { + types.push("mpim"); + } + if (scopes.includes("im:read")) { + types.push("im"); + } + } + const conversationsResp = await this.availableConversations(types.join(), cursor, true); + let conversations, userNames; + if (types.includes("im")) { + conversations = conversationsResp.conversations; + const userIds = conversations.map(({ user }) => user); + userNames = await this.userNameLookup(userIds); + } else { + conversations = conversationsResp.conversations.filter((c) => !c.is_im); + } + return { + options: conversations.map((c) => { + if (c.is_im) { + return { + label: `Direct messaging with: @${userNames[c.user]}`, + value: c.id, + }; + } else if (c.is_mpim) { + return { + label: c.purpose.value, + value: c.id, + }; + } else { + return { + label: `${c.is_private + ? "Private" + : "Public"} channel: ${c.name}`, + value: c.id, + }; + } + }), + context: { + types, + cursor: conversationsResp.cursor, + }, + }; + }, + }, + channelId: { + type: "string", + label: "Channel ID", + description: "Select the channel's id.", + async options({ + prevContext, + types = Object.values(constants.CHANNEL_TYPE), + channelsFilter = (channel) => channel, + excludeArchived = true, + }) { + const { + channels, + response_metadata: { next_cursor: cursor }, + } = await this.conversationsList({ + types: types.join(), + cursor: prevContext.cursor, + limit: constants.LIMIT, + exclude_archived: excludeArchived, + throwRateLimitError: true, + }); + + let userNames; + if (types.includes("im")) { + const userIds = channels.filter(({ is_im }) => is_im).map(({ user }) => user); + userNames = await this.userNameLookup(userIds); + } + + const options = channels + .filter(channelsFilter) + .map((c) => { + if (c.is_im) { + return { + label: `Direct messaging with: @${userNames[c.user]}`, + value: c.id, + }; + } else if (c.is_mpim) { + return { + label: c.purpose.value, + value: c.id, + }; + } else { + return { + label: `${c.is_private + ? "Private" + : "Public"} channel: ${c.name}`, + value: c.id, + }; + } + }); + + return { + options, + context: { + cursor, + }, + }; + }, + }, + team: { + type: "string", + label: "Team", + description: "Select a team.", + async options({ prevContext }) { + const { + teams, + response_metadata: { next_cursor: cursor }, + } = await this.authTeamsList({ + cursor: prevContext.cursor, + limit: constants.LIMIT, + throwRateLimitError: true, + }); + + return { + options: teams.map((team) => ({ + label: team.name, + value: team.id, + })), + + context: { + cursor, + }, + }; + }, + }, + messageTs: { + type: "string", + label: "Message Timestamp", + description: "Timestamp of a message. e.g. `1403051575.000407`.", + }, + text: { + type: "string", + label: "Text", + description: "Text of the message to send (see Slack's [formatting docs](https://api.slack.com/reference/surfaces/formatting)). This field is usually necessary, unless you're providing only attachments instead.", + }, + topic: { + type: "string", + label: "Topic", + description: "Text of the new channel topic.", + }, + purpose: { + type: "string", + label: "Purpose", + description: "Text of the new channel purpose.", + }, + query: { + type: "string", + label: "Query", + description: "Search query.", + }, + file: { + type: "string", + label: "File ID", + description: "Specify a file by providing its ID.", + async options({ + channel, page, + }) { + const { files } = await this.listFiles({ + channel, + page: page + 1, + count: constants.LIMIT, + throwRateLimitError: true, + }); + return files?.map(({ + id: value, name: label, + }) => ({ + value, + label, + })) || []; + }, + }, + attachments: { + type: "string", + label: "Attachments", + description: "A JSON-based array of structured attachments, presented as a URL-encoded string (e.g., `[{\"pretext\": \"pre-hello\", \"text\": \"text-world\"}]`).", + optional: true, + }, + unfurl_links: { + type: "boolean", + label: "Unfurl Links", + description: "Default to `false`. Pass `true` to enable unfurling of links.", + default: false, + optional: true, + }, + unfurl_media: { + type: "boolean", + label: "Unfurl Media", + description: "Defaults to `false`. Pass `true` to enable unfurling of media content.", + default: false, + optional: true, + }, + parse: { + type: "string", + label: "Parse", + description: "Change how messages are treated. Defaults to none. By default, URLs will be hyperlinked. Set `parse` to `none` to remove the hyperlinks. The behavior of `parse` is different for text formatted with `mrkdwn`. By default, or when `parse` is set to `none`, `mrkdwn` formatting is implemented. To ignore `mrkdwn` formatting, set `parse` to full.", + optional: true, + }, + as_user: { + type: "boolean", + label: "Send as User", + description: "Optionally pass `true` to post the message as the authenticated user, instead of as a bot. Defaults to `false`.", + default: false, + optional: true, + }, + mrkdwn: { + label: "Send text as Slack mrkdwn", + type: "boolean", + description: "`true` by default. Pass `false` to disable Slack markup parsing. [See docs here](https://api.slack.com/reference/surfaces/formatting)", + default: true, + optional: true, + }, + post_at: { + label: "Schedule message", + description: "Messages can only be scheduled up to 120 days in advance, and cannot be scheduled for the past. The datetime should be in ISO 8601 format. (Example: `2014-01-01T00:00:00Z`)", + type: "string", + optional: true, + }, + username: { + type: "string", + label: "Bot Username", + description: "Optionally customize your bot's user name (default is `Pipedream`). Must be used in conjunction with `Send as User` set to false, otherwise ignored.", + optional: true, + }, + blocks: { + type: "string", + label: "Blocks", + description: "Enter an array of [structured blocks](https://app.slack.com/block-kit-builder) as a URL-encoded string. E.g., `[{ \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"This is a mrkdwn section block :ghost: *this is bold*, and ~this is crossed out~, and \" }}]`\n\n**Tip:** Construct your blocks in a code step, return them as an array, and then pass the return value to this step.", + optional: true, + }, + icon_emoji: { + type: "string", + label: "Icon (emoji)", + description: "Optionally provide an emoji to use as the icon for this message. E.g., `:fire:` Overrides `icon_url`. Must be used in conjunction with `Send as User` set to `false`, otherwise ignored.", + optional: true, + async options() { + return await this.getCustomEmojis({ + throwRateLimitError: true, + }); + }, + }, + content: { + label: "File Path or URL", + description: "The file to upload. Provide either a file URL or a path to a file in the `/tmp` directory (for example, `/tmp/myFile.txt`)", + type: "string", + }, + link_names: { + type: "boolean", + label: "Link Names", + description: "Find and link channel names and usernames.", + optional: true, + }, + thread_broadcast: { + type: "boolean", + label: "Send Channel Message", + description: "If `true`, posts in the thread and channel. Used in conjunction with `Message Timestamp` and indicates whether reply should be made visible to everyone in the channel. Defaults to `false`.", + default: false, + optional: true, + }, + reply_channel: { + label: "Reply Channel or Conversation ID", + type: "string", + description: "Provide the channel or conversation ID for the thread to reply to (e.g., if triggering on new Slack messages, enter `{{event.channel}}`). If the channel does not match the thread timestamp, a new message will be posted to this channel.", + optional: true, + }, + icon_url: { + type: "string", + label: "Icon (image URL)", + description: "Optionally provide an image URL to use as the icon for this message. Must be used in conjunction with `Send as User` set to `false`, otherwise ignored.", + optional: true, + }, + initial_comment: { + type: "string", + label: "Initial Comment", + description: "The message text introducing the file", + optional: true, + }, + email: { + type: "string", + label: "Email", + description: "An email address belonging to a user in the workspace", + }, + metadata_event_type: { + type: "string", + label: "Metadata Event Type", + description: "The name of the metadata event. Example: `task_created`", + optional: true, + }, + metadata_event_payload: { + type: "string", + label: "Metadata Event Payload", + description: "The payload of the metadata event. Must be a JSON string. Example: `{ \"id\": \"11223\", \"title\": \"Redesign Homepage\"}`", + optional: true, + }, + ignoreMyself: { + type: "boolean", + label: "Ignore myself", + description: "Ignore messages from me", + default: false, + }, + keyword: { + type: "string", + label: "Keyword", + description: "Keyword to monitor", + }, + ignoreBot: { + type: "boolean", + label: "Ignore Bots", + description: "Ignore messages from bots", + default: false, + optional: true, + }, + resolveNames: { + type: "boolean", + label: "Resolve Names", + description: "Instead of returning `channel`, `team`, and `user` as IDs, return their human-readable names.", + default: false, + optional: true, + }, + pageSize: { + type: "integer", + label: "Page Size", + description: "The number of results to include in a page. Default: 250", + default: constants.LIMIT, + optional: true, + }, + numPages: { + type: "integer", + label: "Number of Pages", + description: "The number of pages to retrieve. Default: 1", + default: 1, + optional: true, + }, + }, + methods: { + getChannelLabel(resource) { + if (resource.user) { + return `Direct Messaging with: @${resource.user.name}`; + } + + const { + is_private: isPrivate, + name, + } = resource.channel; + + return `${isPrivate && "Private" || "Public"} channel #${name}`; + }, + mySlackId() { + return this.$auth.oauth_uid; + }, + getToken(opts = {}) { + return opts.as_user === false + ? this.$auth.bot_token + : this.$auth.oauth_access_token; + }, + /** + * Returns a Slack Web Client object authenticated with the user's access + * token + */ + sdk(opts = {}) { + return new WebClient(this.getToken(opts), { + rejectRateLimitedCalls: true, + }); + }, + async makeRequest({ + method = "", throwRateLimitError = false, as_user, ...args + } = {}) { + const props = method.split("."); + const sdk = props.reduce((reduction, prop) => + reduction[prop], this.sdk({ + as_user, + })); + + let response; + try { + response = await this._withRetries(() => sdk(args), throwRateLimitError); + } catch (error) { + if (error?.data?.error === "channel_not_found" && as_user === false) { + throw new ConfigurationError(`${error} + Ensure the bot is a member of the channel, or set the **As User** option to true to act on behalf of the authenticated user. + `); + } + throw `${error}`; + } + + if (!response.ok) { + throw response.error; + } + return response; + }, + async _withRetries(apiCall, throwRateLimitError = false) { + const retryOpts = { + retries: 3, + minTimeout: 30000, + }; + return retry(async (bail) => { + try { + return await apiCall(); + } catch (error) { + const statusCode = get(error, "code"); + if (statusCode === "slack_webapi_rate_limited_error") { + if (throwRateLimitError) { + bail(new Error(`Rate limit exceeded. ${error}`)); + } else { + console.log(`Rate limit exceeded. Will retry in ${retryOpts.minTimeout / 1000} seconds`); + throw error; + } + } + bail(error); + } + }, retryOpts); + }, + /** + * Returns a list of channel-like conversations in a workspace. The + * "channels" returned depend on what the calling token has access to and + * the directives placed in the types parameter. + * + * @param {string} [types] - a comma-separated list of channel types to get. + * Any combination of: `public_channel`, `private_channel`, `mpim`, `im` + * @param {string} [cursor] - a cursor returned by the previous API call, + * used to paginate through collections of data + * @returns an object containing a list of conversations and the cursor for the next + * page of conversations + */ + async availableConversations(types, cursor, throwRateLimitError = false) { + const { + channels: conversations, + response_metadata: { next_cursor: nextCursor }, + } = await this.usersConversations({ + types, + cursor, + limit: constants.LIMIT, + exclude_archived: true, + throwRateLimitError, + }); + return { + cursor: nextCursor, + conversations, + }; + }, + async userNameLookup(ids = [], throwRateLimitError = true, args = {}) { + let cursor; + const userNames = {}; + do { + const { + members: users, + response_metadata: { next_cursor: nextCursor }, + } = await this.usersList({ + limit: constants.LIMIT, + cursor, + throwRateLimitError, + ...args, + }); + + for (const user of users) { + if (ids.includes(user.id)) { + userNames[user.id] = user.name; + } + } + + cursor = nextCursor; + } while (cursor && Object.keys(userNames).length < ids.length); + return userNames; + }, + /** + * Checks authentication & identity. + * @param {*} args Arguments object + * @returns Promise + */ + authTest(args = {}) { + return this.makeRequest({ + method: "auth.test", + ...args, + }); + }, + /** + * Lists all reminders created by or for a given user. + * @param {*} args Arguments object + * @param {string} [args.team_id] Encoded team id, required if org token is passed. + * E.g. `T1234567890` + * @returns Promise + */ + remindersList(args = {}) { + return this.makeRequest({ + method: "reminders.list", + ...args, + }); + }, + /** + * List all User Groups for a team + * @param {*} args + * @returns Promise + */ + usergroupsList(args = {}) { + return this.makeRequest({ + method: "usergroups.list", + ...args, + }); + }, + authTeamsList(args = {}) { + args.limit ||= constants.LIMIT; + return this.makeRequest({ + method: "auth.teams.list", + ...args, + }); + }, + /** + * List conversations the calling user may access. + * Bot Scopes: `channels:read` `groups:read` `im:read` `mpim:read` + * @param {UsersConversationsArguments} args Arguments object + * @param {string} [args.cursor] Pagination value e.g. (`dXNlcjpVMDYxTkZUVDI=`) + * @param {boolean} [args.exclude_archived] Set to `true` to exclude archived channels + * from the list. Defaults to `false` + * @param {number} [args.limit] Pagination value. Defaults to `250` + * @param {string} [args.team_id] Encoded team id to list users in, + * required if org token is used + * @param {string} [args.types] Mix and match channel types by providing a + * comma-separated list of any combination of `public_channel`, `private_channel`, `mpim`, `im` + * Defaults to `public_channel`. E.g. `im,mpim` + * @param {string} [args.user] Browse conversations by a specific + * user ID's membership. Non-public channels are restricted to those where the calling user + * shares membership. E.g `W0B2345D` + * @returns Promise + */ + usersConversations(args = {}) { + args.limit ||= constants.LIMIT; + return this.makeRequest({ + method: "users.conversations", + user: this.$auth.oauth_uid, + ...args, + }); + }, + /** + * Lists all users in a Slack team. + * Bot Scopes: `users:read` + * @param {UsersListArguments} args Arguments object + * @param {string} [args.cursor] Pagination value e.g. (`dXNlcjpVMDYxTkZUVDI=`) + * @param {boolean} [args.include_locale] Set this to `true` to receive the locale + * for users. Defaults to `false` + * @param {number} [args.limit] The maximum number of items to return. Defaults to `250` + * @param {string} [args.team_id] Encoded team id to list users in, + * required if org token is used + * @returns Promise + */ + usersList(args = {}) { + args.limit ||= constants.LIMIT; + return this.makeRequest({ + method: "users.list", + ...args, + }); + }, + /** + * Lists all channels in a Slack team. + * Bot Scopes: `channels:read` `groups:read` `im:read` `mpim:read` + * @param {ConversationsListArguments} args Arguments object + * @param {string} [args.cursor] Pagination value e.g. (`dXNlcjpVMDYxTkZUVDI=`) + * @param {boolean} [args.exclude_archived] Set to `true` to exclude archived channels + * from the list. Defaults to `false` + * @param {number} [args.limit] pagination value. Defaults to `250` + * @param {string} [args.team_id] encoded team id to list users in, + * required if org token is used + * @param {string} [args.types] Mix and match channel types by providing a + * comma-separated list of any combination of `public_channel`, `private_channel`, `mpim`, `im` + * Defaults to `public_channel`. E.g. `im,mpim` + * @returns Promise + */ + conversationsList(args = {}) { + args.limit ||= constants.LIMIT; + return this.makeRequest({ + method: "conversations.list", + ...args, + }); + }, + /** + * Fetches a conversation's history of messages and events. + * Bot Scopes: `channels:history` `groups:history` `im:history` `mpim:history` + * @param {ConversationsHistoryArguments} args Arguments object + * @param {string} args.channel Conversation ID to fetch history for. E.g. `C1234567890` + * @param {string} [args.cursor] Pagination value e.g. (`dXNlcjpVMDYxTkZUVDI=`) + * @param {boolean} [args.include_all_metadata] + * @param {boolean} [args.inclusive] + * @param {string} [args.latest] + * @param {number} [args.limit] + * @param {string} [args.oldest] + * @returns Promise + */ + conversationsHistory(args = {}) { + args.limit ||= constants.LIMIT; + return this.makeRequest({ + method: "conversations.history", + ...args, + }); + }, + /** + * Retrieve information about a conversation. + * Bot Scopes: `channels:read` `groups:read` `im:read` `mpim:read` + * @param {ConversationsInfoArguments} args Arguments object + * @param {string} args.channel Conversation ID to learn more about. E.g. `C1234567890` + * @param {boolean} [args.include_locale] Set this to `true` to receive the locale + * for users. Defaults to `false` + * @param {boolean} [args.include_num_members] Set to true to include the + * member count for the specified conversation. Defaults to `false` + * @returns Promise + */ + conversationsInfo(args = {}) { + return this.makeRequest({ + method: "conversations.info", + ...args, + }); + }, + /** + * Retrieve information about a conversation. + * Bot Scopes: `users:read` + * @param {UsersInfoArguments} args arguments object + * @param {string} args.user User to get info on. E.g. `W1234567890` + * @param {boolean} [args.include_locale] Set this to true to receive the locale + * for this user. Defaults to `false` + * @returns Promise + */ + usersInfo(args = {}) { + return this.makeRequest({ + method: "users.info", + ...args, + }); + }, + /** + * Searches for messages matching a query. + * User Scopes: `search:read` + * @param {SearchMessagesArguments} args Arguments object + * @param {string} args.query Search query + * @param {number} [args.count] Number of items to return per page. Default `250` + * @param {string} [args.cursor] Use this when getting results with cursormark + * pagination. For first call send `*` for subsequent calls, send the value of + * `next_cursor` returned in the previous call's results + * @param {boolean} [args.highlight] + * @param {number} [args.page] + * @param {string} [args.sort] + * @param {string} [args.sort_dir] + * @param {string} [args.team_id] Encoded team id to search in, + * required if org token is used. E.g. `T1234567890` + * @returns Promise + */ + searchMessages(args = {}) { + args.count ||= constants.LIMIT; + return this.makeRequest({ + method: "search.messages", + ...args, + }); + }, + /** + * Lists reactions made by a user. + * User Scopes: `reactions:read` + * Bot Scopes: `reactions:read` + * @param {ReactionsListArguments} args Arguments object + * @param {number} [args.count] Number of items to return per page. Default `100` + * @param {string} [args.cursor] Parameter for pagination. Set cursor equal to the + * `next_cursor` attribute returned by the previous request's response_metadata. + * This parameter is optional, but pagination is mandatory: the default value simply + * fetches the first "page" of the collection. + * @param {boolean} [args.full] If true always return the complete reaction list. + * @param {number} [args.limit] The maximum number of items to return. + * Fewer than the requested number of items may be returned, even if the end of the + * list hasn't been reached. + * @param {number} [args.page] Page number of results to return. Defaults to `1`. + * @param {string} [args.team_id] Encoded team id to list reactions in, + * required if org token is used + * @param {string} [args.user] Show reactions made by this user. Defaults to the authed user. + * @returns Promise + */ + reactionsList(args = {}) { + args.limit ||= constants.LIMIT; + return this.makeRequest({ + method: "reactions.list", + ...args, + }); + }, + async getCustomEmojis(args = {}) { + const resp = await this.sdk().emoji.list({ + include_categories: true, + limit: constants.LIMIT, + ...args, + }); + + const emojis = Object.keys(resp.emoji); + for (const category of resp.categories) { + emojis.push(...category.emoji_names); + } + return emojis; + }, + listChannelMembers(args = {}) { + args.limit ||= constants.LIMIT; + return this.makeRequest({ + method: "conversations.members", + ...args, + }); + }, + listFiles(args = {}) { + args.count ||= constants.LIMIT; + return this.makeRequest({ + method: "files.list", + ...args, + }); + }, + listGroupMembers(args = {}) { + args.limit ||= constants.LIMIT; + return this.makeRequest({ + method: "usergroups.users.list", + ...args, + }); + }, + getFileInfo(args = {}) { + return this.makeRequest({ + method: "files.info", + ...args, + }); + }, + getUserProfile(args = {}) { + return this.makeRequest({ + method: "users.profile.get", + ...args, + }); + }, + getBotInfo(args = {}) { + return this.makeRequest({ + method: "bots.info", + ...args, + }); + }, + getTeamInfo(args = {}) { + return this.makeRequest({ + method: "team.info", + ...args, + }); + }, + getConversationReplies(args = {}) { + return this.makeRequest({ + method: "conversations.replies", + ...args, + }); + }, + addReactions(args = {}) { + return this.makeRequest({ + method: "reactions.add", + ...args, + }); + }, + postChatMessage(args = {}) { + return this.makeRequest({ + method: "chat.postMessage", + ...args, + }); + }, + archiveConversations(args = {}) { + return this.makeRequest({ + method: "conversations.archive", + ...args, + }); + }, + scheduleMessage(args = {}) { + return this.makeRequest({ + method: "chat.scheduleMessage", + ...args, + }); + }, + createConversations(args = {}) { + return this.makeRequest({ + method: "conversations.create", + ...args, + }); + }, + inviteToConversation(args = {}) { + return this.makeRequest({ + method: "conversations.invite", + ...args, + }); + }, + kickUserFromConversation(args = {}) { + return this.makeRequest({ + method: "conversations.kick", + ...args, + }); + }, + addReminders(args = {}) { + return this.makeRequest({ + method: "reminders.add", + ...args, + }); + }, + deleteFiles(args = {}) { + return this.makeRequest({ + method: "files.delete", + ...args, + }); + }, + deleteMessage(args = {}) { + return this.makeRequest({ + method: "chat.delete", + ...args, + }); + }, + lookupUserByEmail(args = {}) { + return this.makeRequest({ + method: "users.lookupByEmail", + ...args, + }); + }, + setChannelDescription(args = {}) { + return this.makeRequest({ + method: "conversations.setPurpose", + ...args, + }); + }, + setChannelTopic(args = {}) { + return this.makeRequest({ + method: "conversations.setTopic", + ...args, + }); + }, + updateProfile(args = {}) { + return this.makeRequest({ + method: "users.profile.set", + ...args, + }); + }, + updateGroupMembers(args = {}) { + return this.makeRequest({ + method: "usergroups.users.update", + ...args, + }); + }, + updateMessage(args = {}) { + return this.makeRequest({ + method: "chat.update", + ...args, + }); + }, + getUploadUrl(args = {}) { + return this.makeRequest({ + method: "files.getUploadURLExternal", + ...args, + }); + }, + completeUpload(args = {}) { + return this.makeRequest({ + method: "files.completeUploadExternal", + ...args, + }); + }, + }, +}; diff --git a/components/slack_v2_test/sources/common/base.mjs b/components/slack_v2_test/sources/common/base.mjs new file mode 100644 index 0000000000000..fdffe795a98ba --- /dev/null +++ b/components/slack_v2_test/sources/common/base.mjs @@ -0,0 +1,184 @@ +import slack from "../../slack_v2_test.app.mjs"; +import { + NAME_CACHE_MAX_SIZE, NAME_CACHE_TIMEOUT, +} from "./constants.mjs"; + +export default { + props: { + slack, + db: "$.service.db", + }, + methods: { + _getNameCache() { + return this.db.get("nameCache") ?? {}; + }, + _setNameCache(cacheObj) { + this.db.set("nameCache", cacheObj); + }, + _getLastCacheCleanup() { + return this.db.get("lastCacheCleanup") ?? 0; + }, + _setLastCacheCleanup(time) { + this.db.set("lastCacheCleanup", time); + }, + cleanCache(cacheObj) { + console.log("Initiating cache check-up..."); + const timeout = Date.now() - NAME_CACHE_TIMEOUT; + + const entries = Object.entries(cacheObj); + let cleanArr = entries.filter( + ([ + , { ts }, + ]) => ts > timeout, + ); + const diff = entries.length - cleanArr.length; + if (diff) { + console.log(`Cleaned up ${diff} outdated cache entries.`); + } + + if (cleanArr.length > NAME_CACHE_MAX_SIZE) { + console.log(`Reduced the cache from ${cleanArr.length} to ${NAME_CACHE_MAX_SIZE / 2} entries.`); + cleanArr = cleanArr.slice(NAME_CACHE_MAX_SIZE / -2); + } + + const cleanObj = Object.fromEntries(cleanArr); + return cleanObj; + }, + getCache() { + let cacheObj = this._getNameCache(); + + const lastCacheCleanup = this._getLastCacheCleanup(); + const time = Date.now(); + + const shouldCleanCache = time - lastCacheCleanup > NAME_CACHE_TIMEOUT / 2; + if (shouldCleanCache) { + cacheObj = this.cleanCache(cacheObj); + this._setLastCacheCleanup(time); + } + + return [ + cacheObj, + shouldCleanCache, + ]; + }, + async maybeCached(key, refreshVal) { + let [ + cacheObj, + wasUpdated, + ] = this.getCache(); + let record = cacheObj[key]; + const time = Date.now(); + if (!record || time - record.ts > NAME_CACHE_TIMEOUT) { + record = { + ts: time, + val: await refreshVal(), + }; + cacheObj[key] = record; + wasUpdated = true; + } + + if (wasUpdated) { + this._setNameCache(cacheObj); + } + + return record.val; + }, + async getUserName(id) { + return this.maybeCached(`users:${id}`, async () => { + const info = await this.slack.usersInfo({ + user: id, + }); + if (!info.ok) throw new Error(info.error); + return info.user.name; + }); + }, + async getRealName(id) { + return this.maybeCached(`users_real_names:${id}`, async () => { + const info = await this.slack.usersInfo({ + user: id, + }); + if (!info.ok) throw new Error(info.error); + return info.user.real_name; + }); + }, + async getBotName(id) { + return this.maybeCached(`bots:${id}`, async () => { + const info = await this.slack.getBotInfo({ + bot: id, + }); + if (!info.ok) throw new Error(info.error); + return info.bot.name; + }); + }, + async getConversationName(id) { + return this.maybeCached(`conversations:${id}`, async () => { + const info = await this.slack.conversationsInfo({ + channel: id, + }); + if (!info.ok) throw new Error(info.error); + if (info.channel.is_im) { + return `DM with ${await this.getUserName(info.channel.user)}`; + } + return info.channel.name; + }); + }, + async getTeamName(id) { + return this.maybeCached(`team:${id}`, async () => { + try { + const info = await this.slack.getTeamInfo({ + team: id, + }); + return info.team.name; + } catch (err) { + console.log( + "Error getting team name, probably need to re-connect the account at pipedream.com/apps", + err, + ); + return id; + } + }); + }, + async getMessage({ + channel, event_ts: ts, + }) { + return await this.maybeCached( + `lastMessage:${channel}:${ts}`, + async () => { + const response = await this.slack.getConversationReplies({ + channel, + ts, + limit: 1, + }); + + if (response.messages.length) { + response.messages = [ + response.messages[0], + ]; + } + + return response; + }, + ); + }, + processEvent(event) { + return event; + }, + }, + async run(event) { + event = await this.processEvent(event); + + if (event) { + if (!event.client_msg_id) { + event.pipedream_msg_id = `pd_${Date.now()}_${Math.random() + .toString(36) + .substr(2, 10)}`; + } + + this.$emit(event, { + id: event.client_msg_id || event.pipedream_msg_id || event.channel.id, + summary: this.getSummary(event), + ts: event.event_ts || Date.now(), + }); + } + }, +}; diff --git a/components/slack_v2_test/sources/common/constants.mjs b/components/slack_v2_test/sources/common/constants.mjs new file mode 100644 index 0000000000000..3255f46788c83 --- /dev/null +++ b/components/slack_v2_test/sources/common/constants.mjs @@ -0,0 +1,58 @@ +const events = { + im: "User", + message: "Message", + file: "File", + channel: "Channel", +}; + +const eventsOptions = [ + { + label: "User", + value: "im", + }, + { + label: "Message", + value: "message", + }, + { + label: "File", + value: "file", + }, + { + label: "Channel", + value: "channel", + }, +]; + +const SUBTYPE = { + NULL: null, + BOT_MESSAGE: "bot_message", + FILE_SHARE: "file_share", + PD_HISTORY_MESSAGE: "pd_history_message", + MESSAGE_REPLIED: "message_replied", +}; + +const ALLOWED_SUBTYPES = [ + SUBTYPE.NULL, + SUBTYPE.BOT_MESSAGE, + SUBTYPE.FILE_SHARE, + SUBTYPE.PD_HISTORY_MESSAGE, +]; + +const ALLOWED_MESSAGE_IN_CHANNEL_SUBTYPES = [ + SUBTYPE.NULL, + SUBTYPE.BOT_MESSAGE, + SUBTYPE.FILE_SHARE, + SUBTYPE.MESSAGE_REPLIED, +]; + +export const NAME_CACHE_MAX_SIZE = 1000; +export const NAME_CACHE_TIMEOUT = 3600000; + +export default { + events, + eventsOptions, + SUBTYPE, + ALLOWED_SUBTYPES, + ALLOWED_MESSAGE_IN_CHANNEL_SUBTYPES, +}; diff --git a/components/slack_v2_test/sources/new-channel-created/new-channel-created.mjs b/components/slack_v2_test/sources/new-channel-created/new-channel-created.mjs new file mode 100644 index 0000000000000..a6340578a4477 --- /dev/null +++ b/components/slack_v2_test/sources/new-channel-created/new-channel-created.mjs @@ -0,0 +1,32 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "slack_v2_test-new-channel-created", + name: "New Channel Created (Instant)", + version: "0.0.10", + description: "Emit new event when a new channel is created.", + type: "source", + dedupe: "unique", + props: { + ...common.props, + // eslint-disable-next-line pipedream/props-description,pipedream/props-label + slackApphook: { + type: "$.interface.apphook", + appProp: "slack", + async eventNames() { + return [ + "channel_created", + ]; + }, + }, + }, + methods: { + ...common.methods, + getSummary({ channel: { name } }) { + return `New channel created - ${name}`; + }, + }, + sampleEmit, +}; diff --git a/components/slack_v2_test/sources/new-channel-created/test-event.mjs b/components/slack_v2_test/sources/new-channel-created/test-event.mjs new file mode 100644 index 0000000000000..6bcfee6fd7deb --- /dev/null +++ b/components/slack_v2_test/sources/new-channel-created/test-event.mjs @@ -0,0 +1,44 @@ +export default { + "type": "channel_created", + "channel": { + "id": "C024BE91L", + "name": "fun", + "is_channel": true, + "is_group": false, + "is_im": false, + "is_mpim": false, + "is_private": false, + "created": 1360782804, + "is_archived": false, + "is_general": false, + "unlinked": 0, + "name_normalized": "fun", + "is_shared": false, + "is_frozen": false, + "is_org_shared": false, + "is_pending_ext_shared": false, + "pending_shared": [], + "context_team_id": "TLZ203R5", + "updated": 1714140253251, + "parent_conversation": null, + "creator": "U024BE7LH", + "is_ext_shared": false, + "shared_team_ids": [ + "TLZ203R5" + ], + "pending_connected_team_ids": [], + "topic": { + "value": "", + "creator": "", + "last_set": 0 + }, + "purpose": { + "value": "", + "creator": "", + "last_set": 0 + }, + "previous_names": [] + }, + "event_ts": "1714140253.002700", + "pipedream_msg_id": "pd_1714140255038_bkbl3pxpkp" +} \ No newline at end of file diff --git a/components/slack_v2_test/sources/new-direct-message/new-direct-message.mjs b/components/slack_v2_test/sources/new-direct-message/new-direct-message.mjs new file mode 100644 index 0000000000000..ca2663bac71b1 --- /dev/null +++ b/components/slack_v2_test/sources/new-direct-message/new-direct-message.mjs @@ -0,0 +1,53 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "slack_v2_test-new-direct-message", + name: "New Direct Message (Instant)", + version: "1.0.24", + description: "Emit new event when a message was posted in a direct message channel", + type: "source", + dedupe: "unique", + props: { + ...common.props, + // eslint-disable-next-line pipedream/props-description,pipedream/props-label + slackApphook: { + type: "$.interface.apphook", + appProp: "slack", + async eventNames() { + return [ + "message.im", + ]; + }, + }, + ignoreBot: { + propDefinition: [ + common.props.slack, + "ignoreBot", + ], + }, + ignoreSelf: { + type: "boolean", + label: "Ignore Messages from Yourself", + description: "Ignores messages sent to yourself", + default: false, + optional: true, + }, + }, + methods: { + ...common.methods, + getSummary() { + return "New direct message received"; + }, + processEvent(event) { + if ((this.ignoreSelf && event.user == this.slack.mySlackId()) + || ((this.ignoreBot) && (event.subtype === "bot_message" || event.bot_id)) + || (event.subtype === "message_changed")) { + return; + } + return event; + }, + }, + sampleEmit, +}; diff --git a/components/slack_v2_test/sources/new-direct-message/test-event.mjs b/components/slack_v2_test/sources/new-direct-message/test-event.mjs new file mode 100644 index 0000000000000..d19486ed235f0 --- /dev/null +++ b/components/slack_v2_test/sources/new-direct-message/test-event.mjs @@ -0,0 +1,28 @@ +export default { + "user": "USLACKBOT", + "type": "message", + "ts": "1716401124.947359", + "text": "Feeling great!", + "team": "TS8319547", + "blocks": [ + { + "type": "rich_text", + "block_id": "bid/", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Feeling great!" + } + ] + } + ] + } + ], + "channel": "DS676Q73J", + "event_ts": "1716401124.947359", + "channel_type": "im", + "pipedream_msg_id": "pd_1716401126905_tjxu6josgz" +} \ No newline at end of file diff --git a/components/slack_v2_test/sources/new-interaction-event-received/README.md b/components/slack_v2_test/sources/new-interaction-event-received/README.md new file mode 100644 index 0000000000000..4cb59ba41fe5e --- /dev/null +++ b/components/slack_v2_test/sources/new-interaction-event-received/README.md @@ -0,0 +1,85 @@ +# Overview + +Slack messages can contain interactive elements like buttons, dropdowns, radio buttons, and more. This source subscribes to interactive events, like when a button is clicked in a message. + +![Example of a Slack button](https://res.cloudinary.com/pipedreamin/image/upload/v1668443788/docs/components/CleanShot_2022-11-10_at_10.17.172x_dxdz1o.png) + +Then this source will be triggered when you or another Slack user in your workspace clicks a button, selects an option or fills out a form. + +![Example feed of interaction events coming from Slack](https://res.cloudinary.com/pipedreamin/image/upload/v1668443818/docs/components/CleanShot_2022-11-10_at_10.19.152x_eyiims.png) + +With this trigger, you can build workflows that perform some work with other APIs or services, and then reply back to the original message. + +# Getting Started + + + +What this short video to learn how to use this in a workflow, or follow the guide below. + +First, if you haven’t already - send yourself a message containing one or more interactive elements. Use the ******************Sending the message with an interactive element****************** guide below to send a message containing a button. + +If you have already sent a message containing an element, skip to **********************************************Configuring the source.********************************************** + +## Sending the message with an interactive element + +The easiest way is to send yourself a message using the ****************************Slack - Send Message Using Block Kit**************************** action: + +![Selecting the Send Slack Message with Block Kit](https://res.cloudinary.com/pipedreamin/image/upload/v1668443844/docs/components/CleanShot_2022-11-10_at_10.25.522x_vxiooo.png)) + +Then select a **************Channel************** you’d like to send the message to, and use the **************[Block Kit Builder](https://app.slack.com/block-kit-builder/)************** to build a message, or just copy the example button blocks below: + +```jsx +[ + { + "type": "actions", + "elements": [ + { + "type": "button", + "text": { + "type": "plain_text", + "text": "Click Me", + "emoji": true + }, + "value": "click_me_123", + "action_id": "button_click" + } + ] + } +] +``` + +Your ******************Slack - Send Message Using Block Kit****************** should look like this: + +![Setting up the block kit message with a button block](https://res.cloudinary.com/pipedreamin/image/upload/v1668443887/docs/components/CleanShot_2022-11-10_at_10.29.552x_kvfznm.png) + +## Configuring the source + +By default, this source will listen to ******all****** interactive events from your Slack workspace that your connected Slack account has authorization to view. Please note that only messages created via [Slack - Send Block Kit Message](https://pipedream.com/apps/slack/actions/send-block-kit-message) Action, or via API call from the Pipedream app will emit an interaction event with this trigger. Block kit messages sent directly via the Slack's block kit builder will not trigger an interaction event. + +You can filter these events by selecting a specific **************channel************** and/or a specific **********action_id.********** + +### Filtering interactive events by channel + +Use the ****************Channels**************** dropdown to search for a specific channel for this source to subscribe to. ********Only******** button clicks, dropdown selects, etc. *in this selected channel* will trigger the source. + +### Filtering interactive events by `action_id` + +For more specificity, you can filter based on the passed `action_id` to the message. + +The `action_id` is arbitrary. It’s defined on the initial message sending the button, dropdown, or other interactive element’s markup. + +For example, in the section above using the Block Kit to create a message, we defined the button’s `action_id` as `"button_click"`. But you can choose whichever naming convention you’d like. + +If you pass `button_click` as a required `action_id` to this source, then only interactivity events with the `action_id` of `"button_click"` will trigger this source. + +## Troubleshooting + +### I’m clicking buttons, but no events are being received + +Follow these steps to make sure your source is configured correctly: + +1. Make sure that your `action_id` or ****************channels**************** filters apply to that message, remove the filters to make sure that’s not the case. + +1. Make sure that the message comes from the same Slack account that this source is configured with. + +1. Make sure that the message was sent via Pipedream action (e.g. [Slack - Send Block Kit Message](https://pipedream.com/apps/slack/actions/send-block-kit-message) Action) or via API call from the Pipedream app. diff --git a/components/slack_v2_test/sources/new-interaction-event-received/new-interaction-event-received.mjs b/components/slack_v2_test/sources/new-interaction-event-received/new-interaction-event-received.mjs new file mode 100644 index 0000000000000..17165b2180168 --- /dev/null +++ b/components/slack_v2_test/sources/new-interaction-event-received/new-interaction-event-received.mjs @@ -0,0 +1,105 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + name: "New Interaction Events (Instant)", + version: "0.0.20", + key: "slack_v2_test-new-interaction-event-received", + description: "Emit new events on new Slack [interactivity events](https://api.slack.com/interactivity) sourced from [Block Kit interactive elements](https://api.slack.com/interactivity/components), [Slash commands](https://api.slack.com/interactivity/slash-commands), or [Shortcuts](https://api.slack.com/interactivity/shortcuts).", + type: "source", + props: { + ...common.props, + alert: { + type: "alert", + alertType: "info", + content: "Please note that only messages created via Pipedream's [Send Block Kit Message](https://pipedream.com/apps/slack/actions/send-block-kit-message) Action, or via API call from the Pipedream app will emit an interaction event with this trigger. \n\nBlock kit messages sent directly via the Slack's block kit builder will not trigger an interaction event. \n\nSee the [documentation](https://pipedream.com/apps/slack/triggers/new-interaction-event-received) for more details.", + }, + action_ids: { + type: "string[]", + label: "Action IDs", + description: "Filter interaction events by specific `action_id`'s to subscribe for new interaction events. If none are specified, all `action_ids` created via Pipedream will emit new events.", + optional: true, + default: [], + }, + conversations: { + propDefinition: [ + common.props.slack, + "conversation", + ], + type: "string[]", + label: "Channels", + description: "Filter interaction events by one or more channels. If none selected, any interaction event in any channel will emit new events.", + optional: true, + default: [], + }, + // eslint-disable-next-line pipedream/props-description,pipedream/props-label + slackApphook: { + type: "$.interface.apphook", + appProp: "slack", + /** + * Subscribes to potentially 4 different events: + * `interaction_events` - all interaction events on the authenticated account + * `interaction_events:${action_id}` - all interaction events with a specific given action_id + * `interaction_events:${channel_id}` - all interaction events within a specific channel + * `interaction_events:${channel_id}:${action_id}` - action_id within a specific channel + * @returns string[] + */ + async eventNames() { + // start with action_ids, since they can be the most specific + const action_events = this.action_ids.reduce((carry, action_id) => { + // if channels are provided, spread them + if (this.conversations && this.conversations.length > 0) { + return [ + ...carry, + ...this.conversations.map( + (channel) => `interaction_events:${channel}:${action_id}`, + ), + ]; + } + + return [ + ...carry, + `interaction_events:${action_id}`, + ]; + }, []); + + if (action_events.length > 0) return action_events; + + // if no action_ids are specified, move down to channels + const channel_events = this.conversations.map( + (channel) => `interaction_events:${channel}`, + ); + + if (channel_events.length > 0) return channel_events; + + // if not specific action_ids or channels are specified, subscribe to all events + return [ + "interaction_events", + ]; + }, + }, + }, + methods: {}, + async run(event) { + this.$emit( + { + event, + }, + { + summary: `New interaction event${ + event?.channel?.id + ? ` in channel ${event.channel.id}` + : "" + }${ + event.actions?.length > 0 + ? ` from action_ids ${event.actions + .map((action) => action.action_id) + .join(", ")}` + : "" + }`, + ts: Date.now(), + }, + ); + }, + sampleEmit, +}; diff --git a/components/slack_v2_test/sources/new-interaction-event-received/test-event.mjs b/components/slack_v2_test/sources/new-interaction-event-received/test-event.mjs new file mode 100644 index 0000000000000..940a52c301ea8 --- /dev/null +++ b/components/slack_v2_test/sources/new-interaction-event-received/test-event.mjs @@ -0,0 +1,86 @@ +export default { + "event": { + "type": "block_actions", + "user": { + "id": "US676PZLY", + "username": "test.user", + "name": "test.user", + "team_id": "TS8319547" + }, + "api_app_id": "AN9231S6L", + "token": "UYc82mtyZWRhvUXQ6TXrv4wq", + "container": { + "type": "message", + "message_ts": "1716402983.247149", + "channel_id": "CS8319KD5", + "is_ephemeral": false + }, + "trigger_id": "7161731794692.892103311143.4020ed3595908eca11e4076438354dbb", + "team": { + "id": "TS8319547", + "domain": "test-j1q3506" + }, + "enterprise": null, + "is_enterprise_install": false, + "channel": { + "id": "CS8319KD5", + "name": "testing" + }, + "message": { + "subtype": "bot_message", + "text": "Click Me button Sent via ", + "username": "Pipedream", + "type": "message", + "ts": "1716402983.247149", + "bot_id": "BRTDL45RQ", + "app_id": "AN9231S6L", + "blocks": [ + { + "type": "actions", + "block_id": "SJp0j", + "elements": [ + { + "type": "button", + "action_id": "button_click", + "text": { + "type": "plain_text", + "text": "Click Me", + "emoji": true + }, + "value": "click_me_123" + } + ] + }, + { + "type": "context", + "block_id": "ysmBN", + "elements": [ + { + "type": "mrkdwn", + "text": "Sent via ", + "verbatim": false + } + ] + } + ] + }, + "state": { + "values": {} + }, + "response_url": "https://hooks.slack.com/actions/TS8319547/7156351250101/J0w1NoVIXjChEwp4WQab4tcv", + "actions": [ + { + "action_id": "button_click", + "block_id": "SJp0j", + "text": { + "type": "plain_text", + "text": "Click Me", + "emoji": true + }, + "value": "click_me_123", + "type": "button", + "action_ts": "1716403200.549150" + } + ] + } +} \ No newline at end of file diff --git a/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs b/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs new file mode 100644 index 0000000000000..9266b97e24b08 --- /dev/null +++ b/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs @@ -0,0 +1,129 @@ +import common from "../common/base.mjs"; +import constants from "../common/constants.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "slack_v2_test-new-keyword-mention", + name: "New Keyword Mention (Instant)", + version: "0.0.8", + description: "Emit new event when a specific keyword is mentioned in a channel", + type: "source", + dedupe: "unique", + props: { + ...common.props, + conversations: { + propDefinition: [ + common.props.slack, + "conversation", + ], + type: "string[]", + label: "Channels", + description: "Select one or more channels to monitor for new messages.", + optional: true, + }, + // eslint-disable-next-line pipedream/props-description,pipedream/props-label + slackApphook: { + type: "$.interface.apphook", + appProp: "slack", + async eventNames() { + return this.conversations || [ + "message", + ]; + }, + }, + keyword: { + propDefinition: [ + common.props.slack, + "keyword", + ], + }, + ignoreBot: { + propDefinition: [ + common.props.slack, + "ignoreBot", + ], + }, + }, + hooks: { + ...common.hooks, + async deploy() { + // emit historical events + const messages = await this.getMatches({ + query: this.keyword, + sort: "timestamp", + }); + const filteredMessages = this.conversations?.length > 0 + ? messages.filter((message) => this.conversations.includes(message.channel.id)) + : messages; + await this.emitHistoricalEvents(filteredMessages.slice(-25).reverse()); + }, + }, + methods: { + ...common.methods, + async getMatches(params) { + return (await this.slack.searchMessages(params)).messages.matches || []; + }, + async emitHistoricalEvents(messages) { + for (const message of messages) { + const event = await this.processEvent({ + ...message, + subtype: message.subtype || constants.SUBTYPE.PD_HISTORY_MESSAGE, + }); + if (event) { + if (!event.client_msg_id) { + event.pipedream_msg_id = `pd_${Date.now()}_${Math.random().toString(36) + .substr(2, 10)}`; + } + + this.$emit(event, { + id: event.client_msg_id || event.pipedream_msg_id, + summary: this.getSummary(event), + ts: event.event_ts || Date.now(), + }); + } + } + }, + getSummary() { + return "New keyword mention received"; + }, + async processEvent(event) { + const { + type: msgType, + subtype, + bot_id: botId, + text, + } = event; + + if (msgType !== "message") { + console.log(`Ignoring event with unexpected type "${msgType}"`); + return; + } + + // This source is designed to just emit an event for each new message received. + // Due to inconsistencies with the shape of message_changed and message_deleted + // events, we are ignoring them for now. If you want to handle these types of + // events, feel free to change this code!! + if (subtype && !constants.ALLOWED_SUBTYPES.includes(subtype)) { + console.log(`Ignoring message with subtype. "${subtype}"`); + return; + } + + if ((this.ignoreBot) && (subtype === constants.SUBTYPE.BOT_MESSAGE || botId)) { + return; + } + + let emitEvent = false; + if (text.indexOf(this.keyword) !== -1) { + emitEvent = true; + } else if (subtype === constants.SUBTYPE.PD_HISTORY_MESSAGE) { + emitEvent = true; + } + + if (emitEvent) { + return event; + } + }, + }, + sampleEmit, +}; diff --git a/components/slack_v2_test/sources/new-keyword-mention/test-event.mjs b/components/slack_v2_test/sources/new-keyword-mention/test-event.mjs new file mode 100644 index 0000000000000..7c85b12599e3d --- /dev/null +++ b/components/slack_v2_test/sources/new-keyword-mention/test-event.mjs @@ -0,0 +1,28 @@ +export default { + "user": "US676PZLY", + "type": "message", + "ts": "1716404766.096289", + "client_msg_id": "b26387fd-5afe-46a9-bf63-a7aabd6fb40f", + "text": "hello", + "team": "TS8319547", + "blocks": [ + { + "type": "rich_text", + "block_id": "aY6KK", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "hello" + } + ] + } + ] + } + ], + "channel": "CS8319KD5", + "event_ts": "1716404766.096289", + "channel_type": "channel" +} \ No newline at end of file diff --git a/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs b/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs new file mode 100644 index 0000000000000..a7e5a5a4867c1 --- /dev/null +++ b/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs @@ -0,0 +1,99 @@ +import common from "../common/base.mjs"; +import constants from "../common/constants.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "slack_v2_test-new-message-in-channels", + name: "New Message In Channels (Instant)", + version: "1.0.25", + description: "Emit new event when a new message is posted to one or more channels", + type: "source", + dedupe: "unique", + props: { + ...common.props, + conversations: { + propDefinition: [ + common.props.slack, + "conversation", + ], + type: "string[]", + label: "Channels", + description: "Select one or more channels to monitor for new messages.", + optional: true, + }, + // eslint-disable-next-line pipedream/props-description,pipedream/props-label + slackApphook: { + type: "$.interface.apphook", + appProp: "slack", + async eventNames() { + return this.conversations || [ + "message", + ]; + }, + }, + resolveNames: { + propDefinition: [ + common.props.slack, + "resolveNames", + ], + }, + ignoreBot: { + propDefinition: [ + common.props.slack, + "ignoreBot", + ], + }, + ignoreThreads: { + type: "boolean", + label: "Ignore replies in threads", + description: "Ignore replies to messages in threads", + optional: true, + }, + }, + methods: { + ...common.methods, + getSummary() { + return "New message in channel"; + }, + async processEvent(event) { + if (event.type !== "message") { + console.log(`Ignoring event with unexpected type "${event.type}"`); + return; + } + if (event.subtype && !constants.ALLOWED_MESSAGE_IN_CHANNEL_SUBTYPES.includes(event.subtype)) { + // This source is designed to just emit an event for each new message received. + // Due to inconsistencies with the shape of message_changed and message_deleted + // events, we are ignoring them for now. If you want to handle these types of + // events, feel free to change this code!! + console.log("Ignoring message with subtype."); + return; + } + if ((this.ignoreBot) && (event.subtype == "bot_message" || event.bot_id)) { + return; + } + // There is no thread message type only the thread_ts field + // indicates if the message is part of a thread in the event. + if (this.ignoreThreads && event.thread_ts) { + console.log("Ignoring reply in thread"); + return; + } + if (this.resolveNames) { + if (event.user) { + event.user_id = event.user; + event.user = await this.getUserName(event.user); + } else if (event.bot_id) { + event.bot = await this.getBotName(event.bot_id); + } + event.channel_id = event.channel; + event.channel = await this.getConversationName(event.channel); + if (event.team) { + event.team_id = event.team; + event.team = await this.getTeamName(event.team); + } + } + return event; + }, + }, + sampleEmit, +}; diff --git a/components/slack_v2_test/sources/new-message-in-channels/test-event.mjs b/components/slack_v2_test/sources/new-message-in-channels/test-event.mjs new file mode 100644 index 0000000000000..3f87589e9539f --- /dev/null +++ b/components/slack_v2_test/sources/new-message-in-channels/test-event.mjs @@ -0,0 +1,45 @@ +export default { + "client_msg_id": "1a7b4cd8-7c83-4f6e-92f8-bfbd4f77d888", + "type": "message", + "text": "Hello <@U06MDSMHK7B>, I’ve registered the task here: ", + "user": "tuleanphuonghh", + "ts": "1702506383.129070", + "blocks": [ + { + "type": "rich_text", + "block_id": "Gh1p", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Hello " + }, + { + "type": "user", + "user_id": "U06MDSMHK7B" + }, + { + "type": "text", + "text": ", I’ve registered the task here: " + }, + { + "type": "link", + "url": "https://github.com/DreamPipe/Dreampipe/issues/8395" + } + ] + } + ] + } + ], + "team": "Dreampipe Users", + "thread_ts": "1702504303.421340", + "parent_user_id": "U06MDSMHK7B", + "channel": "support", + "event_ts": "1702506383.129070", + "channel_type": "channel", + "user_id": "U04DXTI5SRG", + "channel_id": "CPUIZSV6B", + "team_id": "TNZFYHTCG" + } \ No newline at end of file diff --git a/components/slack_v2_test/sources/new-reaction-added/new-reaction-added.mjs b/components/slack_v2_test/sources/new-reaction-added/new-reaction-added.mjs new file mode 100644 index 0000000000000..ec594ea51aa6b --- /dev/null +++ b/components/slack_v2_test/sources/new-reaction-added/new-reaction-added.mjs @@ -0,0 +1,109 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "slack_v2_test-new-reaction-added", + name: "New Reaction Added (Instant)", + version: "1.1.26", + description: "Emit new event when a member has added an emoji reaction to a message", + type: "source", + dedupe: "unique", + props: { + ...common.props, + conversations: { + propDefinition: [ + common.props.slack, + "conversation", + ], + type: "string[]", + label: "Channels", + description: "Select one or more channels to monitor for new messages.", + optional: true, + }, + // eslint-disable-next-line pipedream/props-description,pipedream/props-label + slackApphook: { + type: "$.interface.apphook", + appProp: "slack", + async eventNames() { + if (this.conversations?.length) { + const conversations = []; + for (const conversation of this.conversations) { + conversations.push(`reaction_added:${conversation}`); + } + return conversations; + } + + return [ + "reaction_added", + ]; + }, + }, + ignoreBot: { + propDefinition: [ + common.props.slack, + "ignoreBot", + ], + }, + iconEmoji: { + propDefinition: [ + common.props.slack, + "icon_emoji", + ], + description: "Select one or more emojis to use as a filter. E.g. `fire, email`", + type: "string[]", + optional: true, + }, + includeUserData: { + label: "Include User Data", + description: "Include user object in the response. Default `false`", + type: "boolean", + optional: true, + default: false, + }, + }, + methods: { + ...common.methods, + getSummary() { + return "New reaction added"; + }, + async processEvent(event) { + let iconEmojiParsed = []; + + try { + iconEmojiParsed = typeof this.iconEmoji === "string" ? + JSON.parse(this.iconEmoji) : + this.iconEmoji; + } catch (error) { + iconEmojiParsed = this.iconEmoji.replace(/\s+/g, "").split(","); + } + + if ( + ((this.ignoreBot) && (event.subtype == "bot_message" || event.bot_id)) || + (iconEmojiParsed?.length > 0 && !iconEmojiParsed.includes(event.reaction)) + ) { + return; + } + + if (this.includeUserData) { + const userResponse = await this.slack.usersInfo({ + user: event.user, + }); + const itemUserResponse = await this.slack.usersInfo({ + user: event.user, + }); + + event.userInfo = userResponse.user; + event.itemUserInfo = itemUserResponse.user; + } + + event.message = await this.getMessage({ + channel: event.item.channel, + event_ts: event.item.ts, + }); + + return event; + }, + }, + sampleEmit, +}; diff --git a/components/slack_v2_test/sources/new-reaction-added/test-event.mjs b/components/slack_v2_test/sources/new-reaction-added/test-event.mjs new file mode 100644 index 0000000000000..106603e2da1c3 --- /dev/null +++ b/components/slack_v2_test/sources/new-reaction-added/test-event.mjs @@ -0,0 +1,193 @@ +export default { + "type": "reaction_added", + "user": "US676PZLY", + "reaction": "squirrel", + "item": { + "type": "message", + "channel": "CS8319KD5", + "ts": "1716405857.659549" + }, + "item_user": "US676PZLY", + "event_ts": "1716406183.000100", + "userInfo": { + "id": "US676PZLY", + "team_id": "TS8319547", + "name": "test.user", + "deleted": false, + "color": "9f69e7", + "real_name": "Test User", + "tz": "America/New_York", + "tz_label": "Eastern Daylight Time", + "tz_offset": -14400, + "profile": { + "title": "", + "phone": "", + "skype": "", + "real_name": "Test User", + "real_name_normalized": "Test User", + "display_name": "", + "display_name_normalized": "", + "fields": null, + "status_text": "", + "status_emoji": "", + "status_emoji_display_info": [], + "status_expiration": 0, + "avatar_hash": "g010b11df3bb", + "email": "test@sample.com", + "first_name": "Test", + "last_name": "User", + "status_text_canonical": "", + "team": "TS8319547" + }, + "is_admin": true, + "is_owner": true, + "is_primary_owner": true, + "is_restricted": false, + "is_ultra_restricted": false, + "is_bot": false, + "is_app_user": false, + "updated": 1703787612, + "is_email_confirmed": true, + "has_2fa": false, + "who_can_share_contact_card": "EVERYONE" + }, + "itemUserInfo": { + "id": "US676PZLY", + "team_id": "TS8319547", + "name": "test.user", + "deleted": false, + "color": "9f69e7", + "real_name": "Test User", + "tz": "America/New_York", + "tz_label": "Eastern Daylight Time", + "tz_offset": -14400, + "profile": { + "title": "", + "phone": "", + "skype": "", + "real_name": "Test User", + "real_name_normalized": "Test User", + "display_name": "", + "display_name_normalized": "", + "fields": null, + "status_text": "", + "status_emoji": "", + "status_emoji_display_info": [], + "status_expiration": 0, + "avatar_hash": "g010b11df3bb", + "email": "test@sample.com", + "first_name": "Test", + "last_name": "User", + "status_text_canonical": "", + "team": "TS8319547" + }, + "is_admin": true, + "is_owner": true, + "is_primary_owner": true, + "is_restricted": false, + "is_ultra_restricted": false, + "is_bot": false, + "is_app_user": false, + "updated": 1703787612, + "is_email_confirmed": true, + "has_2fa": false, + "who_can_share_contact_card": "EVERYONE" + }, + "message": { + "ok": true, + "messages": [ + { + "user": "US676PZLY", + "type": "message", + "ts": "1716405857.659549", + "client_msg_id": "fd68d844-687e-41bf-8475-4215bef572c7", + "text": "hello", + "team": "TS8319547", + "blocks": [ + { + "type": "rich_text", + "block_id": "ZL1yL", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "hello" + } + ] + } + ] + } + ], + "reactions": [ + { + "name": "squirrel", + "users": [ + "US676PZLY" + ], + "count": 1 + } + ] + } + ], + "has_more": false, + "response_metadata": { + "scopes": [ + "identify", + "commands", + "channels:history", + "groups:history", + "im:history", + "mpim:history", + "channels:read", + "emoji:read", + "files:read", + "groups:read", + "im:read", + "mpim:read", + "reactions:read", + "reminders:read", + "search:read", + "stars:read", + "team:read", + "users:read", + "users:read.email", + "pins:read", + "usergroups:read", + "dnd:read", + "users.profile:read", + "channels:write", + "chat:write:user", + "chat:write:bot", + "files:write:user", + "groups:write", + "im:write", + "mpim:write", + "reactions:write", + "reminders:write", + "stars:write", + "users:write", + "pins:write", + "usergroups:write", + "dnd:write", + "users.profile:write", + "links:read", + "links:write", + "remote_files:share", + "remote_files:read", + "bookmarks:write", + "calls:write", + "calls:read" + ], + "acceptedScopes": [ + "channels:history", + "groups:history", + "mpim:history", + "im:history", + "read" + ] + } + }, + "pipedream_msg_id": "pd_1716406186371_xezly8lgzn" +} \ No newline at end of file diff --git a/components/slack_v2_test/sources/new-saved-message/new-saved-message.mjs b/components/slack_v2_test/sources/new-saved-message/new-saved-message.mjs new file mode 100644 index 0000000000000..9593670d77095 --- /dev/null +++ b/components/slack_v2_test/sources/new-saved-message/new-saved-message.mjs @@ -0,0 +1,32 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "slack_v2_test-new-saved-message", + name: "New Saved Message (Instant)", + version: "0.0.6", + description: "Emit new event when a message is saved. Note: The endpoint is marked as deprecated, and Slack might shut this off at some point down the line.", + type: "source", + dedupe: "unique", + props: { + ...common.props, + // eslint-disable-next-line pipedream/props-description,pipedream/props-label + slackApphook: { + type: "$.interface.apphook", + appProp: "slack", + async eventNames() { + return [ + "star_added", + ]; + }, + }, + }, + methods: { + ...common.methods, + getSummary() { + return "New saved message"; + }, + }, + sampleEmit, +}; diff --git a/components/slack_v2_test/sources/new-saved-message/test-event.mjs b/components/slack_v2_test/sources/new-saved-message/test-event.mjs new file mode 100644 index 0000000000000..433e5bcb8d4ec --- /dev/null +++ b/components/slack_v2_test/sources/new-saved-message/test-event.mjs @@ -0,0 +1,37 @@ +export default { + "type": "star_added", + "user": "US676PZLY", + "item": { + "type": "message", + "channel": "C055ECVUMLN", + "message": { + "user": "US676PZLY", + "type": "message", + "ts": "1718379912.272779", + "client_msg_id": "def19b3b-4283-47bd-a2da-f32b35c0329c", + "text": "hello", + "team": "TS8319547", + "blocks": [ + { + "type": "rich_text", + "block_id": "ZL1yL", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "hello" + } + ] + } + ] + } + ], + "permalink": "https://michellestest-j1q3506.slack.com/archives/C055ECVUMLN/p1718379912272779" + }, + "date_create": 1718385156 + }, + "event_ts": "1718385156.694322", + "pipedream_msg_id": "pd_1718385158733_tl8yx25evl" +} \ No newline at end of file diff --git a/components/slack_v2_test/sources/new-user-added/new-user-added.mjs b/components/slack_v2_test/sources/new-user-added/new-user-added.mjs new file mode 100644 index 0000000000000..c14c45e163aa2 --- /dev/null +++ b/components/slack_v2_test/sources/new-user-added/new-user-added.mjs @@ -0,0 +1,32 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "slack_v2_test-new-user-added", + name: "New User Added (Instant)", + version: "0.0.4", + description: "Emit new event when a new member joins a workspace.", + type: "source", + dedupe: "unique", + props: { + ...common.props, + // eslint-disable-next-line pipedream/props-description,pipedream/props-label + slackApphook: { + type: "$.interface.apphook", + appProp: "slack", + async eventNames() { + return [ + "team_join", + ]; + }, + }, + }, + methods: { + ...common.methods, + getSummary({ user: { name } }) { + return `New User: ${name}`; + }, + }, + sampleEmit, +}; diff --git a/components/slack_v2_test/sources/new-user-added/test-event.mjs b/components/slack_v2_test/sources/new-user-added/test-event.mjs new file mode 100644 index 0000000000000..3bcd19da42f6a --- /dev/null +++ b/components/slack_v2_test/sources/new-user-added/test-event.mjs @@ -0,0 +1,48 @@ +export default { + "type": "team_join", + "user": { + "id": "U080GULP8SD", + "team_id": "TS8319547", + "name": "", + "deleted": false, + "color": "9b3b45", + "real_name": "", + "tz": "America/New_York", + "tz_label": "Eastern Standard Time", + "tz_offset": -18000, + "profile": { + "title": "", + "phone": "", + "skype": "", + "real_name": "", + "real_name_normalized": "", + "display_name": "", + "display_name_normalized": "", + "fields": {}, + "status_text": "", + "status_emoji": "", + "status_emoji_display_info": [], + "status_expiration": 0, + "avatar_hash": "g96b6e8b38c2", + "email": "", + "first_name": "", + "last_name": "", + "status_text_canonical": "", + "team": "TS8319547" + }, + "is_admin": false, + "is_owner": false, + "is_primary_owner": false, + "is_restricted": false, + "is_ultra_restricted": false, + "is_bot": false, + "is_app_user": false, + "updated": 1731094476, + "is_email_confirmed": true, + "who_can_share_contact_card": "EVERYONE", + "presence": "away" + }, + "cache_ts": 1731094476, + "event_ts": "1731094477.001400", + "pipedream_msg_id": "pd_1731094479305_v1ic236by8" +} \ No newline at end of file diff --git a/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs b/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs new file mode 100644 index 0000000000000..e953ac346fb1d --- /dev/null +++ b/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs @@ -0,0 +1,117 @@ +import common from "../common/base.mjs"; +import constants from "../common/constants.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "slack_v2_test-new-user-mention", + name: "New User Mention (Instant)", + version: "0.0.8", + description: "Emit new event when a username or specific keyword is mentioned in a channel", + type: "source", + dedupe: "unique", + props: { + ...common.props, + conversations: { + propDefinition: [ + common.props.slack, + "conversation", + ], + type: "string[]", + label: "Channels", + description: "Select one or more channels to monitor for new messages.", + optional: true, + }, + // eslint-disable-next-line pipedream/props-description,pipedream/props-label + slackApphook: { + type: "$.interface.apphook", + appProp: "slack", + async eventNames() { + return this.conversations || [ + "message", + ]; + }, + }, + user: { + propDefinition: [ + common.props.slack, + "user", + ], + }, + keyword: { + propDefinition: [ + common.props.slack, + "keyword", + ], + optional: true, + }, + ignoreBot: { + propDefinition: [ + common.props.slack, + "ignoreBot", + ], + }, + }, + methods: { + ...common.methods, + getSummary() { + return "New mention received"; + }, + async processEvent(event) { + const { + type: msgType, + subtype, + bot_id: botId, + text, + blocks = [], + } = event; + const [ + { + elements: [ + { elements = [] } = {}, + ] = [], + } = {}, + ] = blocks; + + if (msgType !== "message") { + console.log(`Ignoring event with unexpected type "${msgType}"`); + return; + } + + // This source is designed to just emit an event for each new message received. + // Due to inconsistencies with the shape of message_changed and message_deleted + // events, we are ignoring them for now. If you want to handle these types of + // events, feel free to change this code!! + if (subtype && !constants.ALLOWED_SUBTYPES.includes(subtype)) { + console.log(`Ignoring message with subtype. "${subtype}"`); + return; + } + + if ((this.ignoreBot) && (subtype === constants.SUBTYPE.BOT_MESSAGE || botId)) { + return; + } + + let emitEvent = false; + if (elements) { + let userMatch = false; + for (const item of elements) { + if (item.user_id && item.user_id === this.user) { + userMatch = true; + break; + } + } + if (userMatch && (!this.keyword || text.indexOf(this.keyword) !== -1)) { + emitEvent = true; + } + } + if (subtype === constants.SUBTYPE.PD_HISTORY_MESSAGE) { + emitEvent = true; + } + + if (emitEvent) { + return event; + } + }, + }, + sampleEmit, +}; diff --git a/components/slack_v2_test/sources/new-user-mention/test-event.mjs b/components/slack_v2_test/sources/new-user-mention/test-event.mjs new file mode 100644 index 0000000000000..7c85b12599e3d --- /dev/null +++ b/components/slack_v2_test/sources/new-user-mention/test-event.mjs @@ -0,0 +1,28 @@ +export default { + "user": "US676PZLY", + "type": "message", + "ts": "1716404766.096289", + "client_msg_id": "b26387fd-5afe-46a9-bf63-a7aabd6fb40f", + "text": "hello", + "team": "TS8319547", + "blocks": [ + { + "type": "rich_text", + "block_id": "aY6KK", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "hello" + } + ] + } + ] + } + ], + "channel": "CS8319KD5", + "event_ts": "1716404766.096289", + "channel_type": "channel" +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a6e00927c63e9..e3410d187d4fc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30051,22 +30051,22 @@ packages: superagent@3.8.1: resolution: {integrity: sha512-VMBFLYgFuRdfeNQSMLbxGSLfmXL/xc+OO+BZp41Za/NRDBet/BNbkRJrYzCUu0u4GU0i/ml2dtT8b9qgkw9z6Q==} engines: {node: '>= 4.0'} - deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net + deprecated: Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at . superagent@4.1.0: resolution: {integrity: sha512-FT3QLMasz0YyCd4uIi5HNe+3t/onxMyEho7C3PSqmti3Twgy2rXT4fmkTz6wRL6bTF4uzPcfkUCa8u4JWHw8Ag==} engines: {node: '>= 6.0'} - deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net + deprecated: Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at . superagent@5.3.1: resolution: {integrity: sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ==} engines: {node: '>= 7.0.0'} - deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net + deprecated: Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at . superagent@7.1.6: resolution: {integrity: sha512-gZkVCQR1gy/oUXr+kxJMLDjla434KmSOKbx5iGD30Ql+AkJQ/YlPKECJy2nhqOsHLjGHzoDTXNSjhnvWhzKk7g==} engines: {node: '>=6.4.0 <13 || >=14'} - deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net + deprecated: Please downgrade to v7.1.5 if you need IE/ActiveXObject support OR upgrade to v8.0.0 as we no longer support IE and published an incorrect patch version (see https://github.com/visionmedia/superagent/issues/1731) supports-color@2.0.0: resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==} From 5c46a1f6ed530309049541507ec0433d4aa5de8e Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Thu, 31 Jul 2025 20:29:48 -0400 Subject: [PATCH 02/34] update pnpm-lock.yaml --- pnpm-lock.yaml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e3410d187d4fc..268a7bcb4d275 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12750,6 +12750,21 @@ importers: components/slack_demo_app_1: {} + components/slack_v2_test: + dependencies: + '@pipedream/platform': + specifier: ^3.1.0 + version: 3.1.0 + '@slack/web-api': + specifier: ^7.9.0 + version: 7.9.1 + async-retry: + specifier: ^1.3.3 + version: 1.3.3 + lodash: + specifier: ^4.17.21 + version: 4.17.21 + components/slicktext: dependencies: '@pipedream/platform': @@ -37391,6 +37406,8 @@ snapshots: '@putout/operator-filesystem': 5.0.0(putout@36.13.1(eslint@8.57.1)(typescript@5.6.3)) '@putout/operator-json': 2.2.0 putout: 36.13.1(eslint@8.57.1)(typescript@5.6.3) + transitivePeerDependencies: + - supports-color '@putout/operator-regexp@1.0.0(putout@36.13.1(eslint@8.57.1)(typescript@5.6.3))': dependencies: From c53df88c920fd069e953b1afd09a9288696c3111 Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Mon, 4 Aug 2025 14:12:14 -0400 Subject: [PATCH 03/34] fix undefined usersToAdd in Update Group Members action --- .../actions/update-group-members/update-group-members.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/slack_v2_test/actions/update-group-members/update-group-members.mjs b/components/slack_v2_test/actions/update-group-members/update-group-members.mjs index bfe361f52ee64..4f8ab07d7380f 100644 --- a/components/slack_v2_test/actions/update-group-members/update-group-members.mjs +++ b/components/slack_v2_test/actions/update-group-members/update-group-members.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-update-group-members", name: "Update Groups Members", description: "Update the list of users for a User Group. [See the documentation](https://api.slack.com/methods/usergroups.users.update)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { slack, @@ -46,8 +46,8 @@ export default { async run({ $ }) { const { userGroup, - usersToAdd, - usersToRemove, + usersToAdd = [], + usersToRemove = [], team, } = this; let { users } = await this.slack.listGroupMembers({ From 76c25782996a326be1094c45e71a8a0b03ddbf77 Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Mon, 4 Aug 2025 14:13:12 -0400 Subject: [PATCH 04/34] update pnpm-lock.yaml --- pnpm-lock.yaml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 268a7bcb4d275..7cda7132028a1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17705,12 +17705,12 @@ packages: '@datadog/sketches-js@2.1.1': resolution: {integrity: sha512-d5RjycE+MObE/hU+8OM5Zp4VjTwiPLRa8299fj7muOmR16fb942z8byoMbCErnGh0lBevvgkGrLclQDvINbIyg==} - '@definitelytyped/header-parser@0.2.19': - resolution: {integrity: sha512-zu+RxQpUCgorYUQZoyyrRIn9CljL1CeM4qak3NDeMO1r7tjAkodfpAGnVzx/6JR2OUk0tAgwmZxNMSwd9LVgxw==} + '@definitelytyped/header-parser@0.2.20': + resolution: {integrity: sha512-97YPAlUo8XjWNtZ+6k+My+50/ljE2iX6KEPjOZ1Az1RsZdKwJ6taAX3F5g6SY1SJr50bzdm2RZzyQNdRmHcs4w==} engines: {node: '>=18.18.0'} - '@definitelytyped/typescript-versions@0.1.8': - resolution: {integrity: sha512-iz6q9aTwWW7CzN2g8jFQfZ955D63LA+wdIAKz4+2pCc/7kokmEHie1/jVWSczqLFOlmH+69bWQxIurryBP/sig==} + '@definitelytyped/typescript-versions@0.1.9': + resolution: {integrity: sha512-Qjalw9eNlcTjXhzx0Q6kHKuRCOUt/M5RGGRGKsiYlm/nveGvPX9liZSQlGXZVwyQ5I9qvq/GdaWiPchQ+ZXOrQ==} engines: {node: '>=18.18.0'} '@definitelytyped/utils@0.1.8': @@ -34936,13 +34936,13 @@ snapshots: '@datadog/sketches-js@2.1.1': {} - '@definitelytyped/header-parser@0.2.19': + '@definitelytyped/header-parser@0.2.20': dependencies: - '@definitelytyped/typescript-versions': 0.1.8 + '@definitelytyped/typescript-versions': 0.1.9 '@definitelytyped/utils': 0.1.8 semver: 7.7.2 - '@definitelytyped/typescript-versions@0.1.8': {} + '@definitelytyped/typescript-versions@0.1.9': {} '@definitelytyped/utils@0.1.8': dependencies: @@ -42305,7 +42305,7 @@ snapshots: dts-critic@3.3.11(typescript@5.7.2): dependencies: - '@definitelytyped/header-parser': 0.2.19 + '@definitelytyped/header-parser': 0.2.20 command-exists: 1.2.9 rimraf: 3.0.2 semver: 6.3.1 @@ -42317,8 +42317,8 @@ snapshots: dtslint@4.2.1(typescript@5.7.2): dependencies: - '@definitelytyped/header-parser': 0.2.19 - '@definitelytyped/typescript-versions': 0.1.8 + '@definitelytyped/header-parser': 0.2.20 + '@definitelytyped/typescript-versions': 0.1.9 '@definitelytyped/utils': 0.1.8 dts-critic: 3.3.11(typescript@5.7.2) fs-extra: 6.0.1 From 46410b26a7773296f4b0f61f1833313f68b04898 Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Thu, 4 Sep 2025 20:58:07 -0400 Subject: [PATCH 05/34] List users by their real names in DM dropdowns --- components/slack_v2_test/slack_v2_test.app.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/slack_v2_test/slack_v2_test.app.mjs b/components/slack_v2_test/slack_v2_test.app.mjs index 7e72734739261..d84b0809e3382 100644 --- a/components/slack_v2_test/slack_v2_test.app.mjs +++ b/components/slack_v2_test/slack_v2_test.app.mjs @@ -571,7 +571,7 @@ export default { for (const user of users) { if (ids.includes(user.id)) { - userNames[user.id] = user.name; + userNames[user.id] = user.profile.real_name; } } From b42e22bc99cdf136089ee2b820d2af85af5f96a9 Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Thu, 4 Sep 2025 21:00:43 -0400 Subject: [PATCH 06/34] bump slack_v2_test component versions --- .../actions/add-emoji-reaction/add-emoji-reaction.mjs | 2 +- .../slack_v2_test/actions/approve-workflow/approve-workflow.mjs | 2 +- .../slack_v2_test/actions/archive-channel/archive-channel.mjs | 2 +- .../slack_v2_test/actions/create-channel/create-channel.mjs | 2 +- .../slack_v2_test/actions/create-reminder/create-reminder.mjs | 2 +- components/slack_v2_test/actions/delete-file/delete-file.mjs | 2 +- .../slack_v2_test/actions/delete-message/delete-message.mjs | 2 +- components/slack_v2_test/actions/find-message/find-message.mjs | 2 +- .../actions/find-user-by-email/find-user-by-email.mjs | 2 +- components/slack_v2_test/actions/get-file/get-file.mjs | 2 +- .../actions/invite-user-to-channel/invite-user-to-channel.mjs | 2 +- components/slack_v2_test/actions/kick-user/kick-user.mjs | 2 +- .../slack_v2_test/actions/list-channels/list-channels.mjs | 2 +- components/slack_v2_test/actions/list-files/list-files.mjs | 2 +- .../actions/list-group-members/list-group-members.mjs | 2 +- .../actions/list-members-in-channel/list-members-in-channel.mjs | 2 +- components/slack_v2_test/actions/list-replies/list-replies.mjs | 2 +- components/slack_v2_test/actions/list-users/list-users.mjs | 2 +- .../actions/reply-to-a-message/reply-to-a-message.mjs | 2 +- .../actions/send-block-kit-message/send-block-kit-message.mjs | 2 +- .../actions/send-large-message/send-large-message.mjs | 2 +- .../actions/send-message-advanced/send-message-advanced.mjs | 2 +- .../actions/send-message-to-channel/send-message-to-channel.mjs | 2 +- .../send-message-to-user-or-group.mjs | 2 +- components/slack_v2_test/actions/send-message/send-message.mjs | 2 +- .../actions/set-channel-description/set-channel-description.mjs | 2 +- .../actions/set-channel-topic/set-channel-topic.mjs | 2 +- components/slack_v2_test/actions/set-status/set-status.mjs | 2 +- .../actions/update-group-members/update-group-members.mjs | 2 +- .../slack_v2_test/actions/update-message/update-message.mjs | 2 +- .../slack_v2_test/actions/update-profile/update-profile.mjs | 2 +- components/slack_v2_test/actions/upload-file/upload-file.mjs | 2 +- .../actions/verify-slack-signature/verify-slack-signature.mjs | 2 +- .../sources/new-channel-created/new-channel-created.mjs | 2 +- .../sources/new-direct-message/new-direct-message.mjs | 2 +- .../new-interaction-event-received.mjs | 2 +- .../sources/new-keyword-mention/new-keyword-mention.mjs | 2 +- .../sources/new-message-in-channels/new-message-in-channels.mjs | 2 +- .../sources/new-reaction-added/new-reaction-added.mjs | 2 +- .../sources/new-saved-message/new-saved-message.mjs | 2 +- .../slack_v2_test/sources/new-user-added/new-user-added.mjs | 2 +- .../slack_v2_test/sources/new-user-mention/new-user-mention.mjs | 2 +- 42 files changed, 42 insertions(+), 42 deletions(-) diff --git a/components/slack_v2_test/actions/add-emoji-reaction/add-emoji-reaction.mjs b/components/slack_v2_test/actions/add-emoji-reaction/add-emoji-reaction.mjs index 9e2fb6e5860e9..fb90d7e624d06 100644 --- a/components/slack_v2_test/actions/add-emoji-reaction/add-emoji-reaction.mjs +++ b/components/slack_v2_test/actions/add-emoji-reaction/add-emoji-reaction.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-add-emoji-reaction", name: "Add Emoji Reaction", description: "Add an emoji reaction to a message. [See the documentation](https://api.slack.com/methods/reactions.add)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/approve-workflow/approve-workflow.mjs b/components/slack_v2_test/actions/approve-workflow/approve-workflow.mjs index c6d730595742d..eb5623f5923a5 100644 --- a/components/slack_v2_test/actions/approve-workflow/approve-workflow.mjs +++ b/components/slack_v2_test/actions/approve-workflow/approve-workflow.mjs @@ -5,7 +5,7 @@ export default { key: "slack_v2_test-approve-workflow", name: "Approve Workflow", description: "Suspend the workflow until approved by a Slack message. [See the documentation](https://pipedream.com/docs/code/nodejs/rerun#flowsuspend)", - version: "0.1.0", + version: "0.1.1", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/archive-channel/archive-channel.mjs b/components/slack_v2_test/actions/archive-channel/archive-channel.mjs index 783c2d93f97f7..a56c6051955db 100644 --- a/components/slack_v2_test/actions/archive-channel/archive-channel.mjs +++ b/components/slack_v2_test/actions/archive-channel/archive-channel.mjs @@ -5,7 +5,7 @@ export default { key: "slack_v2_test-archive-channel", name: "Archive Channel", description: "Archive a channel. [See the documentation](https://api.slack.com/methods/conversations.archive)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/create-channel/create-channel.mjs b/components/slack_v2_test/actions/create-channel/create-channel.mjs index b3e28d5fce400..58416eb2f5162 100644 --- a/components/slack_v2_test/actions/create-channel/create-channel.mjs +++ b/components/slack_v2_test/actions/create-channel/create-channel.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-create-channel", name: "Create a Channel", description: "Create a new channel. [See the documentation](https://api.slack.com/methods/conversations.create)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/create-reminder/create-reminder.mjs b/components/slack_v2_test/actions/create-reminder/create-reminder.mjs index dc0d86881ad62..d93bf766124f4 100644 --- a/components/slack_v2_test/actions/create-reminder/create-reminder.mjs +++ b/components/slack_v2_test/actions/create-reminder/create-reminder.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-create-reminder", name: "Create Reminder", description: "Create a reminder. [See the documentation](https://api.slack.com/methods/reminders.add)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/delete-file/delete-file.mjs b/components/slack_v2_test/actions/delete-file/delete-file.mjs index f71de46aa266e..ce49294de4a53 100644 --- a/components/slack_v2_test/actions/delete-file/delete-file.mjs +++ b/components/slack_v2_test/actions/delete-file/delete-file.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-delete-file", name: "Delete File", description: "Delete a file. [See the documentation](https://api.slack.com/methods/files.delete)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/delete-message/delete-message.mjs b/components/slack_v2_test/actions/delete-message/delete-message.mjs index 896856940d54b..103f6572d827a 100644 --- a/components/slack_v2_test/actions/delete-message/delete-message.mjs +++ b/components/slack_v2_test/actions/delete-message/delete-message.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-delete-message", name: "Delete Message", description: "Delete a message. [See the documentation](https://api.slack.com/methods/chat.delete)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/find-message/find-message.mjs b/components/slack_v2_test/actions/find-message/find-message.mjs index 3e6d4d3efb165..34169010d595d 100644 --- a/components/slack_v2_test/actions/find-message/find-message.mjs +++ b/components/slack_v2_test/actions/find-message/find-message.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-find-message", name: "Find Message", description: "Find a Slack message. [See the documentation](https://api.slack.com/methods/search.messages)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/find-user-by-email/find-user-by-email.mjs b/components/slack_v2_test/actions/find-user-by-email/find-user-by-email.mjs index 6043183bdc0c9..b16b0d1ecbb8a 100644 --- a/components/slack_v2_test/actions/find-user-by-email/find-user-by-email.mjs +++ b/components/slack_v2_test/actions/find-user-by-email/find-user-by-email.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-find-user-by-email", name: "Find User by Email", description: "Find a user by matching against their email. [See the documentation](https://api.slack.com/methods/users.lookupByEmail)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/get-file/get-file.mjs b/components/slack_v2_test/actions/get-file/get-file.mjs index 015d58b4d6ee6..946e2697dfb77 100644 --- a/components/slack_v2_test/actions/get-file/get-file.mjs +++ b/components/slack_v2_test/actions/get-file/get-file.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-get-file", name: "Get File", description: "Return information about a file. [See the documentation](https://api.slack.com/methods/files.info)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/invite-user-to-channel/invite-user-to-channel.mjs b/components/slack_v2_test/actions/invite-user-to-channel/invite-user-to-channel.mjs index 2b6074373c0c7..c879838aedbdd 100644 --- a/components/slack_v2_test/actions/invite-user-to-channel/invite-user-to-channel.mjs +++ b/components/slack_v2_test/actions/invite-user-to-channel/invite-user-to-channel.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-invite-user-to-channel", name: "Invite User to Channel", description: "Invite a user to an existing channel. [See the documentation](https://api.slack.com/methods/conversations.invite)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/kick-user/kick-user.mjs b/components/slack_v2_test/actions/kick-user/kick-user.mjs index 4d4e87ff6a25d..cbbc5260ff2e0 100644 --- a/components/slack_v2_test/actions/kick-user/kick-user.mjs +++ b/components/slack_v2_test/actions/kick-user/kick-user.mjs @@ -5,7 +5,7 @@ export default { key: "slack_v2_test-kick-user", name: "Kick User", description: "Remove a user from a conversation. [See the documentation](https://api.slack.com/methods/conversations.kick)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/list-channels/list-channels.mjs b/components/slack_v2_test/actions/list-channels/list-channels.mjs index 131d0ec755152..e382b209558d5 100644 --- a/components/slack_v2_test/actions/list-channels/list-channels.mjs +++ b/components/slack_v2_test/actions/list-channels/list-channels.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-list-channels", name: "List Channels", description: "Return a list of all channels in a workspace. [See the documentation](https://api.slack.com/methods/conversations.list)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/list-files/list-files.mjs b/components/slack_v2_test/actions/list-files/list-files.mjs index 05a4057c10e83..382a258cdcc82 100644 --- a/components/slack_v2_test/actions/list-files/list-files.mjs +++ b/components/slack_v2_test/actions/list-files/list-files.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-list-files", name: "List Files", description: "Return a list of files within a team. [See the documentation](https://api.slack.com/methods/files.list)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/list-group-members/list-group-members.mjs b/components/slack_v2_test/actions/list-group-members/list-group-members.mjs index 8ed45a9ca396e..b3271ab6bb125 100644 --- a/components/slack_v2_test/actions/list-group-members/list-group-members.mjs +++ b/components/slack_v2_test/actions/list-group-members/list-group-members.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-list-group-members", name: "List Group Members", description: "List all users in a User Group. [See the documentation](https://api.slack.com/methods/usergroups.users.list)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/list-members-in-channel/list-members-in-channel.mjs b/components/slack_v2_test/actions/list-members-in-channel/list-members-in-channel.mjs index 70500cc532e59..86ad9ec467ec8 100644 --- a/components/slack_v2_test/actions/list-members-in-channel/list-members-in-channel.mjs +++ b/components/slack_v2_test/actions/list-members-in-channel/list-members-in-channel.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-list-members-in-channel", name: "List Members in Channel", description: "Retrieve members of a channel. [See the documentation](https://api.slack.com/methods/conversations.members)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/list-replies/list-replies.mjs b/components/slack_v2_test/actions/list-replies/list-replies.mjs index 48102a4d0e57b..914f92eee5b52 100644 --- a/components/slack_v2_test/actions/list-replies/list-replies.mjs +++ b/components/slack_v2_test/actions/list-replies/list-replies.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-list-replies", name: "List Replies", description: "Retrieve a thread of messages posted to a conversation. [See the documentation](https://api.slack.com/methods/conversations.replies)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/list-users/list-users.mjs b/components/slack_v2_test/actions/list-users/list-users.mjs index bb6ea138f0bf0..0fd53df22b4d5 100644 --- a/components/slack_v2_test/actions/list-users/list-users.mjs +++ b/components/slack_v2_test/actions/list-users/list-users.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-list-users", name: "List Users", description: "Return a list of all users in a workspace. [See the documentation](https://api.slack.com/methods/users.list)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/reply-to-a-message/reply-to-a-message.mjs b/components/slack_v2_test/actions/reply-to-a-message/reply-to-a-message.mjs index ea0fc62e40c3c..fe53256731041 100644 --- a/components/slack_v2_test/actions/reply-to-a-message/reply-to-a-message.mjs +++ b/components/slack_v2_test/actions/reply-to-a-message/reply-to-a-message.mjs @@ -6,7 +6,7 @@ export default { key: "slack_v2_test-reply-to-a-message", name: "Reply to a Message Thread", description: "Send a message as a threaded reply. See [postMessage](https://api.slack.com/methods/chat.postMessage) or [scheduleMessage](https://api.slack.com/methods/chat.scheduleMessage) docs here", - version: "0.1.1", + version: "0.1.2", type: "action", props: { slack: common.props.slack, diff --git a/components/slack_v2_test/actions/send-block-kit-message/send-block-kit-message.mjs b/components/slack_v2_test/actions/send-block-kit-message/send-block-kit-message.mjs index a1558a0fbdbad..ebceeefa65df5 100644 --- a/components/slack_v2_test/actions/send-block-kit-message/send-block-kit-message.mjs +++ b/components/slack_v2_test/actions/send-block-kit-message/send-block-kit-message.mjs @@ -7,7 +7,7 @@ export default { key: "slack_v2_test-send-block-kit-message", name: "Build and Send a Block Kit Message", description: "Configure custom blocks and send to a channel, group, or user. [See the documentation](https://api.slack.com/tools/block-kit-builder).", - version: "0.5.1", + version: "0.5.2", type: "action", props: { slack: common.props.slack, diff --git a/components/slack_v2_test/actions/send-large-message/send-large-message.mjs b/components/slack_v2_test/actions/send-large-message/send-large-message.mjs index 31d391f59bf1a..b2eaf73f2fbcf 100644 --- a/components/slack_v2_test/actions/send-large-message/send-large-message.mjs +++ b/components/slack_v2_test/actions/send-large-message/send-large-message.mjs @@ -5,7 +5,7 @@ export default { key: "slack_v2_test-send-large-message", name: "Send a Large Message (3000+ characters)", description: "Send a large message (more than 3000 characters) to a channel, group or user. See [postMessage](https://api.slack.com/methods/chat.postMessage) or [scheduleMessage](https://api.slack.com/methods/chat.scheduleMessage) docs here", - version: "0.1.1", + version: "0.1.2", type: "action", props: { slack: common.props.slack, diff --git a/components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs b/components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs index 39bd8871f7fd7..db52bccb03ef3 100644 --- a/components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs +++ b/components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs @@ -7,7 +7,7 @@ export default { key: "slack_v2_test-send-message-advanced", name: "Send Message (Advanced)", description: "Customize advanced setttings and send a message to a channel, group or user. See [postMessage](https://api.slack.com/methods/chat.postMessage) or [scheduleMessage](https://api.slack.com/methods/chat.scheduleMessage) docs here", - version: "0.1.1", + version: "0.1.2", type: "action", props: { slack: common.props.slack, diff --git a/components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs b/components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs index 2b538d64d4f83..2526244fef19a 100644 --- a/components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs +++ b/components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs @@ -6,7 +6,7 @@ export default { key: "slack_v2_test-send-message-to-channel", name: "Send Message to Channel", description: "Send a message to a public or private channel. [See the documentation](https://api.slack.com/methods/chat.postMessage)", - version: "0.1.1", + version: "0.1.2", type: "action", props: { slack: common.props.slack, diff --git a/components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs b/components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs index a9425a0bb6dd4..53a84b67c0ba9 100644 --- a/components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs +++ b/components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs @@ -7,7 +7,7 @@ export default { key: "slack_v2_test-send-message-to-user-or-group", name: "Send Message to User or Group", description: "Send a message to a user or group. [See the documentation](https://api.slack.com/methods/chat.postMessage)", - version: "0.1.1", + version: "0.1.2", type: "action", props: { slack: common.props.slack, diff --git a/components/slack_v2_test/actions/send-message/send-message.mjs b/components/slack_v2_test/actions/send-message/send-message.mjs index 1250fa6042e37..466c14d423007 100644 --- a/components/slack_v2_test/actions/send-message/send-message.mjs +++ b/components/slack_v2_test/actions/send-message/send-message.mjs @@ -6,7 +6,7 @@ export default { key: "slack_v2_test-send-message", name: "Send Message", description: "Send a message to a user, group, private channel or public channel. [See the documentation](https://api.slack.com/methods/chat.postMessage)", - version: "0.1.1", + version: "0.1.2", type: "action", props: { slack: common.props.slack, diff --git a/components/slack_v2_test/actions/set-channel-description/set-channel-description.mjs b/components/slack_v2_test/actions/set-channel-description/set-channel-description.mjs index 8a4bbb793b73a..27459d80ba977 100644 --- a/components/slack_v2_test/actions/set-channel-description/set-channel-description.mjs +++ b/components/slack_v2_test/actions/set-channel-description/set-channel-description.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-set-channel-description", name: "Set Channel Description", description: "Change the description or purpose of a channel. [See the documentation](https://api.slack.com/methods/conversations.setPurpose)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/set-channel-topic/set-channel-topic.mjs b/components/slack_v2_test/actions/set-channel-topic/set-channel-topic.mjs index f2b0f43b9afc0..fa74009e52d81 100644 --- a/components/slack_v2_test/actions/set-channel-topic/set-channel-topic.mjs +++ b/components/slack_v2_test/actions/set-channel-topic/set-channel-topic.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-set-channel-topic", name: "Set Channel Topic", description: "Set the topic on a selected channel. [See the documentation](https://api.slack.com/methods/conversations.setTopic)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/set-status/set-status.mjs b/components/slack_v2_test/actions/set-status/set-status.mjs index 9bb3dcbf358d2..d6917b95b79fa 100644 --- a/components/slack_v2_test/actions/set-status/set-status.mjs +++ b/components/slack_v2_test/actions/set-status/set-status.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-set-status", name: "Set Status", description: "Set the current status for a user. [See the documentation](https://api.slack.com/methods/users.profile.set)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/update-group-members/update-group-members.mjs b/components/slack_v2_test/actions/update-group-members/update-group-members.mjs index 4f8ab07d7380f..c55169e25a33c 100644 --- a/components/slack_v2_test/actions/update-group-members/update-group-members.mjs +++ b/components/slack_v2_test/actions/update-group-members/update-group-members.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-update-group-members", name: "Update Groups Members", description: "Update the list of users for a User Group. [See the documentation](https://api.slack.com/methods/usergroups.users.update)", - version: "0.0.2", + version: "0.0.3", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/update-message/update-message.mjs b/components/slack_v2_test/actions/update-message/update-message.mjs index 234131b1c3cbc..0115dd00c8d55 100644 --- a/components/slack_v2_test/actions/update-message/update-message.mjs +++ b/components/slack_v2_test/actions/update-message/update-message.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-update-message", name: "Update Message", description: "Update a message. [See the documentation](https://api.slack.com/methods/chat.update)", - version: "0.1.1", + version: "0.1.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/update-profile/update-profile.mjs b/components/slack_v2_test/actions/update-profile/update-profile.mjs index 36b7eee62cad3..7865a691761eb 100644 --- a/components/slack_v2_test/actions/update-profile/update-profile.mjs +++ b/components/slack_v2_test/actions/update-profile/update-profile.mjs @@ -5,7 +5,7 @@ export default { key: "slack_v2_test-update-profile", name: "Update Profile", description: "Update basic profile field such as name or title. [See the documentation](https://api.slack.com/methods/users.profile.set)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/upload-file/upload-file.mjs b/components/slack_v2_test/actions/upload-file/upload-file.mjs index 76f836792edba..3e9366bfb278b 100644 --- a/components/slack_v2_test/actions/upload-file/upload-file.mjs +++ b/components/slack_v2_test/actions/upload-file/upload-file.mjs @@ -8,7 +8,7 @@ export default { key: "slack_v2_test-upload-file", name: "Upload File", description: "Upload a file. [See the documentation](https://api.slack.com/messaging/files#uploading_files)", - version: "0.1.1", + version: "0.1.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/verify-slack-signature/verify-slack-signature.mjs b/components/slack_v2_test/actions/verify-slack-signature/verify-slack-signature.mjs index a7588c4b06c03..744ceaec25c54 100644 --- a/components/slack_v2_test/actions/verify-slack-signature/verify-slack-signature.mjs +++ b/components/slack_v2_test/actions/verify-slack-signature/verify-slack-signature.mjs @@ -5,7 +5,7 @@ export default { key: "slack_v2_test-verify-slack-signature", name: "Verify Slack Signature", description: "Verifying requests from Slack, slack signs its requests using a secret that's unique to your app. [See the documentation](https://api.slack.com/authentication/verifying-requests-from-slack)", - version: "0.0.1", + version: "0.0.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/sources/new-channel-created/new-channel-created.mjs b/components/slack_v2_test/sources/new-channel-created/new-channel-created.mjs index a6340578a4477..f47f107fa0cdd 100644 --- a/components/slack_v2_test/sources/new-channel-created/new-channel-created.mjs +++ b/components/slack_v2_test/sources/new-channel-created/new-channel-created.mjs @@ -5,7 +5,7 @@ export default { ...common, key: "slack_v2_test-new-channel-created", name: "New Channel Created (Instant)", - version: "0.0.10", + version: "0.0.11", description: "Emit new event when a new channel is created.", type: "source", dedupe: "unique", diff --git a/components/slack_v2_test/sources/new-direct-message/new-direct-message.mjs b/components/slack_v2_test/sources/new-direct-message/new-direct-message.mjs index ca2663bac71b1..b1448f3006c6d 100644 --- a/components/slack_v2_test/sources/new-direct-message/new-direct-message.mjs +++ b/components/slack_v2_test/sources/new-direct-message/new-direct-message.mjs @@ -5,7 +5,7 @@ export default { ...common, key: "slack_v2_test-new-direct-message", name: "New Direct Message (Instant)", - version: "1.0.24", + version: "1.0.26", description: "Emit new event when a message was posted in a direct message channel", type: "source", dedupe: "unique", diff --git a/components/slack_v2_test/sources/new-interaction-event-received/new-interaction-event-received.mjs b/components/slack_v2_test/sources/new-interaction-event-received/new-interaction-event-received.mjs index 17165b2180168..1d9d0a4c2072e 100644 --- a/components/slack_v2_test/sources/new-interaction-event-received/new-interaction-event-received.mjs +++ b/components/slack_v2_test/sources/new-interaction-event-received/new-interaction-event-received.mjs @@ -3,7 +3,7 @@ import sampleEmit from "./test-event.mjs"; export default { name: "New Interaction Events (Instant)", - version: "0.0.20", + version: "0.0.21", key: "slack_v2_test-new-interaction-event-received", description: "Emit new events on new Slack [interactivity events](https://api.slack.com/interactivity) sourced from [Block Kit interactive elements](https://api.slack.com/interactivity/components), [Slash commands](https://api.slack.com/interactivity/slash-commands), or [Shortcuts](https://api.slack.com/interactivity/shortcuts).", type: "source", diff --git a/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs b/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs index 9266b97e24b08..0e50450261f09 100644 --- a/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs +++ b/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs @@ -6,7 +6,7 @@ export default { ...common, key: "slack_v2_test-new-keyword-mention", name: "New Keyword Mention (Instant)", - version: "0.0.8", + version: "0.0.9", description: "Emit new event when a specific keyword is mentioned in a channel", type: "source", dedupe: "unique", diff --git a/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs b/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs index a7e5a5a4867c1..2d5e9072610c6 100644 --- a/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs +++ b/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs @@ -6,7 +6,7 @@ export default { ...common, key: "slack_v2_test-new-message-in-channels", name: "New Message In Channels (Instant)", - version: "1.0.25", + version: "1.0.26", description: "Emit new event when a new message is posted to one or more channels", type: "source", dedupe: "unique", diff --git a/components/slack_v2_test/sources/new-reaction-added/new-reaction-added.mjs b/components/slack_v2_test/sources/new-reaction-added/new-reaction-added.mjs index ec594ea51aa6b..5c03b71f07b86 100644 --- a/components/slack_v2_test/sources/new-reaction-added/new-reaction-added.mjs +++ b/components/slack_v2_test/sources/new-reaction-added/new-reaction-added.mjs @@ -5,7 +5,7 @@ export default { ...common, key: "slack_v2_test-new-reaction-added", name: "New Reaction Added (Instant)", - version: "1.1.26", + version: "1.1.27", description: "Emit new event when a member has added an emoji reaction to a message", type: "source", dedupe: "unique", diff --git a/components/slack_v2_test/sources/new-saved-message/new-saved-message.mjs b/components/slack_v2_test/sources/new-saved-message/new-saved-message.mjs index 9593670d77095..29f7b3bdbe912 100644 --- a/components/slack_v2_test/sources/new-saved-message/new-saved-message.mjs +++ b/components/slack_v2_test/sources/new-saved-message/new-saved-message.mjs @@ -5,7 +5,7 @@ export default { ...common, key: "slack_v2_test-new-saved-message", name: "New Saved Message (Instant)", - version: "0.0.6", + version: "0.0.7", description: "Emit new event when a message is saved. Note: The endpoint is marked as deprecated, and Slack might shut this off at some point down the line.", type: "source", dedupe: "unique", diff --git a/components/slack_v2_test/sources/new-user-added/new-user-added.mjs b/components/slack_v2_test/sources/new-user-added/new-user-added.mjs index c14c45e163aa2..57ba92505e985 100644 --- a/components/slack_v2_test/sources/new-user-added/new-user-added.mjs +++ b/components/slack_v2_test/sources/new-user-added/new-user-added.mjs @@ -5,7 +5,7 @@ export default { ...common, key: "slack_v2_test-new-user-added", name: "New User Added (Instant)", - version: "0.0.4", + version: "0.0.5", description: "Emit new event when a new member joins a workspace.", type: "source", dedupe: "unique", diff --git a/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs b/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs index e953ac346fb1d..55ed9c5c437a9 100644 --- a/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs +++ b/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs @@ -6,7 +6,7 @@ export default { ...common, key: "slack_v2_test-new-user-mention", name: "New User Mention (Instant)", - version: "0.0.8", + version: "0.0.9", description: "Emit new event when a username or specific keyword is mentioned in a channel", type: "source", dedupe: "unique", From fbc9ff9d54fb5c9e9bbc1927a3c976c8e423ec53 Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Sun, 7 Sep 2025 19:37:46 -0400 Subject: [PATCH 07/34] List users by real names in group DMs in dropdowns --- .../slack_v2_test/slack_v2_test.app.mjs | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/components/slack_v2_test/slack_v2_test.app.mjs b/components/slack_v2_test/slack_v2_test.app.mjs index d84b0809e3382..c0a598b133724 100644 --- a/components/slack_v2_test/slack_v2_test.app.mjs +++ b/components/slack_v2_test/slack_v2_test.app.mjs @@ -32,7 +32,7 @@ export default { const userNames = await this.userNameLookup(userIds); return { options: conversationsResp.conversations.map((c) => ({ - label: `@${userNames[c.user]}`, + label: `${userNames[c.user]}`, value: c.user || c.id, })), context: { @@ -121,24 +121,39 @@ export default { } } const conversationsResp = await this.availableConversations(types.join(), cursor, true); - let conversations, userNames; + let conversations, userIds, usernames, userNames; if (types.includes("im")) { conversations = conversationsResp.conversations; - const userIds = conversations.map(({ user }) => user); - userNames = await this.userNameLookup(userIds); + userIds = conversations.map(({ user }) => user).filter(Boolean); } else { conversations = conversationsResp.conversations.filter((c) => !c.is_im); } + if (types.includes("mpim")) { + usernames = [ + ...new Set(conversations.filter((c) => c.is_mpim).map((c) => c.purpose.value) + .map((v) => v.match(/@[\w.-]+/g) || []) + .flat() + .map((u) => u.slice(1))), + ]; + } + if ((userIds?.length > 0) || (usernames?.length > 0)) { + userNames = await this.userNameLookup(userIds, usernames); + } + return { options: conversations.map((c) => { if (c.is_im) { return { - label: `Direct messaging with: @${userNames[c.user]}`, + label: `Direct messaging with: ${userNames[c.user]}`, value: c.id, }; } else if (c.is_mpim) { + const usernames = c.purpose.value.match(/@[\w.-]+/g) || []; + const realNames = usernames.map((u) => userNames[u.slice(1)] || u); return { - label: c.purpose.value, + label: realNames.length + ? `Group messaging with: ${realNames.join(", ")}` + : c.purpose.value, value: c.id, }; } else { @@ -555,7 +570,7 @@ export default { conversations, }; }, - async userNameLookup(ids = [], throwRateLimitError = true, args = {}) { + async userNameLookup(ids = [], usernames = [], throwRateLimitError = true, args = {}) { let cursor; const userNames = {}; do { @@ -573,10 +588,13 @@ export default { if (ids.includes(user.id)) { userNames[user.id] = user.profile.real_name; } + if (usernames.includes(user.name)) { + userNames[user.name] = user.profile.real_name; + } } cursor = nextCursor; - } while (cursor && Object.keys(userNames).length < ids.length); + } while (cursor && Object.keys(userNames).length < (ids.length + usernames.length)); return userNames; }, /** From 7dc8ef000a83c891e62968a631499de2a0b2a595 Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Sun, 7 Sep 2025 19:52:46 -0400 Subject: [PATCH 08/34] rename and separate userNameLookup and realNameLookup methods --- .../slack_v2_test/slack_v2_test.app.mjs | 50 ++++++++++++++----- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/components/slack_v2_test/slack_v2_test.app.mjs b/components/slack_v2_test/slack_v2_test.app.mjs index c0a598b133724..592d6a65a44eb 100644 --- a/components/slack_v2_test/slack_v2_test.app.mjs +++ b/components/slack_v2_test/slack_v2_test.app.mjs @@ -121,7 +121,7 @@ export default { } } const conversationsResp = await this.availableConversations(types.join(), cursor, true); - let conversations, userIds, usernames, userNames; + let conversations, userIds, userNames, realNames; if (types.includes("im")) { conversations = conversationsResp.conversations; userIds = conversations.map(({ user }) => user).filter(Boolean); @@ -129,30 +129,32 @@ export default { conversations = conversationsResp.conversations.filter((c) => !c.is_im); } if (types.includes("mpim")) { - usernames = [ + userNames = [ ...new Set(conversations.filter((c) => c.is_mpim).map((c) => c.purpose.value) .map((v) => v.match(/@[\w.-]+/g) || []) .flat() .map((u) => u.slice(1))), ]; } - if ((userIds?.length > 0) || (usernames?.length > 0)) { - userNames = await this.userNameLookup(userIds, usernames); + if ((userIds?.length > 0) || (userNames?.length > 0)) { + // Look up real names for userIds and userNames at the same time to + // minimize number of API calls. + realNames = await this.realNameLookup(userIds, userNames); } return { options: conversations.map((c) => { if (c.is_im) { return { - label: `Direct messaging with: ${userNames[c.user]}`, + label: `Direct messaging with: ${realNames[c.user]}`, value: c.id, }; } else if (c.is_mpim) { const usernames = c.purpose.value.match(/@[\w.-]+/g) || []; - const realNames = usernames.map((u) => userNames[u.slice(1)] || u); + const realnames = usernames.map((u) => realNames[u.slice(1)] || u); return { - label: realNames.length - ? `Group messaging with: ${realNames.join(", ")}` + label: realnames.length + ? `Group messaging with: ${realnames.join(", ")}` : c.purpose.value, value: c.id, }; @@ -570,7 +572,7 @@ export default { conversations, }; }, - async userNameLookup(ids = [], usernames = [], throwRateLimitError = true, args = {}) { + async userNameLookup(ids = [], throwRateLimitError = true, args = {}) { let cursor; const userNames = {}; do { @@ -588,14 +590,38 @@ export default { if (ids.includes(user.id)) { userNames[user.id] = user.profile.real_name; } + } + + cursor = nextCursor; + } while (cursor && Object.keys(userNames).length < ids.length); + return userNames; + }, + async realNameLookup(ids = [], usernames = [], throwRateLimitError = true, args = {}) { + let cursor; + const realNames = {}; + do { + const { + members: users, + response_metadata: { next_cursor: nextCursor }, + } = await this.usersList({ + limit: constants.LIMIT, + cursor, + throwRateLimitError, + ...args, + }); + + for (const user of users) { + if (ids.includes(user.id)) { + realNames[user.id] = user.profile.real_name; + } if (usernames.includes(user.name)) { - userNames[user.name] = user.profile.real_name; + realNames[user.name] = user.profile.real_name; } } cursor = nextCursor; - } while (cursor && Object.keys(userNames).length < (ids.length + usernames.length)); - return userNames; + } while (cursor && Object.keys(realNames).length < (ids.length + usernames.length)); + return realNames; }, /** * Checks authentication & identity. From 9a61fa50f50c63a3a42b9dce127583bfd237ebe4 Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Sun, 14 Sep 2025 18:13:28 -0400 Subject: [PATCH 09/34] use bot token to get or list files in v2, user tokens will not have the `files:read` scope --- components/slack_v2_test/slack_v2_test.app.mjs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/components/slack_v2_test/slack_v2_test.app.mjs b/components/slack_v2_test/slack_v2_test.app.mjs index 592d6a65a44eb..d79f28b4fb026 100644 --- a/components/slack_v2_test/slack_v2_test.app.mjs +++ b/components/slack_v2_test/slack_v2_test.app.mjs @@ -483,7 +483,7 @@ export default { return this.$auth.oauth_uid; }, getToken(opts = {}) { - return opts.as_user === false + return (opts.as_bot && this.$auth.bot_token) ? this.$auth.bot_token : this.$auth.oauth_access_token; }, @@ -497,19 +497,21 @@ export default { }); }, async makeRequest({ - method = "", throwRateLimitError = false, as_user, ...args + method = "", throwRateLimitError = false, as_bot, ...args } = {}) { + as_bot = args.as_user === false || as_bot; + const props = method.split("."); const sdk = props.reduce((reduction, prop) => reduction[prop], this.sdk({ - as_user, + as_bot, })); let response; try { response = await this._withRetries(() => sdk(args), throwRateLimitError); } catch (error) { - if (error?.data?.error === "channel_not_found" && as_user === false) { + if (error?.data?.error === "channel_not_found" && as_bot) { throw new ConfigurationError(`${error} Ensure the bot is a member of the channel, or set the **As User** option to true to act on behalf of the authenticated user. `); @@ -859,6 +861,7 @@ export default { args.count ||= constants.LIMIT; return this.makeRequest({ method: "files.list", + as_bot: true, ...args, }); }, @@ -872,6 +875,7 @@ export default { getFileInfo(args = {}) { return this.makeRequest({ method: "files.info", + as_bot: true, ...args, }); }, From a1acbfbc503521daac7c8f767bde23abd4c04dfd Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Mon, 15 Sep 2025 11:05:36 -0400 Subject: [PATCH 10/34] Add addToChannel prop to message-based triggers and remove historical messages for new-keyword-mention source since the search:read scope is no longer requested. --- .../slack_v2_test/slack_v2_test.app.mjs | 7 +++ .../slack_v2_test/sources/common/base.mjs | 22 ++++++++++ .../new-keyword-mention.mjs | 44 +++++-------------- .../new-message-in-channels.mjs | 14 ++++++ .../new-user-mention/new-user-mention.mjs | 8 ++++ 5 files changed, 62 insertions(+), 33 deletions(-) diff --git a/components/slack_v2_test/slack_v2_test.app.mjs b/components/slack_v2_test/slack_v2_test.app.mjs index d79f28b4fb026..31fcd12d5fc4f 100644 --- a/components/slack_v2_test/slack_v2_test.app.mjs +++ b/components/slack_v2_test/slack_v2_test.app.mjs @@ -465,6 +465,13 @@ export default { default: 1, optional: true, }, + addToChannel: { + type: "boolean", + label: "Add app to channel automatically?", + description: "If `true`, the app will be added to the specified channel(s) automatically. If `false`, you must add the app to the channel manually. Defaults to `true`.", + default: true, + optional: false, + }, }, methods: { getChannelLabel(resource) { diff --git a/components/slack_v2_test/sources/common/base.mjs b/components/slack_v2_test/sources/common/base.mjs index fdffe795a98ba..3ebb083e977a5 100644 --- a/components/slack_v2_test/sources/common/base.mjs +++ b/components/slack_v2_test/sources/common/base.mjs @@ -160,6 +160,28 @@ export default { }, ); }, + async getBotInfo() { + return await this.slack.authTest({ + as_bot: true, + }); + }, + async addAppToChannels(channelIds = []) { + const { user_id: botUserId } = await this.getBotInfo(); + // XXX: Don't try to add the app to DM or group DM channels, + // it will result in an error: method_not_supported_for_channel_type + for (const channel of channelIds) { + try { + await this.slack.inviteToConversation({ + channel, + users: botUserId, + }); + } catch (error) { + if (!error.includes("already_in_channel")) { + throw error; + } + } + } + }, processEvent(event) { return event; }, diff --git a/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs b/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs index 0e50450261f09..8105e4bbcf2f8 100644 --- a/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs +++ b/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs @@ -38,6 +38,12 @@ export default { "keyword", ], }, + addToChannel: { + propDefinition: [ + common.props.slack, + "addToChannel", + ], + }, ignoreBot: { propDefinition: [ common.props.slack, @@ -47,43 +53,15 @@ export default { }, hooks: { ...common.hooks, - async deploy() { - // emit historical events - const messages = await this.getMatches({ - query: this.keyword, - sort: "timestamp", - }); - const filteredMessages = this.conversations?.length > 0 - ? messages.filter((message) => this.conversations.includes(message.channel.id)) - : messages; - await this.emitHistoricalEvents(filteredMessages.slice(-25).reverse()); + async activate() { + if (this.addToChannel && this.conversations?.length) { + if (this.conversations[0] === "message") return; + await this.addAppToChannels(this.conversations); + } }, }, methods: { ...common.methods, - async getMatches(params) { - return (await this.slack.searchMessages(params)).messages.matches || []; - }, - async emitHistoricalEvents(messages) { - for (const message of messages) { - const event = await this.processEvent({ - ...message, - subtype: message.subtype || constants.SUBTYPE.PD_HISTORY_MESSAGE, - }); - if (event) { - if (!event.client_msg_id) { - event.pipedream_msg_id = `pd_${Date.now()}_${Math.random().toString(36) - .substr(2, 10)}`; - } - - this.$emit(event, { - id: event.client_msg_id || event.pipedream_msg_id, - summary: this.getSummary(event), - ts: event.event_ts || Date.now(), - }); - } - } - }, getSummary() { return "New keyword mention received"; }, diff --git a/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs b/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs index 2d5e9072610c6..acc6ec1c92934 100644 --- a/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs +++ b/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs @@ -32,6 +32,12 @@ export default { ]; }, }, + addToChannel: { + propDefinition: [ + common.props.slack, + "addToChannel", + ], + }, resolveNames: { propDefinition: [ common.props.slack, @@ -51,6 +57,14 @@ export default { optional: true, }, }, + hooks: { + async activate() { + if (this.addToChannel && this.conversations?.length) { + if (this.conversations[0] === "message") return; + await this.addAppToChannels(this.conversations); + } + }, + }, methods: { ...common.methods, getSummary() { diff --git a/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs b/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs index 55ed9c5c437a9..8f65d1160af13 100644 --- a/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs +++ b/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs @@ -52,6 +52,14 @@ export default { ], }, }, + hooks: { + async activate() { + if (this.addToChannel && this.conversations?.length) { + if (this.conversations[0] === "message") return; + await this.addAppToChannels(this.conversations); + } + }, + }, methods: { ...common.methods, getSummary() { From 5da889a5e50f3f402554e3436254aee77521381c Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Mon, 15 Sep 2025 14:19:58 -0400 Subject: [PATCH 11/34] specify app will be added to non-DM channels only in prop description --- components/slack_v2_test/slack_v2_test.app.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/slack_v2_test/slack_v2_test.app.mjs b/components/slack_v2_test/slack_v2_test.app.mjs index 31fcd12d5fc4f..86354ca335ace 100644 --- a/components/slack_v2_test/slack_v2_test.app.mjs +++ b/components/slack_v2_test/slack_v2_test.app.mjs @@ -468,7 +468,7 @@ export default { addToChannel: { type: "boolean", label: "Add app to channel automatically?", - description: "If `true`, the app will be added to the specified channel(s) automatically. If `false`, you must add the app to the channel manually. Defaults to `true`.", + description: "If `true`, the app will be added to the specified non-DM channel(s) automatically. If `false`, you must add the app to the channel manually. Defaults to `true`.", default: true, optional: false, }, From 522e2ea8ec5b696608f652168e74c28a9da11a29 Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Mon, 15 Sep 2025 14:26:54 -0400 Subject: [PATCH 12/34] omit suggestion to set "Send as User" unless using a 'chat' method --- components/slack_v2_test/slack_v2_test.app.mjs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/components/slack_v2_test/slack_v2_test.app.mjs b/components/slack_v2_test/slack_v2_test.app.mjs index 86354ca335ace..a789b3cdddc0b 100644 --- a/components/slack_v2_test/slack_v2_test.app.mjs +++ b/components/slack_v2_test/slack_v2_test.app.mjs @@ -519,8 +519,15 @@ export default { response = await this._withRetries(() => sdk(args), throwRateLimitError); } catch (error) { if (error?.data?.error === "channel_not_found" && as_bot) { + // If method starts with chat, include the part about "As User" + // Otherwise, just say "Ensure the bot is a member of the channel" + if (method.startsWith("chat.")) { + throw new ConfigurationError(`${error} + Ensure the bot is a member of the channel, or set the **Send as User** option to true to act on behalf of the authenticated user. + `); + } throw new ConfigurationError(`${error} - Ensure the bot is a member of the channel, or set the **As User** option to true to act on behalf of the authenticated user. + Ensure the bot is a member of the channel. `); } throw `${error}`; From 0f6e268d4bd5f6f58bb2fc3ce32bfc8d1d34b02c Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Mon, 15 Sep 2025 14:31:38 -0400 Subject: [PATCH 13/34] check that bot info is valid ignore error when attempting to add app to DM or group DM --- .../slack_v2_test/sources/common/base.mjs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/components/slack_v2_test/sources/common/base.mjs b/components/slack_v2_test/sources/common/base.mjs index 3ebb083e977a5..b768194df3e4d 100644 --- a/components/slack_v2_test/sources/common/base.mjs +++ b/components/slack_v2_test/sources/common/base.mjs @@ -161,14 +161,18 @@ export default { ); }, async getBotInfo() { - return await this.slack.authTest({ + const botInfo = await this.slack.authTest({ as_bot: true, }); + if (!botInfo?.bot_id) { + throw new Error("Could not get bot info. Make sure the Slack app has a bot user."); + } + return botInfo; }, async addAppToChannels(channelIds = []) { - const { user_id: botUserId } = await this.getBotInfo(); - // XXX: Don't try to add the app to DM or group DM channels, - // it will result in an error: method_not_supported_for_channel_type + const { bot_id: botUserId } = await this.getBotInfo(); + // XXX: Trying to add the app to DM or group DM channels results in the + // error: method_not_supported_for_channel_type for (const channel of channelIds) { try { await this.slack.inviteToConversation({ @@ -176,7 +180,10 @@ export default { users: botUserId, }); } catch (error) { - if (!error.includes("already_in_channel")) { + if (![ + "method_not_supported_for_channel_type", + "already_in_channel", + ].some((msg) => error.includes(msg))) { throw error; } } From 94e599d3e4b50c507042ee52a21fe279a9d35476 Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Mon, 15 Sep 2025 17:19:34 -0400 Subject: [PATCH 14/34] open conversation as the bot when sending message as bot --- .../send-message-to-user-or-group.mjs | 1 + components/slack_v2_test/slack_v2_test.app.mjs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs b/components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs index 53a84b67c0ba9..8fe8964896772 100644 --- a/components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs +++ b/components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs @@ -66,6 +66,7 @@ export default { } const { channel: { id } } = await this.openConversation({ users: this.users.join(), + as_bot: this.as_user === false, }); return id; }, diff --git a/components/slack_v2_test/slack_v2_test.app.mjs b/components/slack_v2_test/slack_v2_test.app.mjs index a789b3cdddc0b..754f9ddbef5a5 100644 --- a/components/slack_v2_test/slack_v2_test.app.mjs +++ b/components/slack_v2_test/slack_v2_test.app.mjs @@ -504,9 +504,9 @@ export default { }); }, async makeRequest({ - method = "", throwRateLimitError = false, as_bot, ...args + method = "", throwRateLimitError = false, as_user, as_bot, ...args } = {}) { - as_bot = args.as_user === false || as_bot; + as_bot = as_user === false || as_bot; const props = method.split("."); const sdk = props.reduce((reduction, prop) => From 73291ee59c535b5b3c31b614d5286c62b0d758db Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Tue, 16 Sep 2025 16:16:10 -0400 Subject: [PATCH 15/34] add `addToChannel` prop to relevant components Actions: - get-file - list-files - list-replies - send-message - send-message-advanced - send-message-to-channel Triggers: - new-keyword-mention - new-message-in-channels - new-user-mention Use bot token for Get File, List Files, and List Replies since the user token will no longer have `files:read` nor `*:history` scopes. --- .../actions/common/send-message.mjs | 6 +++ .../actions/get-file/get-file.mjs | 6 +++ .../actions/list-files/list-files.mjs | 12 ++++++ .../actions/list-replies/list-replies.mjs | 12 ++++++ .../send-message-advanced.mjs | 6 +++ .../send-message-to-channel.mjs | 6 +++ .../actions/send-message/send-message.mjs | 6 +++ .../slack_v2_test/slack_v2_test.app.mjs | 40 +++++++++++++++++++ .../slack_v2_test/sources/common/base.mjs | 29 -------------- .../new-keyword-mention.mjs | 3 +- .../new-message-in-channels.mjs | 4 +- .../new-user-mention/new-user-mention.mjs | 4 +- 12 files changed, 99 insertions(+), 35 deletions(-) diff --git a/components/slack_v2_test/actions/common/send-message.mjs b/components/slack_v2_test/actions/common/send-message.mjs index eb72f6065a339..3cde7eeb823dd 100644 --- a/components/slack_v2_test/actions/common/send-message.mjs +++ b/components/slack_v2_test/actions/common/send-message.mjs @@ -181,6 +181,12 @@ export default { }, }, async run({ $ }) { + if (this.addToChannel) { + await this.slack.maybeAddAppToChannels([ + this.conversation, + ]); + } + let blocks = this.blocks; if (!blocks) { diff --git a/components/slack_v2_test/actions/get-file/get-file.mjs b/components/slack_v2_test/actions/get-file/get-file.mjs index 946e2697dfb77..82f3ce7830816 100644 --- a/components/slack_v2_test/actions/get-file/get-file.mjs +++ b/components/slack_v2_test/actions/get-file/get-file.mjs @@ -25,6 +25,12 @@ export default { }, }, async run({ $ }) { + if (this.addToChannel) { + await this.slack.maybeAddAppToChannels([ + this.conversation, + ]); + } + const response = await this.slack.getFileInfo({ file: this.file, }); diff --git a/components/slack_v2_test/actions/list-files/list-files.mjs b/components/slack_v2_test/actions/list-files/list-files.mjs index 382a258cdcc82..f2dcfab4748d7 100644 --- a/components/slack_v2_test/actions/list-files/list-files.mjs +++ b/components/slack_v2_test/actions/list-files/list-files.mjs @@ -14,6 +14,12 @@ export default { "conversation", ], }, + addToChannel: { + propDefinition: [ + slack, + "addToChannel", + ], + }, team_id: { propDefinition: [ slack, @@ -42,6 +48,12 @@ export default { }, }, async run({ $ }) { + if (this.addToChannel) { + await this.slack.maybeAddAppToChannels([ + this.conversation, + ]); + } + const allFiles = []; const params = { channel: this.conversation, diff --git a/components/slack_v2_test/actions/list-replies/list-replies.mjs b/components/slack_v2_test/actions/list-replies/list-replies.mjs index 914f92eee5b52..1055848c1105c 100644 --- a/components/slack_v2_test/actions/list-replies/list-replies.mjs +++ b/components/slack_v2_test/actions/list-replies/list-replies.mjs @@ -14,6 +14,12 @@ export default { "conversation", ], }, + addToChannel: { + propDefinition: [ + slack, + "addToChannel", + ], + }, timestamp: { propDefinition: [ slack, @@ -34,6 +40,12 @@ export default { }, }, async run({ $ }) { + if (this.addToChannel) { + await this.slack.maybeAddAppToChannels([ + this.conversation, + ]); + } + const replies = []; const params = { channel: this.conversation, diff --git a/components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs b/components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs index db52bccb03ef3..bc187f72d836f 100644 --- a/components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs +++ b/components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs @@ -17,6 +17,12 @@ export default { "conversation", ], }, + addToChannel: { + propDefinition: [ + common.props.slack, + "addToChannel", + ], + }, text: { propDefinition: [ common.props.slack, diff --git a/components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs b/components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs index 2526244fef19a..02a2aa2059607 100644 --- a/components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs +++ b/components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs @@ -23,6 +23,12 @@ export default { ], description: "Select a public or private channel", }, + addToChannel: { + propDefinition: [ + common.props.slack, + "addToChannel", + ], + }, text: { propDefinition: [ common.props.slack, diff --git a/components/slack_v2_test/actions/send-message/send-message.mjs b/components/slack_v2_test/actions/send-message/send-message.mjs index 466c14d423007..7134074a6fc51 100644 --- a/components/slack_v2_test/actions/send-message/send-message.mjs +++ b/components/slack_v2_test/actions/send-message/send-message.mjs @@ -34,6 +34,12 @@ export default { }), ], }, + addToChannel: { + propDefinition: [ + common.props.slack, + "addToChannel", + ], + }, text: { propDefinition: [ common.props.slack, diff --git a/components/slack_v2_test/slack_v2_test.app.mjs b/components/slack_v2_test/slack_v2_test.app.mjs index 754f9ddbef5a5..d61437b5d072e 100644 --- a/components/slack_v2_test/slack_v2_test.app.mjs +++ b/components/slack_v2_test/slack_v2_test.app.mjs @@ -295,6 +295,7 @@ export default { page: page + 1, count: constants.LIMIT, throwRateLimitError: true, + as_bot: true, }); return files?.map(({ id: value, name: label, @@ -639,6 +640,43 @@ export default { } while (cursor && Object.keys(realNames).length < (ids.length + usernames.length)); return realNames; }, + async getBotUserInfo() { + const botInfo = await this.slack.authTest({ + as_bot: true, + }); + if (!botInfo?.bot_id) { + throw new Error("Could not get bot info. Make sure the Slack app has a bot user."); + } + return botInfo; + }, + async maybeAddAppToChannels(channelIds = []) { + if (!this.$auth.bot_token) return; + const { + bot_id, user_id, + } = await this.authTest({ + as_bot: true, + }); + if (!bot_id) { + throw new Error("Could not get bot ID. Make sure the Slack app has a bot user."); + } + // XXX: Trying to add the app to DM or group DM channels results in the + // error: method_not_supported_for_channel_type + for (const channel of channelIds) { + try { + await this.inviteToConversation({ + channel, + users: user_id, + }); + } catch (error) { + if (![ + "method_not_supported_for_channel_type", + "already_in_channel", + ].some((msg) => (error.data?.error || error.message || error)?.includes(msg))) { + throw error; + } + } + } + }, /** * Checks authentication & identity. * @param {*} args Arguments object @@ -765,6 +803,7 @@ export default { args.limit ||= constants.LIMIT; return this.makeRequest({ method: "conversations.history", + as_bot: true, ...args, }); }, @@ -914,6 +953,7 @@ export default { getConversationReplies(args = {}) { return this.makeRequest({ method: "conversations.replies", + as_bot: true, ...args, }); }, diff --git a/components/slack_v2_test/sources/common/base.mjs b/components/slack_v2_test/sources/common/base.mjs index b768194df3e4d..fdffe795a98ba 100644 --- a/components/slack_v2_test/sources/common/base.mjs +++ b/components/slack_v2_test/sources/common/base.mjs @@ -160,35 +160,6 @@ export default { }, ); }, - async getBotInfo() { - const botInfo = await this.slack.authTest({ - as_bot: true, - }); - if (!botInfo?.bot_id) { - throw new Error("Could not get bot info. Make sure the Slack app has a bot user."); - } - return botInfo; - }, - async addAppToChannels(channelIds = []) { - const { bot_id: botUserId } = await this.getBotInfo(); - // XXX: Trying to add the app to DM or group DM channels results in the - // error: method_not_supported_for_channel_type - for (const channel of channelIds) { - try { - await this.slack.inviteToConversation({ - channel, - users: botUserId, - }); - } catch (error) { - if (![ - "method_not_supported_for_channel_type", - "already_in_channel", - ].some((msg) => error.includes(msg))) { - throw error; - } - } - } - }, processEvent(event) { return event; }, diff --git a/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs b/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs index 8105e4bbcf2f8..cce16bc528a81 100644 --- a/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs +++ b/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs @@ -55,8 +55,7 @@ export default { ...common.hooks, async activate() { if (this.addToChannel && this.conversations?.length) { - if (this.conversations[0] === "message") return; - await this.addAppToChannels(this.conversations); + await this.slack.maybeAddAppToChannels(this.conversations); } }, }, diff --git a/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs b/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs index acc6ec1c92934..cce316ae5e8bf 100644 --- a/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs +++ b/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs @@ -58,10 +58,10 @@ export default { }, }, hooks: { + ...common.hooks, async activate() { if (this.addToChannel && this.conversations?.length) { - if (this.conversations[0] === "message") return; - await this.addAppToChannels(this.conversations); + await this.slack.maybeAddAppToChannels(this.conversations); } }, }, diff --git a/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs b/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs index 8f65d1160af13..012a05291dc29 100644 --- a/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs +++ b/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs @@ -53,10 +53,10 @@ export default { }, }, hooks: { + ...common.hooks, async activate() { if (this.addToChannel && this.conversations?.length) { - if (this.conversations[0] === "message") return; - await this.addAppToChannels(this.conversations); + await this.slack.maybeAddAppToChannels(this.conversations); } }, }, From 9e9265fc3de30e200a352de371726e573c6c3fdd Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Tue, 16 Sep 2025 16:45:38 -0400 Subject: [PATCH 16/34] add missing addToChannel prop to Get File --- components/slack_v2_test/actions/get-file/get-file.mjs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/components/slack_v2_test/actions/get-file/get-file.mjs b/components/slack_v2_test/actions/get-file/get-file.mjs index 82f3ce7830816..1710990dd448f 100644 --- a/components/slack_v2_test/actions/get-file/get-file.mjs +++ b/components/slack_v2_test/actions/get-file/get-file.mjs @@ -14,6 +14,12 @@ export default { "conversation", ], }, + addToChannel: { + propDefinition: [ + slack, + "addToChannel", + ], + }, file: { propDefinition: [ slack, From 1b03ec1cdc903d0447e078361c06b8aee796770b Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Tue, 16 Sep 2025 17:09:05 -0400 Subject: [PATCH 17/34] ensure the user is in the specified for Send Message actions, Get File, List Files, and List Replies --- components/slack_v2_test/actions/common/send-message.mjs | 8 +++++--- components/slack_v2_test/actions/get-file/get-file.mjs | 5 +++++ .../slack_v2_test/actions/list-files/list-files.mjs | 5 +++++ .../slack_v2_test/actions/list-replies/list-replies.mjs | 5 +++++ 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/components/slack_v2_test/actions/common/send-message.mjs b/components/slack_v2_test/actions/common/send-message.mjs index 3cde7eeb823dd..25ce9cbcddec1 100644 --- a/components/slack_v2_test/actions/common/send-message.mjs +++ b/components/slack_v2_test/actions/common/send-message.mjs @@ -181,6 +181,11 @@ export default { }, }, async run({ $ }) { + // Ensure the user is in the channel + const { channel } = await this.slack.conversationsInfo({ + channel: resp.channel, + }); + if (this.addToChannel) { await this.slack.maybeAddAppToChannels([ this.conversation, @@ -244,9 +249,6 @@ export default { return await this.slack.scheduleMessage(obj); } const resp = await this.slack.postChatMessage(obj); - const { channel } = await this.slack.conversationsInfo({ - channel: resp.channel, - }); let channelName = `#${channel?.name}`; if (channel.is_im) { const { profile } = await this.slack.getUserProfile({ diff --git a/components/slack_v2_test/actions/get-file/get-file.mjs b/components/slack_v2_test/actions/get-file/get-file.mjs index 1710990dd448f..4dbaa4b7009ca 100644 --- a/components/slack_v2_test/actions/get-file/get-file.mjs +++ b/components/slack_v2_test/actions/get-file/get-file.mjs @@ -35,6 +35,11 @@ export default { await this.slack.maybeAddAppToChannels([ this.conversation, ]); + } else { + // Ensure the user is in the channel + await this.slack.conversationsInfo({ + channel: this.conversation, + }); } const response = await this.slack.getFileInfo({ diff --git a/components/slack_v2_test/actions/list-files/list-files.mjs b/components/slack_v2_test/actions/list-files/list-files.mjs index f2dcfab4748d7..89f29688482e3 100644 --- a/components/slack_v2_test/actions/list-files/list-files.mjs +++ b/components/slack_v2_test/actions/list-files/list-files.mjs @@ -52,6 +52,11 @@ export default { await this.slack.maybeAddAppToChannels([ this.conversation, ]); + } else { + // Ensure the user is in the channel + await this.slack.conversationsInfo({ + channel: this.conversation, + }); } const allFiles = []; diff --git a/components/slack_v2_test/actions/list-replies/list-replies.mjs b/components/slack_v2_test/actions/list-replies/list-replies.mjs index 1055848c1105c..b27c8e0426c0b 100644 --- a/components/slack_v2_test/actions/list-replies/list-replies.mjs +++ b/components/slack_v2_test/actions/list-replies/list-replies.mjs @@ -44,6 +44,11 @@ export default { await this.slack.maybeAddAppToChannels([ this.conversation, ]); + } else { + // Ensure the user is in the channel + await this.slack.conversationsInfo({ + channel: this.conversation, + }); } const replies = []; From f20fe758bae79e98a5c8537cc6702e2d67ce37f9 Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Tue, 16 Sep 2025 17:09:40 -0400 Subject: [PATCH 18/34] show warning in New Direct Message if user is missing im:history scope --- .../new-direct-message/new-direct-message.mjs | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/components/slack_v2_test/sources/new-direct-message/new-direct-message.mjs b/components/slack_v2_test/sources/new-direct-message/new-direct-message.mjs index b1448f3006c6d..8c7ca0953e490 100644 --- a/components/slack_v2_test/sources/new-direct-message/new-direct-message.mjs +++ b/components/slack_v2_test/sources/new-direct-message/new-direct-message.mjs @@ -1,16 +1,23 @@ import common from "../common/base.mjs"; import sampleEmit from "./test-event.mjs"; +const slack = { + ...common.props.slack, + reloadProps: true, +}; + export default { ...common, key: "slack_v2_test-new-direct-message", name: "New Direct Message (Instant)", - version: "1.0.26", + version: "1.0.28", description: "Emit new event when a message was posted in a direct message channel", type: "source", dedupe: "unique", props: { ...common.props, + /* eslint-disable-next-line pipedream/props-description,pipedream/props-label */ + slack, // eslint-disable-next-line pipedream/props-description,pipedream/props-label slackApphook: { type: "$.interface.apphook", @@ -23,7 +30,7 @@ export default { }, ignoreBot: { propDefinition: [ - common.props.slack, + slack, "ignoreBot", ], }, @@ -35,6 +42,21 @@ export default { optional: true, }, }, + async additionalProps() { + const { response_metadata: { scopes } } = await this.slack.authTest({ + throwRateLimitError: true, + }); + if (!scopes.includes("im:history")) { + return { + alert: { + type: "alert", + alertType: "warning", + content: "The Slack account connected does not have the `im:history` scope. Events will only be emitted for direct messages with the Bot.", + }, + }; + } + return {}; + }, methods: { ...common.methods, getSummary() { From 7ea15cc1f18f224b87e4a82e80f636c24d5b1db8 Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Tue, 16 Sep 2025 17:11:27 -0400 Subject: [PATCH 19/34] handle error getting message in New Reaction Added source when the Bot is not in the channel --- .../new-reaction-added/new-reaction-added.mjs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/components/slack_v2_test/sources/new-reaction-added/new-reaction-added.mjs b/components/slack_v2_test/sources/new-reaction-added/new-reaction-added.mjs index 5c03b71f07b86..00ade53409a4f 100644 --- a/components/slack_v2_test/sources/new-reaction-added/new-reaction-added.mjs +++ b/components/slack_v2_test/sources/new-reaction-added/new-reaction-added.mjs @@ -97,10 +97,14 @@ export default { event.itemUserInfo = itemUserResponse.user; } - event.message = await this.getMessage({ - channel: event.item.channel, - event_ts: event.item.ts, - }); + try { + event.message = await this.getMessage({ + channel: event.item.channel, + event_ts: event.item.ts, + }); + } catch (err) { + console.log("Error fetching message:", err); + } return event; }, From b2fa0284f0d76ac7ebe55ece0f790996ec3fa05d Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Tue, 16 Sep 2025 17:25:35 -0400 Subject: [PATCH 20/34] delete verify-slack-signature action rejected by approval team --- .../verify-slack-signature.mjs | 60 ------------------- 1 file changed, 60 deletions(-) delete mode 100644 components/slack_v2_test/actions/verify-slack-signature/verify-slack-signature.mjs diff --git a/components/slack_v2_test/actions/verify-slack-signature/verify-slack-signature.mjs b/components/slack_v2_test/actions/verify-slack-signature/verify-slack-signature.mjs deleted file mode 100644 index 744ceaec25c54..0000000000000 --- a/components/slack_v2_test/actions/verify-slack-signature/verify-slack-signature.mjs +++ /dev/null @@ -1,60 +0,0 @@ -import crypto from "crypto"; -import slack from "../../slack_v2_test.app.mjs"; - -export default { - key: "slack_v2_test-verify-slack-signature", - name: "Verify Slack Signature", - description: "Verifying requests from Slack, slack signs its requests using a secret that's unique to your app. [See the documentation](https://api.slack.com/authentication/verifying-requests-from-slack)", - version: "0.0.2", - type: "action", - props: { - slack, - slackSigningSecret: { - type: "string", - label: "Signing Secret", - description: "Slack [Signing Secret](https://api.slack.com/authentication/verifying-requests-from-slack#:~:text=Slack%20Signing%20Secret%2C%20available%20in%20the%20app%20admin%20panel%20under%20Basic%20Info.), available in the app admin panel under Basic Info.", - secret: true, - }, - slackSignature: { - type: "string", - label: "X-Slack-Signature", - description: "Slack signature (from X-Slack-Signature header).", - }, - slackRequestTimestamp: { - type: "string", - label: "X-Slack-Request-Timestamp", - description: "Slack request timestamp (from X-Slack-Request-Timestamp header).", - }, - requestBody: { - type: "any", - label: "Request Body", - description: "The body of the request to be verified.", - }, - }, - async run({ $ }) { - const { - slackSignature, - slackRequestTimestamp, - requestBody, - slackSigningSecret, - } = this; - const requestBodyStr = typeof (requestBody) === "string" ? - requestBody : - JSON.stringify(requestBody); - const sigBaseString = `v0:${slackRequestTimestamp}:${requestBodyStr}`; - const sha256Hex = crypto.createHmac("sha256", slackSigningSecret) - .update(sigBaseString, "utf8") - .digest("hex"); - const mySignature = `v0=${sha256Hex}`; - if (crypto.timingSafeEqual(Buffer.from(mySignature, "utf8"), Buffer.from(slackSignature, "utf8"))) { - $.export("$summary", `Successfully verified the request with "${slackSignature}" signature`); - return { - success: true, - }; - } - $.export("$summary", "Slack signature mismatch with provided properties, it may be a configuration issue."); - return { - success: false, - }; - }, -}; From 37ab8cc63b6a856d81fb8bf3017bc3aa8d84ce47 Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Tue, 16 Sep 2025 17:55:36 -0400 Subject: [PATCH 21/34] add addToChannel prop to missing actions - Reply to a Message - Send Block Kit Message - Send Large Message and fix channel membership check in Send Message actions --- .../actions/common/send-message.mjs | 18 ++++++++++----- .../reply-to-a-message/reply-to-a-message.mjs | 6 +++++ .../send-block-kit-message.mjs | 6 +++++ .../send-large-message/send-large-message.mjs | 22 +++++++++++++++++-- 4 files changed, 44 insertions(+), 8 deletions(-) diff --git a/components/slack_v2_test/actions/common/send-message.mjs b/components/slack_v2_test/actions/common/send-message.mjs index 25ce9cbcddec1..315f41a2414c2 100644 --- a/components/slack_v2_test/actions/common/send-message.mjs +++ b/components/slack_v2_test/actions/common/send-message.mjs @@ -181,15 +181,18 @@ export default { }, }, async run({ $ }) { - // Ensure the user is in the channel - const { channel } = await this.slack.conversationsInfo({ - channel: resp.channel, - }); + const channelId = await this.getChannelId(); + let channel; if (this.addToChannel) { await this.slack.maybeAddAppToChannels([ - this.conversation, + channelId, ]); + } else if (!channelId.startsWith("U")) { + // Ensure the user is in the channel + channel = await this.slack.conversationsInfo({ + channel: channelId, + }); } let blocks = this.blocks; @@ -227,7 +230,7 @@ export default { const obj = { text: this.text, - channel: await this.getChannelId(), + channel: channelId, attachments: this.attachments, unfurl_links: this.unfurl_links, unfurl_media: this.unfurl_media, @@ -249,6 +252,9 @@ export default { return await this.slack.scheduleMessage(obj); } const resp = await this.slack.postChatMessage(obj); + channel ??= await this.slack.conversationsInfo({ + channel: resp.channel, + }); let channelName = `#${channel?.name}`; if (channel.is_im) { const { profile } = await this.slack.getUserProfile({ diff --git a/components/slack_v2_test/actions/reply-to-a-message/reply-to-a-message.mjs b/components/slack_v2_test/actions/reply-to-a-message/reply-to-a-message.mjs index fe53256731041..faec1d2c1358a 100644 --- a/components/slack_v2_test/actions/reply-to-a-message/reply-to-a-message.mjs +++ b/components/slack_v2_test/actions/reply-to-a-message/reply-to-a-message.mjs @@ -16,6 +16,12 @@ export default { "conversation", ], }, + addToChannel: { + propDefinition: [ + common.props.slack, + "addToChannel", + ], + }, text: { propDefinition: [ slack, diff --git a/components/slack_v2_test/actions/send-block-kit-message/send-block-kit-message.mjs b/components/slack_v2_test/actions/send-block-kit-message/send-block-kit-message.mjs index ebceeefa65df5..c8b6685d1dbe0 100644 --- a/components/slack_v2_test/actions/send-block-kit-message/send-block-kit-message.mjs +++ b/components/slack_v2_test/actions/send-block-kit-message/send-block-kit-message.mjs @@ -17,6 +17,12 @@ export default { "conversation", ], }, + addToChannel: { + propDefinition: [ + common.props.slack, + "addToChannel", + ], + }, text: { type: "string", label: "Notification Text", diff --git a/components/slack_v2_test/actions/send-large-message/send-large-message.mjs b/components/slack_v2_test/actions/send-large-message/send-large-message.mjs index b2eaf73f2fbcf..d46b239b68832 100644 --- a/components/slack_v2_test/actions/send-large-message/send-large-message.mjs +++ b/components/slack_v2_test/actions/send-large-message/send-large-message.mjs @@ -15,6 +15,12 @@ export default { "conversation", ], }, + addToChannel: { + propDefinition: [ + common.props.slack, + "addToChannel", + ], + }, text: { propDefinition: [ common.props.slack, @@ -30,6 +36,19 @@ export default { ...common.props, }, async run({ $ }) { + let channel; + + if (this.addToChannel) { + await this.slack.maybeAddAppToChannels([ + this.conversation, + ]); + } else if (!this.conversation.startsWith("U")) { + // Ensure the user is in the channel + await this.slack.conversationsInfo({ + channel: this.conversation, + }); + } + if (this.include_sent_via_pipedream_flag) { const sentViaPipedreamText = this._makeSentViaPipedreamBlock(); this.text += `\n\n\n${sentViaPipedreamText.elements[0].text}`; @@ -74,8 +93,7 @@ export default { } else { response = await this.slack.postChatMessage(obj); } - - const { channel } = await this.slack.conversationsInfo({ + channel ??= await this.slack.conversationsInfo({ channel: response.channel, }); let channelName = `#${channel?.name}`; From 57ed109dccffc7ced61b1dfb096a9636a51af889 Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Tue, 16 Sep 2025 18:11:10 -0400 Subject: [PATCH 22/34] fix channel membership checks in Send Message and Send Large Message actions Checking membership is only needed if sending message as the bot Add channel membership checks to Delete Message and Update Message actions --- .../slack_v2_test/actions/common/send-message.mjs | 2 +- .../actions/delete-message/delete-message.mjs | 11 +++++++++++ .../actions/send-large-message/send-large-message.mjs | 2 +- .../actions/update-message/update-message.mjs | 7 +++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/components/slack_v2_test/actions/common/send-message.mjs b/components/slack_v2_test/actions/common/send-message.mjs index 315f41a2414c2..6a4025f717c75 100644 --- a/components/slack_v2_test/actions/common/send-message.mjs +++ b/components/slack_v2_test/actions/common/send-message.mjs @@ -188,7 +188,7 @@ export default { await this.slack.maybeAddAppToChannels([ channelId, ]); - } else if (!channelId.startsWith("U")) { + } else if (!this.as_user && !channelId.startsWith("U")) { // Ensure the user is in the channel channel = await this.slack.conversationsInfo({ channel: channelId, diff --git a/components/slack_v2_test/actions/delete-message/delete-message.mjs b/components/slack_v2_test/actions/delete-message/delete-message.mjs index 103f6572d827a..0b257099a33fb 100644 --- a/components/slack_v2_test/actions/delete-message/delete-message.mjs +++ b/components/slack_v2_test/actions/delete-message/delete-message.mjs @@ -29,6 +29,17 @@ export default { }, }, async run({ $ }) { + if (this.addToChannel) { + await this.slack.maybeAddAppToChannels([ + this.conversation, + ]); + } else if (!this.as_user) { + // Ensure the user is in the channel + await this.slack.conversationsInfo({ + channel: this.conversation, + }); + } + const response = await this.slack.deleteMessage({ channel: this.conversation, ts: this.timestamp, diff --git a/components/slack_v2_test/actions/send-large-message/send-large-message.mjs b/components/slack_v2_test/actions/send-large-message/send-large-message.mjs index d46b239b68832..39be2e6ee96e2 100644 --- a/components/slack_v2_test/actions/send-large-message/send-large-message.mjs +++ b/components/slack_v2_test/actions/send-large-message/send-large-message.mjs @@ -42,7 +42,7 @@ export default { await this.slack.maybeAddAppToChannels([ this.conversation, ]); - } else if (!this.conversation.startsWith("U")) { + } else if (!this.as_user && !this.conversation.startsWith("U")) { // Ensure the user is in the channel await this.slack.conversationsInfo({ channel: this.conversation, diff --git a/components/slack_v2_test/actions/update-message/update-message.mjs b/components/slack_v2_test/actions/update-message/update-message.mjs index 0115dd00c8d55..0eccc50de1229 100644 --- a/components/slack_v2_test/actions/update-message/update-message.mjs +++ b/components/slack_v2_test/actions/update-message/update-message.mjs @@ -41,6 +41,13 @@ export default { }, }, async run({ $ }) { + if (!this.as_user) { + // Ensure the user is in the channel + await this.slack.conversationsInfo({ + channel: this.conversation, + }); + } + const response = await this.slack.updateMessage({ ts: this.timestamp, text: this.text, From 5d65cf875ec8974c8660d4207432a4644041bf1d Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Tue, 16 Sep 2025 18:58:57 -0400 Subject: [PATCH 23/34] refactor channel membership check --- .../actions/common/send-message.mjs | 11 +++----- .../actions/delete-message/delete-message.mjs | 5 +--- .../actions/get-file/get-file.mjs | 5 +--- .../actions/list-files/list-files.mjs | 5 +--- .../actions/list-replies/list-replies.mjs | 5 +--- .../send-large-message/send-large-message.mjs | 11 +++----- .../actions/update-message/update-message.mjs | 5 +--- .../slack_v2_test/slack_v2_test.app.mjs | 25 ++++++++++++------- 8 files changed, 29 insertions(+), 43 deletions(-) diff --git a/components/slack_v2_test/actions/common/send-message.mjs b/components/slack_v2_test/actions/common/send-message.mjs index 6a4025f717c75..1991300059b40 100644 --- a/components/slack_v2_test/actions/common/send-message.mjs +++ b/components/slack_v2_test/actions/common/send-message.mjs @@ -188,11 +188,8 @@ export default { await this.slack.maybeAddAppToChannels([ channelId, ]); - } else if (!this.as_user && !channelId.startsWith("U")) { - // Ensure the user is in the channel - channel = await this.slack.conversationsInfo({ - channel: channelId, - }); + } else if (!this.as_user) { + channel = (await this.slack.checkAccessToChannel(channelId))?.channel; } let blocks = this.blocks; @@ -252,9 +249,9 @@ export default { return await this.slack.scheduleMessage(obj); } const resp = await this.slack.postChatMessage(obj); - channel ??= await this.slack.conversationsInfo({ + channel ??= (await this.slack.conversationsInfo({ channel: resp.channel, - }); + })).channel; let channelName = `#${channel?.name}`; if (channel.is_im) { const { profile } = await this.slack.getUserProfile({ diff --git a/components/slack_v2_test/actions/delete-message/delete-message.mjs b/components/slack_v2_test/actions/delete-message/delete-message.mjs index 0b257099a33fb..7f567c72db491 100644 --- a/components/slack_v2_test/actions/delete-message/delete-message.mjs +++ b/components/slack_v2_test/actions/delete-message/delete-message.mjs @@ -34,10 +34,7 @@ export default { this.conversation, ]); } else if (!this.as_user) { - // Ensure the user is in the channel - await this.slack.conversationsInfo({ - channel: this.conversation, - }); + await this.slack.checkAccessToChannel(this.conversation); } const response = await this.slack.deleteMessage({ diff --git a/components/slack_v2_test/actions/get-file/get-file.mjs b/components/slack_v2_test/actions/get-file/get-file.mjs index 4dbaa4b7009ca..c34e5b67883d9 100644 --- a/components/slack_v2_test/actions/get-file/get-file.mjs +++ b/components/slack_v2_test/actions/get-file/get-file.mjs @@ -36,10 +36,7 @@ export default { this.conversation, ]); } else { - // Ensure the user is in the channel - await this.slack.conversationsInfo({ - channel: this.conversation, - }); + await this.slack.checkAccessToChannel(this.conversation); } const response = await this.slack.getFileInfo({ diff --git a/components/slack_v2_test/actions/list-files/list-files.mjs b/components/slack_v2_test/actions/list-files/list-files.mjs index 89f29688482e3..281cf9ab641a6 100644 --- a/components/slack_v2_test/actions/list-files/list-files.mjs +++ b/components/slack_v2_test/actions/list-files/list-files.mjs @@ -53,10 +53,7 @@ export default { this.conversation, ]); } else { - // Ensure the user is in the channel - await this.slack.conversationsInfo({ - channel: this.conversation, - }); + await this.slack.checkAccessToChannel(this.conversation); } const allFiles = []; diff --git a/components/slack_v2_test/actions/list-replies/list-replies.mjs b/components/slack_v2_test/actions/list-replies/list-replies.mjs index b27c8e0426c0b..4ad826a6c32f9 100644 --- a/components/slack_v2_test/actions/list-replies/list-replies.mjs +++ b/components/slack_v2_test/actions/list-replies/list-replies.mjs @@ -45,10 +45,7 @@ export default { this.conversation, ]); } else { - // Ensure the user is in the channel - await this.slack.conversationsInfo({ - channel: this.conversation, - }); + await this.slack.checkAccessToChannel(this.conversation); } const replies = []; diff --git a/components/slack_v2_test/actions/send-large-message/send-large-message.mjs b/components/slack_v2_test/actions/send-large-message/send-large-message.mjs index 39be2e6ee96e2..e7a735f50e19a 100644 --- a/components/slack_v2_test/actions/send-large-message/send-large-message.mjs +++ b/components/slack_v2_test/actions/send-large-message/send-large-message.mjs @@ -42,11 +42,8 @@ export default { await this.slack.maybeAddAppToChannels([ this.conversation, ]); - } else if (!this.as_user && !this.conversation.startsWith("U")) { - // Ensure the user is in the channel - await this.slack.conversationsInfo({ - channel: this.conversation, - }); + } else if (!this.as_user) { + channel = (await this.slack.checkAccessToChannel(this.conversation))?.channel; } if (this.include_sent_via_pipedream_flag) { @@ -93,9 +90,9 @@ export default { } else { response = await this.slack.postChatMessage(obj); } - channel ??= await this.slack.conversationsInfo({ + channel ??= (await this.slack.conversationsInfo({ channel: response.channel, - }); + })).channel; let channelName = `#${channel?.name}`; if (channel.is_im) { const { profile } = await this.slack.getUserProfile({ diff --git a/components/slack_v2_test/actions/update-message/update-message.mjs b/components/slack_v2_test/actions/update-message/update-message.mjs index 0eccc50de1229..a46ad708fcc3b 100644 --- a/components/slack_v2_test/actions/update-message/update-message.mjs +++ b/components/slack_v2_test/actions/update-message/update-message.mjs @@ -42,10 +42,7 @@ export default { }, async run({ $ }) { if (!this.as_user) { - // Ensure the user is in the channel - await this.slack.conversationsInfo({ - channel: this.conversation, - }); + await this.slack.checkAccessToChannel(this.conversation); } const response = await this.slack.updateMessage({ diff --git a/components/slack_v2_test/slack_v2_test.app.mjs b/components/slack_v2_test/slack_v2_test.app.mjs index d61437b5d072e..57856aa137ed4 100644 --- a/components/slack_v2_test/slack_v2_test.app.mjs +++ b/components/slack_v2_test/slack_v2_test.app.mjs @@ -640,15 +640,6 @@ export default { } while (cursor && Object.keys(realNames).length < (ids.length + usernames.length)); return realNames; }, - async getBotUserInfo() { - const botInfo = await this.slack.authTest({ - as_bot: true, - }); - if (!botInfo?.bot_id) { - throw new Error("Could not get bot info. Make sure the Slack app has a bot user."); - } - return botInfo; - }, async maybeAddAppToChannels(channelIds = []) { if (!this.$auth.bot_token) return; const { @@ -677,6 +668,22 @@ export default { } } }, + async checkAccessToChannel(channelId) { + // If not using a bot token, skip this check + if (!this.$auth.bot_token) return; + if (!channelId.startsWith("U") && !channelId.startsWith("D")) { + // If not a DM, check if the user is a member of the channel + return await this.conversationsInfo({ + channel: channelId, + throwRateLimitError: true, + }); + } else { + // If a DM, check if the user has a valid token + await this.authTest({ + throwRateLimitError: true, + }); + } + }, /** * Checks authentication & identity. * @param {*} args Arguments object From fc2a976edf89f30776d9cb2c22d702f5396e4b44 Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Tue, 16 Sep 2025 19:34:14 -0400 Subject: [PATCH 24/34] reordered addToChannel prop in New Keyword Mention trigger reverted new-direct-message version bump --- .../sources/new-direct-message/new-direct-message.mjs | 2 +- .../sources/new-keyword-mention/new-keyword-mention.mjs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/components/slack_v2_test/sources/new-direct-message/new-direct-message.mjs b/components/slack_v2_test/sources/new-direct-message/new-direct-message.mjs index 8c7ca0953e490..60da81411dced 100644 --- a/components/slack_v2_test/sources/new-direct-message/new-direct-message.mjs +++ b/components/slack_v2_test/sources/new-direct-message/new-direct-message.mjs @@ -10,7 +10,7 @@ export default { ...common, key: "slack_v2_test-new-direct-message", name: "New Direct Message (Instant)", - version: "1.0.28", + version: "1.0.27", description: "Emit new event when a message was posted in a direct message channel", type: "source", dedupe: "unique", diff --git a/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs b/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs index cce16bc528a81..1bdc9f71de448 100644 --- a/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs +++ b/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs @@ -32,16 +32,16 @@ export default { ]; }, }, - keyword: { + addToChannel: { propDefinition: [ common.props.slack, - "keyword", + "addToChannel", ], }, - addToChannel: { + keyword: { propDefinition: [ common.props.slack, - "addToChannel", + "keyword", ], }, ignoreBot: { From 97509555175d1db407329dbac304430e740bf39f Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Tue, 23 Sep 2025 18:28:02 -0400 Subject: [PATCH 25/34] only list channels in Get File and List File actions --- components/slack_v2_test/actions/get-file/get-file.mjs | 8 ++++++++ .../slack_v2_test/actions/list-files/list-files.mjs | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/components/slack_v2_test/actions/get-file/get-file.mjs b/components/slack_v2_test/actions/get-file/get-file.mjs index c34e5b67883d9..72f4c90376efd 100644 --- a/components/slack_v2_test/actions/get-file/get-file.mjs +++ b/components/slack_v2_test/actions/get-file/get-file.mjs @@ -1,3 +1,4 @@ +import constants from "../../common/constants.mjs"; import slack from "../../slack_v2_test.app.mjs"; export default { @@ -12,7 +13,14 @@ export default { propDefinition: [ slack, "conversation", + () => ({ + types: [ + constants.CHANNEL_TYPE.PUBLIC, + constants.CHANNEL_TYPE.PRIVATE, + ], + }), ], + description: "Select a public or private channel", }, addToChannel: { propDefinition: [ diff --git a/components/slack_v2_test/actions/list-files/list-files.mjs b/components/slack_v2_test/actions/list-files/list-files.mjs index 281cf9ab641a6..1e170231a8d2d 100644 --- a/components/slack_v2_test/actions/list-files/list-files.mjs +++ b/components/slack_v2_test/actions/list-files/list-files.mjs @@ -1,3 +1,4 @@ +import constants from "../../common/constants.mjs"; import slack from "../../slack_v2_test.app.mjs"; export default { @@ -12,7 +13,14 @@ export default { propDefinition: [ slack, "conversation", + () => ({ + types: [ + constants.CHANNEL_TYPE.PUBLIC, + constants.CHANNEL_TYPE.PRIVATE, + ], + }), ], + description: "Select a public or private channel", }, addToChannel: { propDefinition: [ From ccf5e5468e3ebadcc7bc563a881451fe8823c5a7 Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Tue, 23 Sep 2025 18:44:01 -0400 Subject: [PATCH 26/34] only list channels in List Replies action --- .../actions/list-replies/list-replies.mjs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/components/slack_v2_test/actions/list-replies/list-replies.mjs b/components/slack_v2_test/actions/list-replies/list-replies.mjs index 4ad826a6c32f9..c19af387ac0e6 100644 --- a/components/slack_v2_test/actions/list-replies/list-replies.mjs +++ b/components/slack_v2_test/actions/list-replies/list-replies.mjs @@ -1,3 +1,4 @@ +import constants from "../../common/constants.mjs"; import slack from "../../slack_v2_test.app.mjs"; export default { @@ -12,13 +13,14 @@ export default { propDefinition: [ slack, "conversation", + () => ({ + types: [ + constants.CHANNEL_TYPE.PUBLIC, + constants.CHANNEL_TYPE.PRIVATE, + ], + }), ], - }, - addToChannel: { - propDefinition: [ - slack, - "addToChannel", - ], + description: "Select a public or private channel", }, timestamp: { propDefinition: [ From cadbefd5bbd96a4eff9d56a3188cdff80d330b70 Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Tue, 23 Sep 2025 18:52:41 -0400 Subject: [PATCH 27/34] move addToChannel prop to common Send Message file and make it optional Disable it in the Send Message to User or Group action since the app can't be added to a DM or group DM --- components/slack_v2_test/actions/common/send-message.mjs | 7 +++++++ .../actions/reply-to-a-message/reply-to-a-message.mjs | 6 ------ .../send-block-kit-message/send-block-kit-message.mjs | 6 ------ .../actions/send-large-message/send-large-message.mjs | 6 ------ .../send-message-advanced/send-message-advanced.mjs | 6 ------ .../send-message-to-channel/send-message-to-channel.mjs | 6 ------ .../send-message-to-user-or-group.mjs | 6 ++++++ .../slack_v2_test/actions/send-message/send-message.mjs | 6 ------ components/slack_v2_test/slack_v2_test.app.mjs | 1 - 9 files changed, 13 insertions(+), 37 deletions(-) diff --git a/components/slack_v2_test/actions/common/send-message.mjs b/components/slack_v2_test/actions/common/send-message.mjs index 1991300059b40..3b1188ec7f36b 100644 --- a/components/slack_v2_test/actions/common/send-message.mjs +++ b/components/slack_v2_test/actions/common/send-message.mjs @@ -9,6 +9,13 @@ export default { "as_user", ], }, + addToChannel: { + propDefinition: [ + slack, + "addToChannel", + ], + optional: true, + }, post_at: { propDefinition: [ slack, diff --git a/components/slack_v2_test/actions/reply-to-a-message/reply-to-a-message.mjs b/components/slack_v2_test/actions/reply-to-a-message/reply-to-a-message.mjs index faec1d2c1358a..fe53256731041 100644 --- a/components/slack_v2_test/actions/reply-to-a-message/reply-to-a-message.mjs +++ b/components/slack_v2_test/actions/reply-to-a-message/reply-to-a-message.mjs @@ -16,12 +16,6 @@ export default { "conversation", ], }, - addToChannel: { - propDefinition: [ - common.props.slack, - "addToChannel", - ], - }, text: { propDefinition: [ slack, diff --git a/components/slack_v2_test/actions/send-block-kit-message/send-block-kit-message.mjs b/components/slack_v2_test/actions/send-block-kit-message/send-block-kit-message.mjs index c8b6685d1dbe0..ebceeefa65df5 100644 --- a/components/slack_v2_test/actions/send-block-kit-message/send-block-kit-message.mjs +++ b/components/slack_v2_test/actions/send-block-kit-message/send-block-kit-message.mjs @@ -17,12 +17,6 @@ export default { "conversation", ], }, - addToChannel: { - propDefinition: [ - common.props.slack, - "addToChannel", - ], - }, text: { type: "string", label: "Notification Text", diff --git a/components/slack_v2_test/actions/send-large-message/send-large-message.mjs b/components/slack_v2_test/actions/send-large-message/send-large-message.mjs index e7a735f50e19a..7b90352588101 100644 --- a/components/slack_v2_test/actions/send-large-message/send-large-message.mjs +++ b/components/slack_v2_test/actions/send-large-message/send-large-message.mjs @@ -15,12 +15,6 @@ export default { "conversation", ], }, - addToChannel: { - propDefinition: [ - common.props.slack, - "addToChannel", - ], - }, text: { propDefinition: [ common.props.slack, diff --git a/components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs b/components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs index bc187f72d836f..db52bccb03ef3 100644 --- a/components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs +++ b/components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs @@ -17,12 +17,6 @@ export default { "conversation", ], }, - addToChannel: { - propDefinition: [ - common.props.slack, - "addToChannel", - ], - }, text: { propDefinition: [ common.props.slack, diff --git a/components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs b/components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs index 02a2aa2059607..2526244fef19a 100644 --- a/components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs +++ b/components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs @@ -23,12 +23,6 @@ export default { ], description: "Select a public or private channel", }, - addToChannel: { - propDefinition: [ - common.props.slack, - "addToChannel", - ], - }, text: { propDefinition: [ common.props.slack, diff --git a/components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs b/components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs index 8fe8964896772..2d63241507949 100644 --- a/components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs +++ b/components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs @@ -47,6 +47,12 @@ export default { ], }, ...common.props, + // eslint-disable-next-line pipedream/props-label, pipedream/props-description + addToChannel: { + type: "boolean", + ...common.props.addToChannel, + disabled: true, + }, }, methods: { ...common.methods, diff --git a/components/slack_v2_test/actions/send-message/send-message.mjs b/components/slack_v2_test/actions/send-message/send-message.mjs index 7134074a6fc51..466c14d423007 100644 --- a/components/slack_v2_test/actions/send-message/send-message.mjs +++ b/components/slack_v2_test/actions/send-message/send-message.mjs @@ -34,12 +34,6 @@ export default { }), ], }, - addToChannel: { - propDefinition: [ - common.props.slack, - "addToChannel", - ], - }, text: { propDefinition: [ common.props.slack, diff --git a/components/slack_v2_test/slack_v2_test.app.mjs b/components/slack_v2_test/slack_v2_test.app.mjs index 57856aa137ed4..b6ddfd3a825ea 100644 --- a/components/slack_v2_test/slack_v2_test.app.mjs +++ b/components/slack_v2_test/slack_v2_test.app.mjs @@ -471,7 +471,6 @@ export default { label: "Add app to channel automatically?", description: "If `true`, the app will be added to the specified non-DM channel(s) automatically. If `false`, you must add the app to the channel manually. Defaults to `true`.", default: true, - optional: false, }, }, methods: { From 74cb0d022bd48cc2e8560b36bc64fc40c55826d4 Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Tue, 23 Sep 2025 19:00:17 -0400 Subject: [PATCH 28/34] remove addToChannel option from components that no longer need it and always use user token to get conversation history --- .../actions/delete-message/delete-message.mjs | 6 +----- .../actions/list-replies/list-replies.mjs | 8 -------- components/slack_v2_test/slack_v2_test.app.mjs | 2 -- .../new-keyword-mention/new-keyword-mention.mjs | 14 -------------- .../new-message-in-channels.mjs | 14 -------------- .../sources/new-user-mention/new-user-mention.mjs | 8 -------- 6 files changed, 1 insertion(+), 51 deletions(-) diff --git a/components/slack_v2_test/actions/delete-message/delete-message.mjs b/components/slack_v2_test/actions/delete-message/delete-message.mjs index 7f567c72db491..d7add5c764e97 100644 --- a/components/slack_v2_test/actions/delete-message/delete-message.mjs +++ b/components/slack_v2_test/actions/delete-message/delete-message.mjs @@ -29,11 +29,7 @@ export default { }, }, async run({ $ }) { - if (this.addToChannel) { - await this.slack.maybeAddAppToChannels([ - this.conversation, - ]); - } else if (!this.as_user) { + if (!this.as_user) { await this.slack.checkAccessToChannel(this.conversation); } diff --git a/components/slack_v2_test/actions/list-replies/list-replies.mjs b/components/slack_v2_test/actions/list-replies/list-replies.mjs index c19af387ac0e6..dc3986bb8387f 100644 --- a/components/slack_v2_test/actions/list-replies/list-replies.mjs +++ b/components/slack_v2_test/actions/list-replies/list-replies.mjs @@ -42,14 +42,6 @@ export default { }, }, async run({ $ }) { - if (this.addToChannel) { - await this.slack.maybeAddAppToChannels([ - this.conversation, - ]); - } else { - await this.slack.checkAccessToChannel(this.conversation); - } - const replies = []; const params = { channel: this.conversation, diff --git a/components/slack_v2_test/slack_v2_test.app.mjs b/components/slack_v2_test/slack_v2_test.app.mjs index b6ddfd3a825ea..02e73038dd12a 100644 --- a/components/slack_v2_test/slack_v2_test.app.mjs +++ b/components/slack_v2_test/slack_v2_test.app.mjs @@ -809,7 +809,6 @@ export default { args.limit ||= constants.LIMIT; return this.makeRequest({ method: "conversations.history", - as_bot: true, ...args, }); }, @@ -959,7 +958,6 @@ export default { getConversationReplies(args = {}) { return this.makeRequest({ method: "conversations.replies", - as_bot: true, ...args, }); }, diff --git a/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs b/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs index 1bdc9f71de448..2a06eed839a2f 100644 --- a/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs +++ b/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs @@ -32,12 +32,6 @@ export default { ]; }, }, - addToChannel: { - propDefinition: [ - common.props.slack, - "addToChannel", - ], - }, keyword: { propDefinition: [ common.props.slack, @@ -51,14 +45,6 @@ export default { ], }, }, - hooks: { - ...common.hooks, - async activate() { - if (this.addToChannel && this.conversations?.length) { - await this.slack.maybeAddAppToChannels(this.conversations); - } - }, - }, methods: { ...common.methods, getSummary() { diff --git a/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs b/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs index cce316ae5e8bf..2d5e9072610c6 100644 --- a/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs +++ b/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs @@ -32,12 +32,6 @@ export default { ]; }, }, - addToChannel: { - propDefinition: [ - common.props.slack, - "addToChannel", - ], - }, resolveNames: { propDefinition: [ common.props.slack, @@ -57,14 +51,6 @@ export default { optional: true, }, }, - hooks: { - ...common.hooks, - async activate() { - if (this.addToChannel && this.conversations?.length) { - await this.slack.maybeAddAppToChannels(this.conversations); - } - }, - }, methods: { ...common.methods, getSummary() { diff --git a/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs b/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs index 012a05291dc29..55ed9c5c437a9 100644 --- a/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs +++ b/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs @@ -52,14 +52,6 @@ export default { ], }, }, - hooks: { - ...common.hooks, - async activate() { - if (this.addToChannel && this.conversations?.length) { - await this.slack.maybeAddAppToChannels(this.conversations); - } - }, - }, methods: { ...common.methods, getSummary() { From f58c403e2902ba1882bb1ff9590c499fdeab1030 Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Tue, 23 Sep 2025 19:01:01 -0400 Subject: [PATCH 29/34] delete the New Direct Message trigger --- .../new-direct-message/new-direct-message.mjs | 75 ------------------- .../sources/new-direct-message/test-event.mjs | 28 ------- 2 files changed, 103 deletions(-) delete mode 100644 components/slack_v2_test/sources/new-direct-message/new-direct-message.mjs delete mode 100644 components/slack_v2_test/sources/new-direct-message/test-event.mjs diff --git a/components/slack_v2_test/sources/new-direct-message/new-direct-message.mjs b/components/slack_v2_test/sources/new-direct-message/new-direct-message.mjs deleted file mode 100644 index 60da81411dced..0000000000000 --- a/components/slack_v2_test/sources/new-direct-message/new-direct-message.mjs +++ /dev/null @@ -1,75 +0,0 @@ -import common from "../common/base.mjs"; -import sampleEmit from "./test-event.mjs"; - -const slack = { - ...common.props.slack, - reloadProps: true, -}; - -export default { - ...common, - key: "slack_v2_test-new-direct-message", - name: "New Direct Message (Instant)", - version: "1.0.27", - description: "Emit new event when a message was posted in a direct message channel", - type: "source", - dedupe: "unique", - props: { - ...common.props, - /* eslint-disable-next-line pipedream/props-description,pipedream/props-label */ - slack, - // eslint-disable-next-line pipedream/props-description,pipedream/props-label - slackApphook: { - type: "$.interface.apphook", - appProp: "slack", - async eventNames() { - return [ - "message.im", - ]; - }, - }, - ignoreBot: { - propDefinition: [ - slack, - "ignoreBot", - ], - }, - ignoreSelf: { - type: "boolean", - label: "Ignore Messages from Yourself", - description: "Ignores messages sent to yourself", - default: false, - optional: true, - }, - }, - async additionalProps() { - const { response_metadata: { scopes } } = await this.slack.authTest({ - throwRateLimitError: true, - }); - if (!scopes.includes("im:history")) { - return { - alert: { - type: "alert", - alertType: "warning", - content: "The Slack account connected does not have the `im:history` scope. Events will only be emitted for direct messages with the Bot.", - }, - }; - } - return {}; - }, - methods: { - ...common.methods, - getSummary() { - return "New direct message received"; - }, - processEvent(event) { - if ((this.ignoreSelf && event.user == this.slack.mySlackId()) - || ((this.ignoreBot) && (event.subtype === "bot_message" || event.bot_id)) - || (event.subtype === "message_changed")) { - return; - } - return event; - }, - }, - sampleEmit, -}; diff --git a/components/slack_v2_test/sources/new-direct-message/test-event.mjs b/components/slack_v2_test/sources/new-direct-message/test-event.mjs deleted file mode 100644 index d19486ed235f0..0000000000000 --- a/components/slack_v2_test/sources/new-direct-message/test-event.mjs +++ /dev/null @@ -1,28 +0,0 @@ -export default { - "user": "USLACKBOT", - "type": "message", - "ts": "1716401124.947359", - "text": "Feeling great!", - "team": "TS8319547", - "blocks": [ - { - "type": "rich_text", - "block_id": "bid/", - "elements": [ - { - "type": "rich_text_section", - "elements": [ - { - "type": "text", - "text": "Feeling great!" - } - ] - } - ] - } - ], - "channel": "DS676Q73J", - "event_ts": "1716401124.947359", - "channel_type": "im", - "pipedream_msg_id": "pd_1716401126905_tjxu6josgz" -} \ No newline at end of file From 8aeb7f5dd26b38542a93ec50faf7ab7ae5cbf88a Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Tue, 23 Sep 2025 19:17:20 -0400 Subject: [PATCH 30/34] Only list channels (not DMs) in New Message in Channels trigger and Keyword Mention trigger --- .../sources/new-keyword-mention/new-keyword-mention.mjs | 6 ++++++ .../new-message-in-channels/new-message-in-channels.mjs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs b/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs index 2a06eed839a2f..61595e719df65 100644 --- a/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs +++ b/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs @@ -16,6 +16,12 @@ export default { propDefinition: [ common.props.slack, "conversation", + () => ({ + types: [ + constants.CHANNEL_TYPE.PUBLIC, + constants.CHANNEL_TYPE.PRIVATE, + ], + }), ], type: "string[]", label: "Channels", diff --git a/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs b/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs index 2d5e9072610c6..4669a4977cc5b 100644 --- a/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs +++ b/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs @@ -16,6 +16,12 @@ export default { propDefinition: [ common.props.slack, "conversation", + () => ({ + types: [ + constants.CHANNEL_TYPE.PUBLIC, + constants.CHANNEL_TYPE.PRIVATE, + ], + }), ], type: "string[]", label: "Channels", From dae30f1428241c627bdcd0df6f0c83df850326e6 Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Thu, 25 Sep 2025 23:52:04 -0400 Subject: [PATCH 31/34] fixes --- .../actions/common/send-message.mjs | 10 +--- .../actions/find-message/find-message.mjs | 51 ++++++++++++------- .../invite-user-to-channel.mjs | 2 +- .../send-large-message/send-large-message.mjs | 10 +--- .../send-message-advanced.mjs | 2 +- .../send-message-to-channel.mjs | 2 +- .../send-message-to-user-or-group.mjs | 4 +- .../actions/send-message/send-message.mjs | 2 +- .../slack_v2_test/slack_v2_test.app.mjs | 41 ++++++++++++--- .../new-keyword-mention.mjs | 7 +-- .../new-message-in-channels.mjs | 7 +-- .../new-user-mention/new-user-mention.mjs | 9 +++- 12 files changed, 93 insertions(+), 54 deletions(-) diff --git a/components/slack_v2_test/actions/common/send-message.mjs b/components/slack_v2_test/actions/common/send-message.mjs index 3b1188ec7f36b..429635576dec5 100644 --- a/components/slack_v2_test/actions/common/send-message.mjs +++ b/components/slack_v2_test/actions/common/send-message.mjs @@ -259,15 +259,7 @@ export default { channel ??= (await this.slack.conversationsInfo({ channel: resp.channel, })).channel; - let channelName = `#${channel?.name}`; - if (channel.is_im) { - const { profile } = await this.slack.getUserProfile({ - user: channel.user, - }); - channelName = `@${profile.real_name}`; - } else if (channel.is_mpim) { - channelName = `@${channel.purpose.value}`; - } + const channelName = await this.slack.getChannelDisplayName(channel); $.export("$summary", `Successfully sent a message to ${channelName}`); return resp; }, diff --git a/components/slack_v2_test/actions/find-message/find-message.mjs b/components/slack_v2_test/actions/find-message/find-message.mjs index 34169010d595d..c7c35c6e34121 100644 --- a/components/slack_v2_test/actions/find-message/find-message.mjs +++ b/components/slack_v2_test/actions/find-message/find-message.mjs @@ -1,10 +1,11 @@ +import { axios } from "@pipedream/platform"; import slack from "../../slack_v2_test.app.mjs"; export default { key: "slack_v2_test-find-message", name: "Find Message", description: "Find a Slack message. [See the documentation](https://api.slack.com/methods/search.messages)", - version: "0.0.2", + version: "0.1.0", type: "action", props: { slack, @@ -14,18 +15,11 @@ export default { "query", ], }, - teamId: { - propDefinition: [ - slack, - "team", - ], - optional: true, - }, maxResults: { type: "integer", label: "Max Results", description: "The maximum number of messages to return", - default: 100, + default: 20, optional: true, }, sort: { @@ -49,26 +43,49 @@ export default { optional: true, }, }, + methods: { + async searchMessages($, params) { + const data = await axios($, { + method: "POST", + url: "https://slack.com/api/assistant.search.context", + data: { + query: params.query, + sort: params.sort, + sort_dir: params.sort_dir, + cursor: params.cursor, + }, + headers: { + "Authorization": `Bearer ${this.slack.getToken()}`, + "Content-Type": "application/json", + }, + }); + if (!data.ok) { + throw new Error(data.error || "An error occurred while searching messages"); + } + return data; + }, + }, async run({ $ }) { const matches = []; const params = { query: this.query, - team_id: this.teamId, sort: this.sort, sort_dir: this.sortDirection, - page: 1, }; - let hasMore; + let cursor; do { - const { messages } = await this.slack.searchMessages(params); - matches.push(...messages.matches); + if (cursor) { + params.cursor = cursor; + } + const response = await this.searchMessages($, params); + const messages = response.results?.messages || []; + matches.push(...messages); if (matches.length >= this.maxResults) { break; } - hasMore = messages.matches?.length; - params.page++; - } while (hasMore); + cursor = response.response_metadata?.next_cursor; + } while (cursor); if (matches.length > this.maxResults) { matches.length = this.maxResults; diff --git a/components/slack_v2_test/actions/invite-user-to-channel/invite-user-to-channel.mjs b/components/slack_v2_test/actions/invite-user-to-channel/invite-user-to-channel.mjs index c879838aedbdd..e9c96e48c7422 100644 --- a/components/slack_v2_test/actions/invite-user-to-channel/invite-user-to-channel.mjs +++ b/components/slack_v2_test/actions/invite-user-to-channel/invite-user-to-channel.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-invite-user-to-channel", name: "Invite User to Channel", description: "Invite a user to an existing channel. [See the documentation](https://api.slack.com/methods/conversations.invite)", - version: "0.0.2", + version: "0.0.4", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/send-large-message/send-large-message.mjs b/components/slack_v2_test/actions/send-large-message/send-large-message.mjs index 7b90352588101..eea57e5a215b5 100644 --- a/components/slack_v2_test/actions/send-large-message/send-large-message.mjs +++ b/components/slack_v2_test/actions/send-large-message/send-large-message.mjs @@ -87,15 +87,7 @@ export default { channel ??= (await this.slack.conversationsInfo({ channel: response.channel, })).channel; - let channelName = `#${channel?.name}`; - if (channel.is_im) { - const { profile } = await this.slack.getUserProfile({ - user: channel.user, - }); - channelName = `@${profile.real_name}`; - } else if (channel.is_mpim) { - channelName = `@${channel.purpose.value}`; - } + const channelName = await this.slack.getChannelDisplayName(channel); $.export("$summary", `Successfully sent a message to ${channelName}`); return response; }, diff --git a/components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs b/components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs index db52bccb03ef3..a5d22f1dd740f 100644 --- a/components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs +++ b/components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs @@ -7,7 +7,7 @@ export default { key: "slack_v2_test-send-message-advanced", name: "Send Message (Advanced)", description: "Customize advanced setttings and send a message to a channel, group or user. See [postMessage](https://api.slack.com/methods/chat.postMessage) or [scheduleMessage](https://api.slack.com/methods/chat.scheduleMessage) docs here", - version: "0.1.2", + version: "0.1.3", type: "action", props: { slack: common.props.slack, diff --git a/components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs b/components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs index 2526244fef19a..bfec2fc74666e 100644 --- a/components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs +++ b/components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs @@ -6,7 +6,7 @@ export default { key: "slack_v2_test-send-message-to-channel", name: "Send Message to Channel", description: "Send a message to a public or private channel. [See the documentation](https://api.slack.com/methods/chat.postMessage)", - version: "0.1.2", + version: "0.1.3", type: "action", props: { slack: common.props.slack, diff --git a/components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs b/components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs index 2d63241507949..493484d50e2cb 100644 --- a/components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs +++ b/components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs @@ -7,7 +7,7 @@ export default { key: "slack_v2_test-send-message-to-user-or-group", name: "Send Message to User or Group", description: "Send a message to a user or group. [See the documentation](https://api.slack.com/methods/chat.postMessage)", - version: "0.1.2", + version: "0.1.4", type: "action", props: { slack: common.props.slack, @@ -52,6 +52,7 @@ export default { type: "boolean", ...common.props.addToChannel, disabled: true, + hidden: true, }, }, methods: { @@ -72,7 +73,6 @@ export default { } const { channel: { id } } = await this.openConversation({ users: this.users.join(), - as_bot: this.as_user === false, }); return id; }, diff --git a/components/slack_v2_test/actions/send-message/send-message.mjs b/components/slack_v2_test/actions/send-message/send-message.mjs index 466c14d423007..d3444e186305b 100644 --- a/components/slack_v2_test/actions/send-message/send-message.mjs +++ b/components/slack_v2_test/actions/send-message/send-message.mjs @@ -6,7 +6,7 @@ export default { key: "slack_v2_test-send-message", name: "Send Message", description: "Send a message to a user, group, private channel or public channel. [See the documentation](https://api.slack.com/methods/chat.postMessage)", - version: "0.1.2", + version: "0.1.3", type: "action", props: { slack: common.props.slack, diff --git a/components/slack_v2_test/slack_v2_test.app.mjs b/components/slack_v2_test/slack_v2_test.app.mjs index 02e73038dd12a..a0cfeb3e1945d 100644 --- a/components/slack_v2_test/slack_v2_test.app.mjs +++ b/components/slack_v2_test/slack_v2_test.app.mjs @@ -28,11 +28,11 @@ export default { conversationsResp.conversations = conversationsResp.conversations .filter((c) => members.includes(c.user || c.id)); } - const userIds = conversationsResp.conversations.map(({ user }) => user); - const userNames = await this.userNameLookup(userIds); + const userIds = conversationsResp.conversations.map(({ user }) => user).filter(Boolean); + const realNames = await this.realNameLookup(userIds); return { - options: conversationsResp.conversations.map((c) => ({ - label: `${userNames[c.user]}`, + options: conversationsResp.conversations.filter((c) => c.user).map((c) => ({ + label: `${realNames[c.user]}`, value: c.user || c.id, })), context: { @@ -494,6 +494,32 @@ export default { ? this.$auth.bot_token : this.$auth.oauth_access_token; }, + async getChannelDisplayName(channel) { + if (channel.user) { + try { + const { profile } = await this.getUserProfile({ + user: channel.user, + }); + return `@${profile.real_name || profile?.real_name}`; + } catch { + return "user"; + } + } else if (channel.is_mpim) { + try { + const { members } = await this.listChannelMembers({ + channel: channel.id, + }); + const users = await Promise.all(members.map((m) => this.getUserProfile({ + user: m, + }))); + const realNames = users.map((u) => u.profile?.real_name || u.real_name); + return `Group Messaging with: ${realNames.join(", ")}`; + } catch { + return `Group Messaging with: ${channel.purpose.value}`; + } + } + return `#${channel?.name}`; + }, /** * Returns a Slack Web Client object authenticated with the user's access * token @@ -518,7 +544,10 @@ export default { try { response = await this._withRetries(() => sdk(args), throwRateLimitError); } catch (error) { - if (error?.data?.error === "channel_not_found" && as_bot) { + if ([ + "not_in_channel", + "channel_not_found", + ].includes(error?.data?.error) && as_bot) { // If method starts with chat, include the part about "As User" // Otherwise, just say "Ensure the bot is a member of the channel" if (method.startsWith("chat.")) { @@ -604,7 +633,7 @@ export default { for (const user of users) { if (ids.includes(user.id)) { - userNames[user.id] = user.profile.real_name; + userNames[user.id] = user.name; } } diff --git a/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs b/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs index 61595e719df65..f501272a0fd61 100644 --- a/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs +++ b/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs @@ -1,12 +1,13 @@ import common from "../common/base.mjs"; import constants from "../common/constants.mjs"; import sampleEmit from "./test-event.mjs"; +import sharedConstants from "../../common/constants.mjs"; export default { ...common, key: "slack_v2_test-new-keyword-mention", name: "New Keyword Mention (Instant)", - version: "0.0.9", + version: "0.0.10", description: "Emit new event when a specific keyword is mentioned in a channel", type: "source", dedupe: "unique", @@ -18,8 +19,8 @@ export default { "conversation", () => ({ types: [ - constants.CHANNEL_TYPE.PUBLIC, - constants.CHANNEL_TYPE.PRIVATE, + sharedConstants.CHANNEL_TYPE.PUBLIC, + sharedConstants.CHANNEL_TYPE.PRIVATE, ], }), ], diff --git a/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs b/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs index 4669a4977cc5b..45d8c8e59c740 100644 --- a/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs +++ b/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs @@ -1,12 +1,13 @@ import common from "../common/base.mjs"; import constants from "../common/constants.mjs"; import sampleEmit from "./test-event.mjs"; +import sharedConstants from "../../common/constants.mjs"; export default { ...common, key: "slack_v2_test-new-message-in-channels", name: "New Message In Channels (Instant)", - version: "1.0.26", + version: "1.0.27", description: "Emit new event when a new message is posted to one or more channels", type: "source", dedupe: "unique", @@ -18,8 +19,8 @@ export default { "conversation", () => ({ types: [ - constants.CHANNEL_TYPE.PUBLIC, - constants.CHANNEL_TYPE.PRIVATE, + sharedConstants.CHANNEL_TYPE.PUBLIC, + sharedConstants.CHANNEL_TYPE.PRIVATE, ], }), ], diff --git a/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs b/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs index 55ed9c5c437a9..e8046b51a5b8e 100644 --- a/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs +++ b/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs @@ -1,12 +1,13 @@ import common from "../common/base.mjs"; import constants from "../common/constants.mjs"; import sampleEmit from "./test-event.mjs"; +import sharedConstants from "../../common/constants.mjs"; export default { ...common, key: "slack_v2_test-new-user-mention", name: "New User Mention (Instant)", - version: "0.0.9", + version: "0.0.10", description: "Emit new event when a username or specific keyword is mentioned in a channel", type: "source", dedupe: "unique", @@ -16,6 +17,12 @@ export default { propDefinition: [ common.props.slack, "conversation", + () => ({ + types: [ + sharedConstants.CHANNEL_TYPE.PUBLIC, + sharedConstants.CHANNEL_TYPE.PRIVATE, + ], + }), ], type: "string[]", label: "Channels", From 50591a21e9794076a7aeb7ac34d64c53b20fe4de Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Fri, 26 Sep 2025 00:12:00 -0400 Subject: [PATCH 32/34] bump versions --- .../actions/add-emoji-reaction/add-emoji-reaction.mjs | 2 +- .../slack_v2_test/actions/approve-workflow/approve-workflow.mjs | 2 +- .../slack_v2_test/actions/archive-channel/archive-channel.mjs | 2 +- .../slack_v2_test/actions/create-channel/create-channel.mjs | 2 +- .../slack_v2_test/actions/create-reminder/create-reminder.mjs | 2 +- components/slack_v2_test/actions/delete-file/delete-file.mjs | 2 +- .../slack_v2_test/actions/delete-message/delete-message.mjs | 2 +- components/slack_v2_test/actions/find-message/find-message.mjs | 2 +- .../actions/find-user-by-email/find-user-by-email.mjs | 2 +- components/slack_v2_test/actions/get-file/get-file.mjs | 2 +- .../actions/invite-user-to-channel/invite-user-to-channel.mjs | 2 +- components/slack_v2_test/actions/kick-user/kick-user.mjs | 2 +- .../slack_v2_test/actions/list-channels/list-channels.mjs | 2 +- components/slack_v2_test/actions/list-files/list-files.mjs | 2 +- .../actions/list-group-members/list-group-members.mjs | 2 +- .../actions/list-members-in-channel/list-members-in-channel.mjs | 2 +- components/slack_v2_test/actions/list-replies/list-replies.mjs | 2 +- components/slack_v2_test/actions/list-users/list-users.mjs | 2 +- .../actions/reply-to-a-message/reply-to-a-message.mjs | 2 +- .../actions/send-block-kit-message/send-block-kit-message.mjs | 2 +- .../actions/send-large-message/send-large-message.mjs | 2 +- .../actions/send-message-advanced/send-message-advanced.mjs | 2 +- .../actions/send-message-to-channel/send-message-to-channel.mjs | 2 +- .../send-message-to-user-or-group.mjs | 2 +- components/slack_v2_test/actions/send-message/send-message.mjs | 2 +- .../actions/set-channel-description/set-channel-description.mjs | 2 +- .../actions/set-channel-topic/set-channel-topic.mjs | 2 +- components/slack_v2_test/actions/set-status/set-status.mjs | 2 +- .../actions/update-group-members/update-group-members.mjs | 2 +- .../slack_v2_test/actions/update-message/update-message.mjs | 2 +- .../slack_v2_test/actions/update-profile/update-profile.mjs | 2 +- components/slack_v2_test/actions/upload-file/upload-file.mjs | 2 +- .../sources/new-channel-created/new-channel-created.mjs | 2 +- .../new-interaction-event-received.mjs | 2 +- .../sources/new-keyword-mention/new-keyword-mention.mjs | 2 +- .../sources/new-message-in-channels/new-message-in-channels.mjs | 2 +- .../sources/new-reaction-added/new-reaction-added.mjs | 2 +- .../sources/new-saved-message/new-saved-message.mjs | 2 +- .../slack_v2_test/sources/new-user-added/new-user-added.mjs | 2 +- .../slack_v2_test/sources/new-user-mention/new-user-mention.mjs | 2 +- 40 files changed, 40 insertions(+), 40 deletions(-) diff --git a/components/slack_v2_test/actions/add-emoji-reaction/add-emoji-reaction.mjs b/components/slack_v2_test/actions/add-emoji-reaction/add-emoji-reaction.mjs index fb90d7e624d06..badbcf784f2d0 100644 --- a/components/slack_v2_test/actions/add-emoji-reaction/add-emoji-reaction.mjs +++ b/components/slack_v2_test/actions/add-emoji-reaction/add-emoji-reaction.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-add-emoji-reaction", name: "Add Emoji Reaction", description: "Add an emoji reaction to a message. [See the documentation](https://api.slack.com/methods/reactions.add)", - version: "0.0.2", + version: "0.0.3", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/approve-workflow/approve-workflow.mjs b/components/slack_v2_test/actions/approve-workflow/approve-workflow.mjs index eb5623f5923a5..1656a74074e89 100644 --- a/components/slack_v2_test/actions/approve-workflow/approve-workflow.mjs +++ b/components/slack_v2_test/actions/approve-workflow/approve-workflow.mjs @@ -5,7 +5,7 @@ export default { key: "slack_v2_test-approve-workflow", name: "Approve Workflow", description: "Suspend the workflow until approved by a Slack message. [See the documentation](https://pipedream.com/docs/code/nodejs/rerun#flowsuspend)", - version: "0.1.1", + version: "0.1.2", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/archive-channel/archive-channel.mjs b/components/slack_v2_test/actions/archive-channel/archive-channel.mjs index a56c6051955db..3123dac397e42 100644 --- a/components/slack_v2_test/actions/archive-channel/archive-channel.mjs +++ b/components/slack_v2_test/actions/archive-channel/archive-channel.mjs @@ -5,7 +5,7 @@ export default { key: "slack_v2_test-archive-channel", name: "Archive Channel", description: "Archive a channel. [See the documentation](https://api.slack.com/methods/conversations.archive)", - version: "0.0.2", + version: "0.0.3", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/create-channel/create-channel.mjs b/components/slack_v2_test/actions/create-channel/create-channel.mjs index 58416eb2f5162..92982e86e4315 100644 --- a/components/slack_v2_test/actions/create-channel/create-channel.mjs +++ b/components/slack_v2_test/actions/create-channel/create-channel.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-create-channel", name: "Create a Channel", description: "Create a new channel. [See the documentation](https://api.slack.com/methods/conversations.create)", - version: "0.0.2", + version: "0.0.3", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/create-reminder/create-reminder.mjs b/components/slack_v2_test/actions/create-reminder/create-reminder.mjs index d93bf766124f4..634049d8b0ac5 100644 --- a/components/slack_v2_test/actions/create-reminder/create-reminder.mjs +++ b/components/slack_v2_test/actions/create-reminder/create-reminder.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-create-reminder", name: "Create Reminder", description: "Create a reminder. [See the documentation](https://api.slack.com/methods/reminders.add)", - version: "0.0.2", + version: "0.0.3", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/delete-file/delete-file.mjs b/components/slack_v2_test/actions/delete-file/delete-file.mjs index ce49294de4a53..5d76e0a250e72 100644 --- a/components/slack_v2_test/actions/delete-file/delete-file.mjs +++ b/components/slack_v2_test/actions/delete-file/delete-file.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-delete-file", name: "Delete File", description: "Delete a file. [See the documentation](https://api.slack.com/methods/files.delete)", - version: "0.0.2", + version: "0.0.3", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/delete-message/delete-message.mjs b/components/slack_v2_test/actions/delete-message/delete-message.mjs index d7add5c764e97..2e3bde0a2bd27 100644 --- a/components/slack_v2_test/actions/delete-message/delete-message.mjs +++ b/components/slack_v2_test/actions/delete-message/delete-message.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-delete-message", name: "Delete Message", description: "Delete a message. [See the documentation](https://api.slack.com/methods/chat.delete)", - version: "0.0.2", + version: "0.0.3", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/find-message/find-message.mjs b/components/slack_v2_test/actions/find-message/find-message.mjs index c7c35c6e34121..2b9dacf1aba23 100644 --- a/components/slack_v2_test/actions/find-message/find-message.mjs +++ b/components/slack_v2_test/actions/find-message/find-message.mjs @@ -5,7 +5,7 @@ export default { key: "slack_v2_test-find-message", name: "Find Message", description: "Find a Slack message. [See the documentation](https://api.slack.com/methods/search.messages)", - version: "0.1.0", + version: "0.1.1", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/find-user-by-email/find-user-by-email.mjs b/components/slack_v2_test/actions/find-user-by-email/find-user-by-email.mjs index b16b0d1ecbb8a..80c53a93ec156 100644 --- a/components/slack_v2_test/actions/find-user-by-email/find-user-by-email.mjs +++ b/components/slack_v2_test/actions/find-user-by-email/find-user-by-email.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-find-user-by-email", name: "Find User by Email", description: "Find a user by matching against their email. [See the documentation](https://api.slack.com/methods/users.lookupByEmail)", - version: "0.0.2", + version: "0.0.3", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/get-file/get-file.mjs b/components/slack_v2_test/actions/get-file/get-file.mjs index 72f4c90376efd..d0af295a88099 100644 --- a/components/slack_v2_test/actions/get-file/get-file.mjs +++ b/components/slack_v2_test/actions/get-file/get-file.mjs @@ -5,7 +5,7 @@ export default { key: "slack_v2_test-get-file", name: "Get File", description: "Return information about a file. [See the documentation](https://api.slack.com/methods/files.info)", - version: "0.0.2", + version: "0.0.3", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/invite-user-to-channel/invite-user-to-channel.mjs b/components/slack_v2_test/actions/invite-user-to-channel/invite-user-to-channel.mjs index e9c96e48c7422..02a13402c35c7 100644 --- a/components/slack_v2_test/actions/invite-user-to-channel/invite-user-to-channel.mjs +++ b/components/slack_v2_test/actions/invite-user-to-channel/invite-user-to-channel.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-invite-user-to-channel", name: "Invite User to Channel", description: "Invite a user to an existing channel. [See the documentation](https://api.slack.com/methods/conversations.invite)", - version: "0.0.4", + version: "0.0.5", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/kick-user/kick-user.mjs b/components/slack_v2_test/actions/kick-user/kick-user.mjs index cbbc5260ff2e0..99f237b99a23f 100644 --- a/components/slack_v2_test/actions/kick-user/kick-user.mjs +++ b/components/slack_v2_test/actions/kick-user/kick-user.mjs @@ -5,7 +5,7 @@ export default { key: "slack_v2_test-kick-user", name: "Kick User", description: "Remove a user from a conversation. [See the documentation](https://api.slack.com/methods/conversations.kick)", - version: "0.0.2", + version: "0.0.3", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/list-channels/list-channels.mjs b/components/slack_v2_test/actions/list-channels/list-channels.mjs index e382b209558d5..e6232964765f5 100644 --- a/components/slack_v2_test/actions/list-channels/list-channels.mjs +++ b/components/slack_v2_test/actions/list-channels/list-channels.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-list-channels", name: "List Channels", description: "Return a list of all channels in a workspace. [See the documentation](https://api.slack.com/methods/conversations.list)", - version: "0.0.2", + version: "0.0.3", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/list-files/list-files.mjs b/components/slack_v2_test/actions/list-files/list-files.mjs index 1e170231a8d2d..479c793099f6d 100644 --- a/components/slack_v2_test/actions/list-files/list-files.mjs +++ b/components/slack_v2_test/actions/list-files/list-files.mjs @@ -5,7 +5,7 @@ export default { key: "slack_v2_test-list-files", name: "List Files", description: "Return a list of files within a team. [See the documentation](https://api.slack.com/methods/files.list)", - version: "0.0.2", + version: "0.0.3", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/list-group-members/list-group-members.mjs b/components/slack_v2_test/actions/list-group-members/list-group-members.mjs index b3271ab6bb125..f1b2015c6a5ae 100644 --- a/components/slack_v2_test/actions/list-group-members/list-group-members.mjs +++ b/components/slack_v2_test/actions/list-group-members/list-group-members.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-list-group-members", name: "List Group Members", description: "List all users in a User Group. [See the documentation](https://api.slack.com/methods/usergroups.users.list)", - version: "0.0.2", + version: "0.0.3", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/list-members-in-channel/list-members-in-channel.mjs b/components/slack_v2_test/actions/list-members-in-channel/list-members-in-channel.mjs index 86ad9ec467ec8..0f1f8bf6ac4cc 100644 --- a/components/slack_v2_test/actions/list-members-in-channel/list-members-in-channel.mjs +++ b/components/slack_v2_test/actions/list-members-in-channel/list-members-in-channel.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-list-members-in-channel", name: "List Members in Channel", description: "Retrieve members of a channel. [See the documentation](https://api.slack.com/methods/conversations.members)", - version: "0.0.2", + version: "0.0.3", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/list-replies/list-replies.mjs b/components/slack_v2_test/actions/list-replies/list-replies.mjs index dc3986bb8387f..f6edee18dc72d 100644 --- a/components/slack_v2_test/actions/list-replies/list-replies.mjs +++ b/components/slack_v2_test/actions/list-replies/list-replies.mjs @@ -5,7 +5,7 @@ export default { key: "slack_v2_test-list-replies", name: "List Replies", description: "Retrieve a thread of messages posted to a conversation. [See the documentation](https://api.slack.com/methods/conversations.replies)", - version: "0.0.2", + version: "0.0.3", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/list-users/list-users.mjs b/components/slack_v2_test/actions/list-users/list-users.mjs index 0fd53df22b4d5..3dbb863c92712 100644 --- a/components/slack_v2_test/actions/list-users/list-users.mjs +++ b/components/slack_v2_test/actions/list-users/list-users.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-list-users", name: "List Users", description: "Return a list of all users in a workspace. [See the documentation](https://api.slack.com/methods/users.list)", - version: "0.0.2", + version: "0.0.3", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/reply-to-a-message/reply-to-a-message.mjs b/components/slack_v2_test/actions/reply-to-a-message/reply-to-a-message.mjs index fe53256731041..f762be598571b 100644 --- a/components/slack_v2_test/actions/reply-to-a-message/reply-to-a-message.mjs +++ b/components/slack_v2_test/actions/reply-to-a-message/reply-to-a-message.mjs @@ -6,7 +6,7 @@ export default { key: "slack_v2_test-reply-to-a-message", name: "Reply to a Message Thread", description: "Send a message as a threaded reply. See [postMessage](https://api.slack.com/methods/chat.postMessage) or [scheduleMessage](https://api.slack.com/methods/chat.scheduleMessage) docs here", - version: "0.1.2", + version: "0.1.3", type: "action", props: { slack: common.props.slack, diff --git a/components/slack_v2_test/actions/send-block-kit-message/send-block-kit-message.mjs b/components/slack_v2_test/actions/send-block-kit-message/send-block-kit-message.mjs index ebceeefa65df5..fb8a25580b130 100644 --- a/components/slack_v2_test/actions/send-block-kit-message/send-block-kit-message.mjs +++ b/components/slack_v2_test/actions/send-block-kit-message/send-block-kit-message.mjs @@ -7,7 +7,7 @@ export default { key: "slack_v2_test-send-block-kit-message", name: "Build and Send a Block Kit Message", description: "Configure custom blocks and send to a channel, group, or user. [See the documentation](https://api.slack.com/tools/block-kit-builder).", - version: "0.5.2", + version: "0.5.3", type: "action", props: { slack: common.props.slack, diff --git a/components/slack_v2_test/actions/send-large-message/send-large-message.mjs b/components/slack_v2_test/actions/send-large-message/send-large-message.mjs index eea57e5a215b5..733b6b3c653bf 100644 --- a/components/slack_v2_test/actions/send-large-message/send-large-message.mjs +++ b/components/slack_v2_test/actions/send-large-message/send-large-message.mjs @@ -5,7 +5,7 @@ export default { key: "slack_v2_test-send-large-message", name: "Send a Large Message (3000+ characters)", description: "Send a large message (more than 3000 characters) to a channel, group or user. See [postMessage](https://api.slack.com/methods/chat.postMessage) or [scheduleMessage](https://api.slack.com/methods/chat.scheduleMessage) docs here", - version: "0.1.2", + version: "0.1.3", type: "action", props: { slack: common.props.slack, diff --git a/components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs b/components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs index a5d22f1dd740f..816fb4d624550 100644 --- a/components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs +++ b/components/slack_v2_test/actions/send-message-advanced/send-message-advanced.mjs @@ -7,7 +7,7 @@ export default { key: "slack_v2_test-send-message-advanced", name: "Send Message (Advanced)", description: "Customize advanced setttings and send a message to a channel, group or user. See [postMessage](https://api.slack.com/methods/chat.postMessage) or [scheduleMessage](https://api.slack.com/methods/chat.scheduleMessage) docs here", - version: "0.1.3", + version: "0.1.4", type: "action", props: { slack: common.props.slack, diff --git a/components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs b/components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs index bfec2fc74666e..4d3f52cdb667a 100644 --- a/components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs +++ b/components/slack_v2_test/actions/send-message-to-channel/send-message-to-channel.mjs @@ -6,7 +6,7 @@ export default { key: "slack_v2_test-send-message-to-channel", name: "Send Message to Channel", description: "Send a message to a public or private channel. [See the documentation](https://api.slack.com/methods/chat.postMessage)", - version: "0.1.3", + version: "0.1.4", type: "action", props: { slack: common.props.slack, diff --git a/components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs b/components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs index 493484d50e2cb..3d1006814f0b6 100644 --- a/components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs +++ b/components/slack_v2_test/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs @@ -7,7 +7,7 @@ export default { key: "slack_v2_test-send-message-to-user-or-group", name: "Send Message to User or Group", description: "Send a message to a user or group. [See the documentation](https://api.slack.com/methods/chat.postMessage)", - version: "0.1.4", + version: "0.1.5", type: "action", props: { slack: common.props.slack, diff --git a/components/slack_v2_test/actions/send-message/send-message.mjs b/components/slack_v2_test/actions/send-message/send-message.mjs index d3444e186305b..cf471daa9a802 100644 --- a/components/slack_v2_test/actions/send-message/send-message.mjs +++ b/components/slack_v2_test/actions/send-message/send-message.mjs @@ -6,7 +6,7 @@ export default { key: "slack_v2_test-send-message", name: "Send Message", description: "Send a message to a user, group, private channel or public channel. [See the documentation](https://api.slack.com/methods/chat.postMessage)", - version: "0.1.3", + version: "0.1.4", type: "action", props: { slack: common.props.slack, diff --git a/components/slack_v2_test/actions/set-channel-description/set-channel-description.mjs b/components/slack_v2_test/actions/set-channel-description/set-channel-description.mjs index 27459d80ba977..b675db4a6e2c6 100644 --- a/components/slack_v2_test/actions/set-channel-description/set-channel-description.mjs +++ b/components/slack_v2_test/actions/set-channel-description/set-channel-description.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-set-channel-description", name: "Set Channel Description", description: "Change the description or purpose of a channel. [See the documentation](https://api.slack.com/methods/conversations.setPurpose)", - version: "0.0.2", + version: "0.0.3", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/set-channel-topic/set-channel-topic.mjs b/components/slack_v2_test/actions/set-channel-topic/set-channel-topic.mjs index fa74009e52d81..c95f48d184693 100644 --- a/components/slack_v2_test/actions/set-channel-topic/set-channel-topic.mjs +++ b/components/slack_v2_test/actions/set-channel-topic/set-channel-topic.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-set-channel-topic", name: "Set Channel Topic", description: "Set the topic on a selected channel. [See the documentation](https://api.slack.com/methods/conversations.setTopic)", - version: "0.0.2", + version: "0.0.3", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/set-status/set-status.mjs b/components/slack_v2_test/actions/set-status/set-status.mjs index d6917b95b79fa..a7563d79eeef1 100644 --- a/components/slack_v2_test/actions/set-status/set-status.mjs +++ b/components/slack_v2_test/actions/set-status/set-status.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-set-status", name: "Set Status", description: "Set the current status for a user. [See the documentation](https://api.slack.com/methods/users.profile.set)", - version: "0.0.2", + version: "0.0.3", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/update-group-members/update-group-members.mjs b/components/slack_v2_test/actions/update-group-members/update-group-members.mjs index c55169e25a33c..bd327e509d2f3 100644 --- a/components/slack_v2_test/actions/update-group-members/update-group-members.mjs +++ b/components/slack_v2_test/actions/update-group-members/update-group-members.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-update-group-members", name: "Update Groups Members", description: "Update the list of users for a User Group. [See the documentation](https://api.slack.com/methods/usergroups.users.update)", - version: "0.0.3", + version: "0.0.4", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/update-message/update-message.mjs b/components/slack_v2_test/actions/update-message/update-message.mjs index a46ad708fcc3b..2ab202380b3d5 100644 --- a/components/slack_v2_test/actions/update-message/update-message.mjs +++ b/components/slack_v2_test/actions/update-message/update-message.mjs @@ -4,7 +4,7 @@ export default { key: "slack_v2_test-update-message", name: "Update Message", description: "Update a message. [See the documentation](https://api.slack.com/methods/chat.update)", - version: "0.1.2", + version: "0.1.3", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/update-profile/update-profile.mjs b/components/slack_v2_test/actions/update-profile/update-profile.mjs index 7865a691761eb..648d5a3b8dc4e 100644 --- a/components/slack_v2_test/actions/update-profile/update-profile.mjs +++ b/components/slack_v2_test/actions/update-profile/update-profile.mjs @@ -5,7 +5,7 @@ export default { key: "slack_v2_test-update-profile", name: "Update Profile", description: "Update basic profile field such as name or title. [See the documentation](https://api.slack.com/methods/users.profile.set)", - version: "0.0.2", + version: "0.0.3", type: "action", props: { slack, diff --git a/components/slack_v2_test/actions/upload-file/upload-file.mjs b/components/slack_v2_test/actions/upload-file/upload-file.mjs index 3e9366bfb278b..a37bde6960120 100644 --- a/components/slack_v2_test/actions/upload-file/upload-file.mjs +++ b/components/slack_v2_test/actions/upload-file/upload-file.mjs @@ -8,7 +8,7 @@ export default { key: "slack_v2_test-upload-file", name: "Upload File", description: "Upload a file. [See the documentation](https://api.slack.com/messaging/files#uploading_files)", - version: "0.1.2", + version: "0.1.3", type: "action", props: { slack, diff --git a/components/slack_v2_test/sources/new-channel-created/new-channel-created.mjs b/components/slack_v2_test/sources/new-channel-created/new-channel-created.mjs index f47f107fa0cdd..6314d72c5f631 100644 --- a/components/slack_v2_test/sources/new-channel-created/new-channel-created.mjs +++ b/components/slack_v2_test/sources/new-channel-created/new-channel-created.mjs @@ -5,7 +5,7 @@ export default { ...common, key: "slack_v2_test-new-channel-created", name: "New Channel Created (Instant)", - version: "0.0.11", + version: "0.0.12", description: "Emit new event when a new channel is created.", type: "source", dedupe: "unique", diff --git a/components/slack_v2_test/sources/new-interaction-event-received/new-interaction-event-received.mjs b/components/slack_v2_test/sources/new-interaction-event-received/new-interaction-event-received.mjs index 1d9d0a4c2072e..323b351cf1a83 100644 --- a/components/slack_v2_test/sources/new-interaction-event-received/new-interaction-event-received.mjs +++ b/components/slack_v2_test/sources/new-interaction-event-received/new-interaction-event-received.mjs @@ -3,7 +3,7 @@ import sampleEmit from "./test-event.mjs"; export default { name: "New Interaction Events (Instant)", - version: "0.0.21", + version: "0.0.22", key: "slack_v2_test-new-interaction-event-received", description: "Emit new events on new Slack [interactivity events](https://api.slack.com/interactivity) sourced from [Block Kit interactive elements](https://api.slack.com/interactivity/components), [Slash commands](https://api.slack.com/interactivity/slash-commands), or [Shortcuts](https://api.slack.com/interactivity/shortcuts).", type: "source", diff --git a/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs b/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs index f501272a0fd61..c0ee0f8f66888 100644 --- a/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs +++ b/components/slack_v2_test/sources/new-keyword-mention/new-keyword-mention.mjs @@ -7,7 +7,7 @@ export default { ...common, key: "slack_v2_test-new-keyword-mention", name: "New Keyword Mention (Instant)", - version: "0.0.10", + version: "0.0.11", description: "Emit new event when a specific keyword is mentioned in a channel", type: "source", dedupe: "unique", diff --git a/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs b/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs index 45d8c8e59c740..dc032fadd2b03 100644 --- a/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs +++ b/components/slack_v2_test/sources/new-message-in-channels/new-message-in-channels.mjs @@ -7,7 +7,7 @@ export default { ...common, key: "slack_v2_test-new-message-in-channels", name: "New Message In Channels (Instant)", - version: "1.0.27", + version: "1.0.28", description: "Emit new event when a new message is posted to one or more channels", type: "source", dedupe: "unique", diff --git a/components/slack_v2_test/sources/new-reaction-added/new-reaction-added.mjs b/components/slack_v2_test/sources/new-reaction-added/new-reaction-added.mjs index 00ade53409a4f..79e4d67a7486d 100644 --- a/components/slack_v2_test/sources/new-reaction-added/new-reaction-added.mjs +++ b/components/slack_v2_test/sources/new-reaction-added/new-reaction-added.mjs @@ -5,7 +5,7 @@ export default { ...common, key: "slack_v2_test-new-reaction-added", name: "New Reaction Added (Instant)", - version: "1.1.27", + version: "1.1.28", description: "Emit new event when a member has added an emoji reaction to a message", type: "source", dedupe: "unique", diff --git a/components/slack_v2_test/sources/new-saved-message/new-saved-message.mjs b/components/slack_v2_test/sources/new-saved-message/new-saved-message.mjs index 29f7b3bdbe912..9884b8b86835e 100644 --- a/components/slack_v2_test/sources/new-saved-message/new-saved-message.mjs +++ b/components/slack_v2_test/sources/new-saved-message/new-saved-message.mjs @@ -5,7 +5,7 @@ export default { ...common, key: "slack_v2_test-new-saved-message", name: "New Saved Message (Instant)", - version: "0.0.7", + version: "0.0.8", description: "Emit new event when a message is saved. Note: The endpoint is marked as deprecated, and Slack might shut this off at some point down the line.", type: "source", dedupe: "unique", diff --git a/components/slack_v2_test/sources/new-user-added/new-user-added.mjs b/components/slack_v2_test/sources/new-user-added/new-user-added.mjs index 57ba92505e985..b06cb290e8b21 100644 --- a/components/slack_v2_test/sources/new-user-added/new-user-added.mjs +++ b/components/slack_v2_test/sources/new-user-added/new-user-added.mjs @@ -5,7 +5,7 @@ export default { ...common, key: "slack_v2_test-new-user-added", name: "New User Added (Instant)", - version: "0.0.5", + version: "0.0.6", description: "Emit new event when a new member joins a workspace.", type: "source", dedupe: "unique", diff --git a/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs b/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs index e8046b51a5b8e..76704da14714e 100644 --- a/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs +++ b/components/slack_v2_test/sources/new-user-mention/new-user-mention.mjs @@ -7,7 +7,7 @@ export default { ...common, key: "slack_v2_test-new-user-mention", name: "New User Mention (Instant)", - version: "0.0.10", + version: "0.0.11", description: "Emit new event when a username or specific keyword is mentioned in a channel", type: "source", dedupe: "unique", From 064aca68411029d0ebc8ad665a7a8a560ff5b674 Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Mon, 6 Oct 2025 13:59:01 -0400 Subject: [PATCH 33/34] include public and private channels in Find Message search --- .../slack_v2_test/actions/find-message/find-message.mjs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/slack_v2_test/actions/find-message/find-message.mjs b/components/slack_v2_test/actions/find-message/find-message.mjs index 2b9dacf1aba23..d91010c24bf7f 100644 --- a/components/slack_v2_test/actions/find-message/find-message.mjs +++ b/components/slack_v2_test/actions/find-message/find-message.mjs @@ -5,7 +5,7 @@ export default { key: "slack_v2_test-find-message", name: "Find Message", description: "Find a Slack message. [See the documentation](https://api.slack.com/methods/search.messages)", - version: "0.1.1", + version: "0.1.2", type: "action", props: { slack, @@ -53,6 +53,7 @@ export default { sort: params.sort, sort_dir: params.sort_dir, cursor: params.cursor, + channel_types: params.channel_types, }, headers: { "Authorization": `Bearer ${this.slack.getToken()}`, @@ -71,6 +72,7 @@ export default { query: this.query, sort: this.sort, sort_dir: this.sortDirection, + channel_types: "public_channel,private_channel", }; let cursor; From df34f9fde2e71a137547dac8125e64c67f99ba1d Mon Sep 17 00:00:00 2001 From: js07 <19861096+js07@users.noreply.github.com> Date: Mon, 6 Oct 2025 14:00:28 -0400 Subject: [PATCH 34/34] update docs link in Find Message action The action now uses the assistant.search.context method --- components/slack_v2_test/actions/find-message/find-message.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/slack_v2_test/actions/find-message/find-message.mjs b/components/slack_v2_test/actions/find-message/find-message.mjs index d91010c24bf7f..b31a4adf88057 100644 --- a/components/slack_v2_test/actions/find-message/find-message.mjs +++ b/components/slack_v2_test/actions/find-message/find-message.mjs @@ -4,7 +4,7 @@ import slack from "../../slack_v2_test.app.mjs"; export default { key: "slack_v2_test-find-message", name: "Find Message", - description: "Find a Slack message. [See the documentation](https://api.slack.com/methods/search.messages)", + description: "Find a Slack message. [See the documentation](https://api.slack.com/methods/assistant.search.context)", version: "0.1.2", type: "action", props: {