Skip to content

Conversation

@js07
Copy link
Collaborator

@js07 js07 commented Oct 13, 2025

WHY

Summary by CodeRabbit

  • New Features
    • Added a full Slack v2 integration with comprehensive messaging, scheduling, file upload, reminders, metadata, profile/group management, reactions, search, and admin operations.
    • Added many Slack actions (send/update/delete messages, Block Kit builders, advanced/large-message flows, channel create/archive/topic/description, invite/kick, reminders, upload/delete/list files, set status, find users/messages, group management).
    • Added Slack event sources for channel creation, messages, mentions, keywords, reactions, saved messages, interactions, and user joins, plus shared constants and caching utilities.

js07 added 12 commits October 13, 2025 16:13
- List users by real name in dropdowns
- List users by real name in action summary
- List files using bot token if available
- Get files using bot token if available
- Show configuration error if bot is not a member of the channel
- Add `addToChannel` prop to Send Message actions
- Check user's access to channel in message actions
- Use RTS API to search messages in Find Message action
- List only channels, not DMs, in 'file' actions
- List only channels, not DMs, in 'message' triggers
- Handle permission error fetching message in New Reaction trigger

Context:

In Slack's v2 OAuth flow, user access tokens and bot access tokens are
issued with separate scopes. User tokens are used to perform actions on
behalf of a user, while bot tokens are used for actions performed by the
app itself. To send a message to a private channel or DM, the bot must
be a member of that channel.

Due to permission restrictions, some endpoints require bot tokens for
access, such as viewing messages in DMs and listing files. As a result,
access to messages and files in DMs is no longer supported.
This allows us to support both existing and new Slack connections
created with Slack's v2 OAuth flow, with or without the deprecated
perspectival chat:write:user and chat:write:bot scopes:

| Slack auth | Token Types | Scopes                | as_user Values  |
|------------|-------------|-----------------------|-----------------|
| v1         | user        | chat:write:bot, :user | true, false     |
| v2         | user, bot   | chat:write:bot, :user | true, false     |
| v2         | user, bot   | chat:write            | true            |

In v2, when the chat:write scope is requested, it replaces
chat:write:bot and chat:write:user. Without the chat:write:bot scope,
as_user cannot be false; user tokens always post as the user, and bot
tokens always post as the bot. In v2, with or without the chat:write:bot
scope, we can use the bot token if as_user is false since it will have
permission to post as itself. And we MAY pass through as_user if it's
true since Slack allows it even when it's superfluous. However, we MUST
pass through as_user if it's true AND the user token still has the
chat:write:bot scope since otherwise the message will post as the bot
user instead of the user.

See:
https://docs.slack.dev/reference/methods/chat.postMessage/#legacy_as_user
so it is shown in the component form by default so users aren't suprised
by the new invite behavior
it's no longer supported for new auths since they will not have the
im:history scope
Fallback to search.messages API method if missing scopese
Future-proofs Slack components to support proxying requests
add/update logged messages when unable to add app to channel
The following components have new minor versions:

- delete-message
- find-message
- get-file
- list-files
- reply-to-a-message
- send-block-kit-message
- send-large-message
- send-message
- send-message-advanced
- send-message-to-channel
- send-message-to-user-or-group
- update-message
- new-keyword-mention
- new-message-in-channels
- new-user-mention

All other Slack components have new patch versions.
@vercel
Copy link

vercel bot commented Oct 13, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
pipedream-docs Ignored Ignored Oct 22, 2025 4:32pm
pipedream-docs-redirect-do-not-edit Ignored Ignored Oct 22, 2025 4:32pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 13, 2025

Walkthrough

Adds a new Slack v2 component: app implementation, package, 40+ action modules (messages, CRUD, listings, membership, uploads), multiple event sources with caching/helpers, shared constants/utilities, and many test-event fixtures. All files are new additions under components/slack_v2 and minor newline fixes in unrelated apps.

Changes

Cohort / File(s) Summary
Core App & Package
components/slack_v2/slack_v2.app.mjs, components/slack_v2/package.json
New slack_v2 app implementation and package manifest providing extensive propDefinitions, API wrappers, retry logic, token selection, and many helper methods; package.json adds dependencies.
Common Messaging Utilities
components/slack_v2/actions/common/send-message.mjs, components/slack_v2/actions/common/build-blocks.mjs, components/slack_v2/actions/common/*
New shared modules for composing and sending Slack messages: rich send-message logic (scheduling, metadata, unfurl, helpers) and a Build Blocks helper supporting array/configure modes and block creation.
Actions — Message Sending & Variants
components/slack_v2/actions/send-message/*.mjs, .../send-block-kit-message.mjs, .../send-large-message.mjs, .../send-message-advanced.mjs, .../reply-to-a-message.mjs, .../approve-workflow.mjs, .../send-message-to-*/*.mjs
Many new actions to send messages to channels, users, or groups, support Block Kit, large messages, advanced options, threaded replies, approval workflow, and composed behaviors using common modules.
Actions — Channel / Resource CRUD
components/slack_v2/actions/create-channel/*.mjs, .../archive-channel.mjs, .../set-channel-topic.mjs, .../set-channel-description.mjs, .../update-profile.mjs, .../set-status.mjs
New actions to create/archive channels and update channel/topic/description, user profiles, and status.
Actions — Message/File Operations
components/slack_v2/actions/delete-message/*.mjs, .../update-message.mjs, .../delete-file.mjs, .../get-file.mjs, .../upload-file.mjs
New actions for deleting/updating messages, file retrieval, deletion, and a two-phase file upload flow.
Actions — Listing, Search & Helpers
components/slack_v2/actions/find-message/*.mjs, .../list-*.mjs
New paginated listing and search actions (channels, files, users, members, replies) and assistant-backed message search with cursor handling.
Actions — Membership & Groups
components/slack_v2/actions/invite-user-to-channel/*.mjs, .../kick-user.mjs, .../update-group-members.mjs, find-user-by-email.mjs
New actions for inviting/kicking users, updating usergroup memberships, and finding users by email with edge-case error handling.
Action — Reactions & Reminders
components/slack_v2/actions/add-emoji-reaction/add-emoji-reaction.mjs, .../create-reminder/create-reminder.mjs
New emoji reaction and reminder creation actions.
Block Builder Consumers
components/slack_v2/actions/send-block-kit-message/send-block-kit-message.mjs, components/slack_v2/actions/send-message-advanced/send-message-advanced.mjs
Actions that combine common send-message logic with build-blocks for dynamic block generation and delegation to common.run.
Sources — Base & Constants
components/slack_v2/sources/common/base.mjs, components/slack_v2/sources/common/constants.mjs
New base source providing caching, name-resolution helpers, emit/run flow, and constants for events/subtypes and cache settings.
Sources — Event Triggers & Fixtures
components/slack_v2/sources/*/* (new-channel-created, new-interaction-event-received, new-keyword-mention, new-message-in-channels, new-reaction-added, new-saved-message, new-user-added, new-user-mention + many test-event.mjs fixtures)
New event sources extending the base: define props (slackApphook), dynamic eventNames, processEvent filtering/enrichment (resolveNames, ignoreBot, keyword, subtypes), getSummary, and sampleEmit fixtures.
Constants for Actions
components/slack_v2/actions/common/constants.mjs
Channel type and limit constants used by action propDefinitions.
Misc formatting fixes
components/cloud_66/cloud_66.app.mjs, components/coinapi/coinapi.app.mjs, components/copicake/copicake.app.mjs, components/cronly/cronly.app.mjs, components/crossmint/crossmint.app.mjs, components/topdesk/topdesk.app.mjs
Trailing newline/file-end normalization only; no functional changes.

Sequence Diagram(s)

sequenceDiagram
    participant Workflow as Workflow
    participant Action as Slack Action
    participant App as slack_v2.app
    participant Slack as Slack API

    Workflow->>Action: invoke action with props
    Action->>Action: normalize inputs, optionally build blocks
    Action->>App: call method (e.g., postMessage, upload, list)
    App->>App: select token & wrap request with retries/backoff
    App->>Slack: HTTP request
    Slack-->>App: response (ok / rate_limit / error)
    alt retryable error
        App->>App: backoff & retry
        App->>Slack: retry request
    else success
        App-->>Action: return response
    else fatal
        App-->>Action: throw error
    end
    Action->>Workflow: export summary and return response
Loading
sequenceDiagram
    participant Slack as Slack Events
    participant Hook as slackApphook
    participant Source as Event Source
    participant Cache as Name Cache
    participant Pipedream as Pipedream Emit

    Slack->>Hook: deliver event
    Hook->>Source: run(event)
    Source->>Source: processEvent (filter by type/subtype/keyword/bot)
    alt resolveNames enabled
        Source->>Cache: maybeCached(key, fetchFn)
        Cache-->>Source: cached value or fresh fetch
        Source->>Source: enrich event with resolved names
    end
    Source->>Pipedream: emit standardized event (id, summary, ts, payload)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Suggested reviewers

  • lcaresia

Poem

🐰
I hopped through folders, built blocks in a row,
Messages, sources, and caching that grow.
From reactions to uploads, each action in sight,
A rabbit’s short rhyme for this Slack_v2 flight.
Merge on — may workflows hum through the night.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request description contains only the template structure with a "## WHY" section header but no actual content—just a placeholder comment ("") that was left blank by the author. The description lacks any explanation of the rationale, scope of changes, or purpose for introducing the Slack v2 components. This represents a largely incomplete description that fails to provide the required information for reviewers to understand why these changes are being made. The author should complete the "## WHY" section by providing a clear explanation of the motivation and rationale for adding these Slack v2 components. This should include context about what problems these components solve, why a new v2 implementation is needed, and any other relevant background information that would help reviewers understand the scope and importance of this change.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title "Slack v2 components" directly relates to the main change in the changeset, which is the introduction of a comprehensive new Slack v2 component module. The title is concise, clear, and accurately summarizes the primary purpose of adding multiple Slack v2 actions, sources, app integration, and supporting utilities. While the title is somewhat broad, it appropriately covers the scope of the changes without being vague or misleading, and follows the principle that titles need not enumerate every detail of the changeset.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch slack-v2-support

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1f27d2b and a17e69c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (6)
  • components/cloud_66/cloud_66.app.mjs (1 hunks)
  • components/coinapi/coinapi.app.mjs (1 hunks)
  • components/copicake/copicake.app.mjs (1 hunks)
  • components/cronly/cronly.app.mjs (1 hunks)
  • components/crossmint/crossmint.app.mjs (1 hunks)
  • components/topdesk/topdesk.app.mjs (1 hunks)
✅ Files skipped from review due to trivial changes (6)
  • components/copicake/copicake.app.mjs
  • components/coinapi/coinapi.app.mjs
  • components/cronly/cronly.app.mjs
  • components/crossmint/crossmint.app.mjs
  • components/cloud_66/cloud_66.app.mjs
  • components/topdesk/topdesk.app.mjs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: pnpm publish
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Lint Code Base
  • GitHub Check: Verify TypeScript components

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@js07 js07 marked this pull request as ready for review October 15, 2025 20:30
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
components/slack/actions/send-large-message/send-large-message.mjs (1)

49-64: Fix metadata_event_payload handling to support object inputs.

When metadata_event_payload is provided as an object (not a string), metadataEventPayload remains undefined, resulting in event_payload: undefined in the metadata object. The code only assigns metadataEventPayload when parsing a string.

Apply this diff to handle both string and object inputs:

-    let metadataEventPayload;
+    let metadataEventPayload = this.metadata_event_payload;

     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,
       };
     }
components/slack/actions/common/send-message.mjs (1)

213-229: Fix metadata_event_payload handling to support object inputs.

When metadata_event_payload is provided as an object (not a string), metadataEventPayload remains undefined, resulting in event_payload: undefined in the metadata object. The code only assigns metadataEventPayload when parsing a string.

Apply this diff to handle both string and object inputs:

-    let metadataEventPayload;
+    let metadataEventPayload = this.metadata_event_payload;

     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,
       };
     }
🧹 Nitpick comments (1)
components/slack/actions/update-group-members/update-group-members.mjs (1)

54-55: Good defensive fix!

Adding default empty arrays prevents TypeError when the optional arrays are undefined, since line 62 calls .includes() and line 63 spreads the array.

Optionally, if a user appears in both the existing group members and usersToAdd, they may be duplicated in the final array (line 63). Consider verifying whether the Slack API deduplicates on their end, or add explicit deduplication if needed:

   users = users.filter((user) => !usersToRemove.includes(user));
-  users.push(...usersToAdd);
+  const allUsers = [...new Set([...users, ...usersToAdd])];
   const response = await this.slack.updateGroupMembers({
     usergroup: userGroup,
-    users,
+    users: allUsers,
     team_id: team,
   });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 25fe600 and 18a1e8f.

📒 Files selected for processing (51)
  • components/azure_cosmos_db/azure_cosmos_db.app.mjs (1 hunks)
  • components/binalyze_air/binalyze_air.app.mjs (1 hunks)
  • components/brainbase_labs/brainbase_labs.app.mjs (1 hunks)
  • components/google_perspective/google_perspective.app.mjs (1 hunks)
  • components/n1n/n1n.app.mjs (1 hunks)
  • components/slack/actions/add-emoji-reaction/add-emoji-reaction.mjs (1 hunks)
  • components/slack/actions/approve-workflow/approve-workflow.mjs (1 hunks)
  • components/slack/actions/archive-channel/archive-channel.mjs (1 hunks)
  • components/slack/actions/common/send-message.mjs (4 hunks)
  • components/slack/actions/create-channel/create-channel.mjs (1 hunks)
  • components/slack/actions/create-reminder/create-reminder.mjs (1 hunks)
  • components/slack/actions/delete-file/delete-file.mjs (1 hunks)
  • components/slack/actions/delete-message/delete-message.mjs (1 hunks)
  • components/slack/actions/find-message/find-message.mjs (3 hunks)
  • components/slack/actions/find-user-by-email/find-user-by-email.mjs (1 hunks)
  • components/slack/actions/get-file/get-file.mjs (3 hunks)
  • components/slack/actions/invite-user-to-channel/invite-user-to-channel.mjs (1 hunks)
  • components/slack/actions/kick-user/kick-user.mjs (1 hunks)
  • components/slack/actions/list-channels/list-channels.mjs (1 hunks)
  • components/slack/actions/list-files/list-files.mjs (3 hunks)
  • components/slack/actions/list-group-members/list-group-members.mjs (1 hunks)
  • components/slack/actions/list-members-in-channel/list-members-in-channel.mjs (1 hunks)
  • components/slack/actions/list-replies/list-replies.mjs (2 hunks)
  • components/slack/actions/list-users/list-users.mjs (1 hunks)
  • components/slack/actions/reply-to-a-message/reply-to-a-message.mjs (1 hunks)
  • components/slack/actions/send-block-kit-message/send-block-kit-message.mjs (1 hunks)
  • components/slack/actions/send-large-message/send-large-message.mjs (3 hunks)
  • components/slack/actions/send-message-advanced/send-message-advanced.mjs (1 hunks)
  • components/slack/actions/send-message-to-channel/send-message-to-channel.mjs (1 hunks)
  • components/slack/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs (2 hunks)
  • components/slack/actions/send-message/send-message.mjs (1 hunks)
  • components/slack/actions/set-channel-description/set-channel-description.mjs (1 hunks)
  • components/slack/actions/set-channel-topic/set-channel-topic.mjs (1 hunks)
  • components/slack/actions/set-status/set-status.mjs (1 hunks)
  • components/slack/actions/update-group-members/update-group-members.mjs (2 hunks)
  • components/slack/actions/update-message/update-message.mjs (1 hunks)
  • components/slack/actions/update-profile/update-profile.mjs (1 hunks)
  • components/slack/actions/upload-file/upload-file.mjs (1 hunks)
  • components/slack/actions/verify-slack-signature/verify-slack-signature.mjs (0 hunks)
  • components/slack/package.json (1 hunks)
  • components/slack/slack.app.mjs (9 hunks)
  • components/slack/sources/new-channel-created/new-channel-created.mjs (1 hunks)
  • components/slack/sources/new-direct-message/new-direct-message.mjs (0 hunks)
  • components/slack/sources/new-direct-message/test-event.mjs (0 hunks)
  • components/slack/sources/new-interaction-event-received/new-interaction-event-received.mjs (1 hunks)
  • components/slack/sources/new-keyword-mention/new-keyword-mention.mjs (2 hunks)
  • components/slack/sources/new-message-in-channels/new-message-in-channels.mjs (2 hunks)
  • components/slack/sources/new-reaction-added/new-reaction-added.mjs (2 hunks)
  • components/slack/sources/new-saved-message/new-saved-message.mjs (1 hunks)
  • components/slack/sources/new-user-added/new-user-added.mjs (1 hunks)
  • components/slack/sources/new-user-mention/new-user-mention.mjs (2 hunks)
💤 Files with no reviewable changes (3)
  • components/slack/sources/new-direct-message/new-direct-message.mjs
  • components/slack/actions/verify-slack-signature/verify-slack-signature.mjs
  • components/slack/sources/new-direct-message/test-event.mjs
🧰 Additional context used
🧬 Code graph analysis (3)
components/slack/actions/common/send-message.mjs (2)
components/slack/slack.app.mjs (3)
  • channelId (24-27)
  • channel (505-507)
  • channel (514-516)
components/slack/actions/send-large-message/send-large-message.mjs (1)
  • channelName (91-91)
components/slack/actions/send-large-message/send-large-message.mjs (1)
components/slack/slack.app.mjs (2)
  • channel (505-507)
  • channel (514-516)
components/slack/slack.app.mjs (2)
components/slack/actions/get-file/get-file.mjs (1)
  • response (53-55)
components/slack/actions/find-message/find-message.mjs (3)
  • response (115-119)
  • response (134-138)
  • cursor (112-112)
🔇 Additional comments (29)
components/slack/sources/new-channel-created/new-channel-created.mjs (1)

8-8: Version bump looks good.

No issues spotted with the update to version 0.0.11.

components/slack/actions/archive-channel/archive-channel.mjs (1)

8-8: Version bump looks good.

Version increment to 0.0.25 is consistent and introduces no regressions.

components/slack/actions/set-channel-topic/set-channel-topic.mjs (1)

7-7: Version bump looks good.

Matches surrounding Slack action updates; no further changes needed.

components/slack/actions/update-profile/update-profile.mjs (1)

8-8: Version bump looks good.

Increment to 0.0.25 keeps this action aligned with the other Slack updates in the PR. No further action needed.

components/slack/actions/approve-workflow/approve-workflow.mjs (1)

8-8: Version bump looks good.

Matches the broader Slack release versioning pattern with no functional impact.

components/slack/sources/new-user-added/new-user-added.mjs (1)

8-8: Version bump looks good

The source metadata increment is consistent and introduces no risk.

components/slack/actions/create-reminder/create-reminder.mjs (1)

7-7: Version bump looks good

The metadata-only bump to 0.0.26 keeps this action aligned with the broader Slack release. 👍

components/slack/actions/send-block-kit-message/send-block-kit-message.mjs (1)

10-10: Version bump looks good.

Increment aligns with the broader Slack v2 rollout; no further action needed.

components/slack/actions/kick-user/kick-user.mjs (1)

8-8: Version bump looks consistent.

Matches the broader component release cadence. No further issues spotted.

components/azure_cosmos_db/azure_cosmos_db.app.mjs (1)

11-11: LGTM — object terminator restored.

Thanks for reintroducing the closing };; keeps the module syntax tidy.

components/slack/actions/reply-to-a-message/reply-to-a-message.mjs (1)

9-9: Version bump looks good

Increment to 0.2.0 is consistent with the ongoing Slack updates in this PR.

components/slack/sources/new-saved-message/new-saved-message.mjs (1)

8-8: Version bump looks good.

The patch version increment is appropriate for this maintenance update.

Note: Line 9 mentions this endpoint is deprecated by Slack and may be shut down in the future. Consider tracking this deprecation to ensure timely migration or removal when Slack discontinues support.

components/slack/actions/find-user-by-email/find-user-by-email.mjs (1)

7-7: LGTM! Version bump aligns with OAuth v2 compatibility.

The version increment appropriately marks this action as compatible with the underlying Slack OAuth v2 changes, even though no logic changes were required.

components/slack/actions/list-channels/list-channels.mjs (1)

7-7: Verify that no functional changes are needed for v2 OAuth support.

The version bump appears intentional for indicating v2 OAuth compatibility. Since this is a read-only action that only lists channels (as indicated by the readOnlyHint: true annotation), it likely doesn't require the additional props (like addToChannel) or pre-run logic that other Slack actions in this PR received.

However, please confirm that the underlying slack.app.mjs component handles all necessary v2 OAuth changes transparently, and that this action will function correctly without any code modifications.

components/slack/actions/add-emoji-reaction/add-emoji-reaction.mjs (1)

7-7: LGTM: Version bump for coordinated release.

The metadata-only version bump aligns with the coordinated release pattern across Slack actions in this PR.

components/slack/actions/update-group-members/update-group-members.mjs (1)

7-7: LGTM!

Version bump is appropriate for this defensive code improvement.

components/slack/actions/list-replies/list-replies.mjs (3)

1-1: LGTM!

The constants import is correctly added and used for channel type constraints.


8-8: LGTM!

The version bump from 0.0.24 to 0.0.25 is appropriate for this change.


21-28: Confirm DM/group channel support isn't required: list-replies now limits the picker to CHANNEL_TYPE.PUBLIC and CHANNEL_TYPE.PRIVATE—verify no existing workflows depend on CHANNEL_TYPE.IM or CHANNEL_TYPE.MPIM.

components/slack/sources/new-reaction-added/new-reaction-added.mjs (2)

8-8: LGTM: Version bump aligns with error handling changes.

The minor version increment from 1.1.26 to 1.2.0 appropriately reflects the defensive error handling added to processEvent.


100-107: Approve with observability enhancements

  • Replace console.log with a structured logger or emit a metric on getMessage failure.
  • Downstream Slack handlers do not reference event.message, so undefined messages won’t break processing.
  • If message retrieval is critical, attach an error field to the returned event for better troubleshooting.
components/slack/actions/send-large-message/send-large-message.mjs (2)

38-42: LGTM! Pre-send channel invitation logic is correct.

The conditional check for addToChannel and subsequent call to maybeAddAppToChannels ensures the app is properly invited to the channel before sending the message. The addToChannel prop is correctly inherited from the common send-message module.


91-91: LGTM! Channel name resolution refactored to use helper.

Using getChannelDisplayName centralizes the channel display logic and improves maintainability.

components/slack/sources/new-user-mention/new-user-mention.mjs (1)

20-25: LGTM! Channel type constraints correctly applied.

The dynamic type constraints limit conversation selection to public and private channels using shared constants, aligning with the pattern adopted across other Slack sources and actions in this PR.

components/slack/actions/common/send-message.mjs (2)

12-17: LGTM! addToChannel prop and pre-send logic implemented correctly.

The new addToChannel prop allows users to opt into automatic channel invitation, and the pre-send logic efficiently caches channelId and invokes maybeAddAppToChannels when needed.

Also applies to: 190-196


258-258: LGTM! Channel name resolution refactored to use helper.

Using getChannelDisplayName centralizes the channel display logic and improves maintainability.

components/slack/sources/new-keyword-mention/new-keyword-mention.mjs (1)

20-25: LGTM! Channel type constraints correctly applied.

The dynamic type constraints limit conversation selection to public and private channels using shared constants, consistent with the pattern in new-user-mention.mjs and other Slack components.

components/slack/actions/get-file/get-file.mjs (1)

21-29: LGTM! All changes follow the established pattern.

The channel type constraints, addToChannel prop, and pre-run invitation logic are correctly implemented and consistent with the pattern adopted across other Slack actions in this PR.

Also applies to: 30-35, 47-51

components/slack/actions/list-files/list-files.mjs (1)

21-29: LGTM! All changes follow the established pattern.

The channel type constraints, addToChannel prop, and pre-run invitation logic are correctly implemented and consistent with the pattern adopted across other Slack actions in this PR.

Also applies to: 30-35, 64-68

Comment on lines 561 to 569
if ([
"not_in_channel",
"channel_not_found",
].some((errorType) => error.includes(errorType)) && asBot) {
const followUp = method.startsWith("chat.")
? "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."
: "Ensure the bot is a member of the channel.";
throw new ConfigurationError(`${error}\n${followUp}`);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix error handling before it throws a TypeError

error is typically an Error object (e.g., WebAPIPlatformError), so calling error.includes(...) throws TypeError: error.includes is not a function, hiding the original Slack error instead of surfacing it. Coerce the error to a string (or read error.data?.error) before running .includes, and use that value in the ConfigurationError message.

-        if ([
-          "not_in_channel",
-          "channel_not_found",
-        ].some((errorType) => error.includes(errorType)) && asBot) {
-          const followUp = method.startsWith("chat.")
-            ? "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."
-            : "Ensure the bot is a member of the channel.";
-          throw new ConfigurationError(`${error}\n${followUp}`);
-        }
-        throw error;
+        const errorMessage = typeof error === "string"
+          ? error
+          : error?.data?.error || error.message || `${error}`;
+        if ([
+          "not_in_channel",
+          "channel_not_found",
+        ].some((errorType) => errorMessage.includes(errorType)) && asBot) {
+          const followUp = method.startsWith("chat.")
+            ? "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."
+            : "Ensure the bot is a member of the channel.";
+          throw new ConfigurationError(`${errorMessage}\n${followUp}`);
+        }
+        throw error;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ([
"not_in_channel",
"channel_not_found",
].some((errorType) => error.includes(errorType)) && asBot) {
const followUp = method.startsWith("chat.")
? "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."
: "Ensure the bot is a member of the channel.";
throw new ConfigurationError(`${error}\n${followUp}`);
}
const errorMessage = typeof error === "string"
? error
: error?.data?.error || error.message || `${error}`;
if ([
"not_in_channel",
"channel_not_found",
].some((errorType) => errorMessage.includes(errorType)) && asBot) {
const followUp = method.startsWith("chat.")
? "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."
: "Ensure the bot is a member of the channel.";
throw new ConfigurationError(`${errorMessage}\n${followUp}`);
}
throw error;
🤖 Prompt for AI Agents
In components/slack/slack.app.mjs around lines 561 to 569, the code assumes
`error` is a string and calls `error.includes(...)`, which throws a TypeError
when `error` is an Error object; change the logic to extract a safe string first
(e.g., const errMsg = typeof error === "string" ? error : error?.data?.error ??
String(error)), use `errMsg.includes(...)` for the conditional, and use `errMsg`
when constructing the ConfigurationError so the original Slack error is
preserved and no TypeError is thrown.

guard against unexpected types
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/slack/actions/find-message/find-message.mjs (1)

42-42: Fix stray backtick in description.

Clean up the string.

-      description: "Sort ascending (asc) or descending (desc)`",
+      description: "Sort ascending (asc) or descending (desc)",
♻️ Duplicate comments (1)
components/slack/slack.app.mjs (1)

561-569: Implement the error message extraction suggested in the previous review.

While ${error} prevents the TypeError, the previous review correctly noted that extracting error.data?.error would preserve the original Slack error message more accurately. The current string coercion might lose structured error details.

Apply the diff suggested in the previous review to properly extract and preserve error details.

🧹 Nitpick comments (7)
components/slack/slack.app.mjs (1)

502-527: Consider logging caught errors for debugging.

While the fallback strings provide good UX when name resolution fails, silently catching errors at lines 509 and 522 could hide legitimate issues (e.g., auth failures, rate limits). Consider adding console.error(err) or similar logging in the catch blocks to aid troubleshooting.

components/slack/actions/find-message/find-message.mjs (6)

80-90: Normalize merge precedence to avoid accidental overrides.

Currently, ...baseMessage and ...rest can override canonical fields (user, ts, text, type, channel). If the canonical fields should win, flip the spread order.

-      const normalized = {
-        type: "message",
-        user: authorUserId,
-        team: teamId,
-        ts: messageTs,
-        text: content,
-        permalink,
-        channel: channelInfo,
-        ...baseMessage,
-        ...rest,
-      };
+      const normalized = {
+        ...baseMessage,
+        ...rest,
+        type: "message",
+        user: authorUserId,
+        team: teamId,
+        ts: messageTs,
+        text: content,
+        permalink,
+        channel: channelInfo,
+      };

If overriding by baseMessage/rest is intentional, consider adding a brief comment to document the precedence.


115-119: Align with existing Slack client usage: pass throwRateLimitError.

Other Slack app calls set throwRateLimitError: true. Consider adding it here for consistency and clearer retry behavior. Based on relevant code snippets.

-        const response = await this.slack.assistantSearch({
+        const response = await this.slack.assistantSearch({
           ...baseParams,
           channel_types: "public_channel,private_channel",
           cursor,
+          throwRateLimitError: true,
         });

117-118: Channel types exclude DMs — intentional?

assistantSearch is limited to public/private channels. If you also want to match IM/MPIM messages, add im,mpim or make it configurable.

-          channel_types: "public_channel,private_channel",
+          channel_types: "public_channel,private_channel,im,mpim",

134-138: Also pass throwRateLimitError to searchMessages.

Match the pattern used across the Slack app for consistent error semantics. Based on relevant code snippets.

-        const response = await this.slack.searchMessages({
+        const response = await this.slack.searchMessages({
           ...baseParams,
           count,
           page,
+          throwRateLimitError: true,
         });

190-194: Avoid sending undefined params to the API.

Only include sort/sort_dir when set to reduce risk of invalid query params.

-    const baseParams = {
-      query: this.query,
-      sort: this.sort,
-      sort_dir: this.sortDirection,
-    };
+    const baseParams = {
+      query: this.query,
+      ...(this.sort && { sort: this.sort }),
+      ...(this.sortDirection && { sort_dir: this.sortDirection }),
+    };

197-205: Optional: Fallback if assistantSearch is unavailable.

If older app versions or tokens don’t expose assistantSearch, pre-check and use searchMessages directly.

-    try {
-      matches = await this.searchWithAssistant(baseParams, maxResults);
-    } catch (error) {
+    try {
+      if (typeof this.slack.assistantSearch !== "function") {
+        matches = await this.searchWithSearchMessages(baseParams, maxResults);
+      } else {
+        matches = await this.searchWithAssistant(baseParams, maxResults);
+      }
+    } catch (error) {
       if (this.shouldFallbackToSearchMessages(error)) {
         matches = await this.searchWithSearchMessages(baseParams, maxResults);
       } else {
         throw error;
       }
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 18a1e8f and 2df5de6.

📒 Files selected for processing (2)
  • components/slack/actions/find-message/find-message.mjs (3 hunks)
  • components/slack/slack.app.mjs (9 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
components/slack/actions/find-message/find-message.mjs (1)
components/slack/slack.app.mjs (8)
  • channel (505-507)
  • channel (514-516)
  • cursor (187-196)
  • cursor (240-247)
  • cursor (629-629)
  • cursor (653-653)
  • response (557-557)
  • page (293-298)
components/slack/slack.app.mjs (2)
components/slack/actions/find-message/find-message.mjs (3)
  • response (115-119)
  • response (134-138)
  • cursor (112-112)
components/slack/actions/get-file/get-file.mjs (1)
  • response (53-55)
🔇 Additional comments (9)
components/slack/slack.app.mjs (7)

5-5: LGTM: ConfigurationError import supports better error handling.

The import enables throwing user-friendly configuration errors when bot permissions are insufficient, improving the developer experience.


31-32: LGTM: Name resolution improvements are well-implemented.

The changes correctly:

  • Filter out falsy user IDs before lookup
  • Optimize by resolving IDs and usernames in parallel
  • Extract usernames from MPIM purpose values using regex

The inline comment at lines 140-142 clearly explains the optimization rationale.

Also applies to: 124-143


468-473: LGTM: addToChannel prop provides good user control.

The prop definition is clear, with a sensible default that automates the common case while allowing users to opt out when needed.


491-501: LGTM: Token selection logic is clear and correct.

The methods properly implement the bot token precedence: use bot token when explicitly requested via asBot and available, otherwise fall back to user token.


532-536: LGTM: SDK initialization properly supports token options.

The method correctly passes options to getToken for bot vs. user token selection and includes slackApiUrl configuration for custom Slack instances.


679-709: LGTM: Robust error handling for channel invitations.

The method correctly:

  • Checks for bot token availability
  • Validates bot_id before proceeding
  • Handles per-channel errors gracefully with logging
  • Skips DM/group DM channels automatically (per the comment)

Note: Sequential channel processing (lines 694-705) could be slow for many channels, but this is likely necessary given Slack's API constraints.


948-973: LGTM: File methods correctly use bot token with clear documentation.

The comments at lines 952-954 and 968-970 clearly explain that the bot token is required because the files:read scope is only requested for bot tokens in the Pipedream app. This prevents auth errors when using file operations.

components/slack/actions/find-message/find-message.mjs (2)

6-7: Verify the new docs endpoint and scope expectations.

Confirm that assistant.search.context is the intended API reference for this action and that required scopes match the implementation and fallback logic.


163-170: Good fix: guarded includes on errorCode.

The optional chaining prevents TypeError when errorCode is falsy. This resolves the earlier review concern.

Comment on lines 652 to 678
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)) {
realNames[user.name] = user.profile.real_name;
}
}

cursor = nextCursor;
} while (cursor && Object.keys(realNames).length < (ids.length + usernames.length));
return realNames;
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix potential infinite loop and performance issue.

Two issues in realNameLookup:

  1. Infinite loop risk (line 676): If some IDs or usernames don't exist in the workspace, realNames will never reach the expected count, causing the loop to paginate indefinitely.

  2. O(n²) performance (lines 667, 670): Using includes() on arrays inside a loop is inefficient for large lists.

Apply this diff to fix both issues:

-    async realNameLookup(ids = [], usernames = [], throwRateLimitError = true, args = {}) {
+    async realNameLookup(ids = [], usernames = [], throwRateLimitError = true, args = {}) {
+      const idSet = new Set(ids);
+      const usernameSet = new Set(usernames);
       let cursor;
       const realNames = {};
+      const targetCount = ids.length + usernames.length;
       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)) {
+          if (idSet.has(user.id)) {
             realNames[user.id] = user.profile.real_name;
           }
-          if (usernames.includes(user.name)) {
+          if (usernameSet.has(user.name)) {
             realNames[user.name] = user.profile.real_name;
           }
         }
 
         cursor = nextCursor;
-      } while (cursor && Object.keys(realNames).length < (ids.length + usernames.length));
+      } while (cursor && Object.keys(realNames).length < targetCount);
       return realNames;
     },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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)) {
realNames[user.name] = user.profile.real_name;
}
}
cursor = nextCursor;
} while (cursor && Object.keys(realNames).length < (ids.length + usernames.length));
return realNames;
},
async realNameLookup(ids = [], usernames = [], throwRateLimitError = true, args = {}) {
const idSet = new Set(ids);
const usernameSet = new Set(usernames);
let cursor;
const realNames = {};
const targetCount = ids.length + usernames.length;
do {
const {
members: users,
response_metadata: { next_cursor: nextCursor },
} = await this.usersList({
limit: constants.LIMIT,
cursor,
throwRateLimitError,
...args,
});
for (const user of users) {
if (idSet.has(user.id)) {
realNames[user.id] = user.profile.real_name;
}
if (usernameSet.has(user.name)) {
realNames[user.name] = user.profile.real_name;
}
}
cursor = nextCursor;
} while (cursor && Object.keys(realNames).length < targetCount);
return realNames;
},
🤖 Prompt for AI Agents
In components/slack/slack.app.mjs around lines 652 to 678, fix the potential
infinite loop and O(n²) performance by converting ids and usernames to Sets
before pagination, then on each page iterate members once and check membership
against those Sets (removing found entries from the Sets and mapping id/username
to real_name), and stop looping when either the Sets are empty (all requested
items resolved) or there is no next_cursor from usersList; also defensively
handle missing response_metadata/next_cursor to avoid relying on undefined and
thus prevent infinite pagination.

Comment on lines 895 to 900
assistantSearch(args = {}) {
args.count ||= constants.LIMIT;
return this.sdk().apiCall("assistant.search.context", {
...args,
});
},
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify whether assistantSearch should use makeRequest for consistency.

Unlike other methods, assistantSearch calls sdk().apiCall() directly, bypassing the retry logic and rate limit handling provided by makeRequest and _withRetries. This could lead to inconsistent behavior compared to other Slack API calls.

If the assistant.search.context API has different requirements that necessitate direct SDK usage, please document this with a comment. Otherwise, consider refactoring to use makeRequest for consistency.


🏁 Script executed:

#!/bin/bash
# Description: Check if other methods use direct apiCall instead of makeRequest

# Search for other instances of direct apiCall usage
rg -n "sdk\(\)\.apiCall" components/slack/

Length of output: 96


🏁 Script executed:

#!/bin/bash
rg -n "this\\.makeRequest" components/slack/slack.app.mjs -C3

Length of output: 7042


🏁 Script executed:

#!/bin/bash
# Search for makeRequest method definition
rg -n "makeRequest\s*\(" components/slack/slack.app.mjs -A10 | head -30

Length of output: 1074


🏁 Script executed:

#!/bin/bash
# Search for _withRetries within makeRequest method
rg -n "_withRetries" components/slack/slack.app.mjs -C2 | head -40

Length of output: 333


Refactor assistantSearch to use makeRequest for consistency with all other API methods.

The method uses direct sdk().apiCall() instead of makeRequest, bypassing the retry mechanism (3 retries) and rate-limit handling provided by _withRetries that every other API method in this file uses. This creates inconsistent error handling and resilience across the codebase.

At lines 895–900, change:

return this.sdk().apiCall("assistant.search.context", {
  ...args,
});

To:

return this.makeRequest({
  method: "assistant.search.context",
  ...args,
});
🤖 Prompt for AI Agents
In components/slack/slack.app.mjs around lines 895 to 900, assistantSearch
currently calls this.sdk().apiCall directly (bypassing retries/rate-limit) and
should be changed to use this.makeRequest to get the same retry and rate-limit
behavior; set args.count default as before, then return this.makeRequest with
method: "assistant.search.context" and spread the rest of args so the call
signature and returned value remain identical but now uses the shared retry
wrapper.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @slack/web-api npm package doesn't currently expose a method on WebClient for this API method, so we can't call it like the other methods.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@js07, thank you for the clarification! That makes perfect sense—if the @slack/web-api package doesn't expose assistant.search.context as a method on WebClient, then using apiCall() directly is the correct approach.

To help future maintainers understand why this method differs from the others, consider adding a brief comment above the method:

// Uses apiCall directly since assistant.search.context is not exposed as a method on WebClient
assistantSearch(args = {}) {
  args.count ||= constants.LIMIT;
  return this.sdk().apiCall("assistant.search.context", {
    ...args,
  });
}

This will make it clear that the direct apiCall() usage is intentional due to SDK limitations.


✏️ Learnings added
Learnt from: js07
PR: PipedreamHQ/pipedream#18744
File: components/slack/slack.app.mjs:0-0
Timestamp: 2025-10-20T00:28:38.702Z
Learning: In components/slack/slack.app.mjs, the assistantSearch method uses sdk().apiCall() directly instead of makeRequest because the slack/web-api npm package doesn't expose assistant.search.context as a method on WebClient. This direct usage is intentional and necessary when Slack API methods are not yet exposed in the SDK.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@js07 js07 changed the title Support Slack v2 OAuth in components Slack v2 components Oct 20, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 34

🧹 Nitpick comments (36)
components/slack_v2/sources/new-interaction-event-received/README.md (1)

81-85: Vary sentence structure in troubleshooting steps to improve readability.

Three consecutive list items all begin with "Make sure that", which creates repetitive reading. Vary the phrasing for better flow.

 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. Verify 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-v2/actions/send-block-kit-message) Action) or via API call from the Pipedream app.
+1. Confirm the message was sent via Pipedream action (e.g. [Slack - Send Block Kit Message](https://pipedream.com/apps/slack-v2/actions/send-block-kit-message) Action) or via API call from the Pipedream app.
components/slack_v2/sources/new-reaction-added/new-reaction-added.mjs (2)

24-41: Define label/description for slackApphook; remove eslint disable.

Improve clarity and satisfy lint without disabling rules.

-    // eslint-disable-next-line pipedream/props-description,pipedream/props-label
     slackApphook: {
       type: "$.interface.apphook",
       appProp: "slack",
+      label: "Slack Apphook",
+      description: "Internal apphook used to receive Slack events. Do not modify.",
       async eventNames() {

71-79: Simplify emoji parsing; iconEmoji is already a string[] prop.

Current JSON/CSV parsing is unnecessary and fragile. Prefer normalized array handling.

-      let iconEmojiParsed = [];
-
-      try {
-        iconEmojiParsed = typeof this.iconEmoji === "string" ?
-          JSON.parse(this.iconEmoji) :
-          this.iconEmoji;
-      } catch (error) {
-        iconEmojiParsed = this.iconEmoji.replace(/\s+/g, "").split(",");
-      }
+      const iconEmojiParsed = Array.isArray(this.iconEmoji)
+        ? this.iconEmoji.map((e) => e.trim()).filter(Boolean)
+        : [];
components/slack_v2/package.json (1)

1-22: Add license/engines and pin critical deps for reproducible builds.

  • Include a license field and Node engines (Pipedream runs Node 18+).
  • Consider pinning to exact versions for stability, especially @pipedream/platform (recommended to pin 3.1.0). Based on learnings.
 {
   "name": "@pipedream/slack_v2",
   "version": "0.0.1",
   "description": "Pipedream Slack_v2 Components",
   "main": "slack_v2.app.mjs",
+  "license": "MIT",
+  "engines": { "node": ">=18" },
   "keywords": [
@@
   "dependencies": {
-    "@pipedream/platform": "^3.1.0",
-    "@slack/web-api": "^7.9.0",
+    "@pipedream/platform": "3.1.0",
+    "@slack/web-api": "^7.9.0",
     "async-retry": "^1.3.3",
     "lodash": "^4.17.21"
   }
 }

Optionally add "repository" metadata.
To confirm latest safe @slack/web-api compatible with Node 18, please verify current releases and changelog.

components/slack_v2/actions/find-user-by-email/find-user-by-email.mjs (1)

23-31: LGTM; consider clearer handling when user isn’t found.

Slack returns ok: false with users_not_found. Optionally set a friendly summary or throw when not found.

     const response = await this.slack.lookupUserByEmail({
       email: this.email,
     });
-    if (response.ok) {
+    if (response.ok) {
       $.export("$summary", `Successfully found user with ID ${response.user.id}`);
-    }
+    } else if (response.error === "users_not_found") {
+      $.export("$summary", "No user found for the provided email");
+    }
     return response;
components/slack_v2/common/constants.mjs (1)

11-24: Inconsistent value mapping in CHANNEL_TYPE_OPTIONS.

The first option uses "Channels" as its value instead of a CHANNEL_TYPE constant. This breaks the pattern established by the other two options (which use CHANNEL_TYPE.MPIM and CHANNEL_TYPE.IM). While this appears intentional (based on usage in approve-workflow.mjs where "Channels" maps to both PUBLIC and PRIVATE channel types), it creates an undocumented special case that could confuse maintainers.

Consider either:

  1. Adding a comment explaining that "Channels" is a special value representing both PUBLIC and PRIVATE channels, or
  2. Refactoring to use a more explicit constant like CHANNEL_TYPE.PUBLIC_AND_PRIVATE or an array value for consistency.
components/slack_v2/actions/set-status/set-status.mjs (1)

37-48: Consider omitting optional properties when not provided.

Lines 41 and 42-43 use the && operator to conditionally set status_emoji and status_expiration. When these optional props are not provided, the properties will be set to falsy values (e.g., undefined, false, 0) rather than being omitted from the object. Some APIs may not handle falsy values gracefully.

Consider using the spread operator to omit properties entirely when not provided:

 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),
+    ...(this.statusEmoji && { status_emoji: `:${this.statusEmoji}:` }),
+    ...(this.statusExpiration && {
+      status_expiration: Math.floor(new Date(this.statusExpiration).getTime() / 1000),
+    }),
   },
 });
components/slack_v2/actions/list-group-members/list-group-members.mjs (1)

61-65: Optional: Remove unnecessary optional chaining.

Since members is initialized as an empty array on line 44, it can never be null or undefined, making the optional chaining on line 61 unnecessary.

-    if (members?.length) {
+    if (members.length) {
components/slack_v2/actions/update-message/update-message.mjs (1)

48-58: LGTM! Consider enhancing the summary with context.

The implementation correctly maps parameters and calls the Slack API. The generic success message is acceptable, though including the channel or timestamp could make debugging easier.

Optional enhancement:

-    $.export("$summary", "Successfully updated message");
+    $.export("$summary", `Successfully updated message in channel ${this.conversation}`);
components/slack_v2/sources/new-interaction-event-received/new-interaction-event-received.mjs (1)

24-34: Make channel multi-select via propDefinition options, not parallel type fields

Declare multi-select in the propDefinition to avoid schema drift between type: "string[]" and the underlying definition.

Apply:

-    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: [],
-    },
+    conversations: {
+      propDefinition: [
+        common.props.slack,
+        "conversation",
+        () => ({ multiple: true }),
+      ],
+      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,
+    },
components/slack_v2/actions/upload-file/upload-file.mjs (2)

52-76: Use metadata-derived filename consistently

Use metadata.name for both the presign request and the form field to avoid mismatches with path parsing.

Apply:

-    const filename = this.content.split("/").pop();
+    const filename = metadata.name;
...
-    formData.append("filename", filename);
+    formData.append("filename", filename);

98-104: Consistent error type + more informative summary

Use the same error style as presign and include filename/channel in the summary.

Apply:

-    if (!completeUploadResponse.ok) {
-      throw new Error(`Error completing upload: ${JSON.stringify(completeUploadResponse)}`);
-    }
+    if (!completeUploadResponse?.ok) {
+      throw new ConfigurationError(`Error completing upload: ${JSON.stringify(completeUploadResponse)}`);
+    }
-
-    $.export("$summary", "Successfully uploaded file");
-    return completeUploadResponse;
+    $.export("$summary", `Successfully uploaded ${metadata.name} to ${this.conversation}`);
+    return completeUploadResponse;
components/slack_v2/actions/send-block-kit-message/send-block-kit-message.mjs (1)

17-33: Consider removing redundant slack prop definition.

Line 18 redefines slack: common.props.slack, but line 31 already spreads ...common.props which includes the slack prop. While this doesn't cause an error (the spread overwrites the earlier definition), it creates unnecessary redundancy.

Apply this diff to remove the redundancy:

  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,
  },
components/slack_v2/actions/list-files/list-files.mjs (1)

80-85: Consider using a boolean for hasMore.

Line 83 assigns hasMore = files.length, making it a number rather than a boolean. While this works in the loop condition, it reduces code clarity. Consider using hasMore = files.length > 0 or hasMore = files.length === this.pageSize for clearer intent.

Apply this diff to improve clarity:

    do {
      const { files } = await this.slack.listFiles(params);
      allFiles.push(...files);
-     hasMore = files.length;
+     hasMore = files.length > 0;
      params.page++;
    } while (hasMore && params.page <= this.numPages);
components/slack_v2/sources/new-saved-message/new-saved-message.mjs (1)

25-30: Improve summary for observability

Consider including user and channel in the summary for quick triage, e.g., “ saved a message in ”.

-    getSummary() {
-      return "New saved message";
-    },
+    getSummary(event) {
+      const user = event?.user || "Someone";
+      const channel = event?.item?.channel || "a channel";
+      return `${user} saved a message in ${channel}`;
+    },
components/slack_v2/actions/update-group-members/update-group-members.mjs (1)

69-70: Skip no-op updates, and align summary variable

Avoid the API call when membership is unchanged; also use the local userGroup for consistency.

-    $.export("$summary", `Successfully updated members of group with ID ${this.userGroup}`);
-    return response;
+    if (response?.ok === false) return response;
+    $.export("$summary", `Updated members of group ${userGroup} (${users.length} users)`);
+    return response;
components/slack_v2/actions/find-message/find-message.mjs (1)

51-66: Guard against cursor loops and overfetch

Add a seen‑cursor set to avoid infinite loops if an API bug returns the same cursor; stop when reaching maxResults.

   async searchWithAssistant(baseParams, maxResults) {
     const matches = [];
-    let cursor;
+    let cursor;
+    const seen = new Set();
 
     do {
       const response = await this.slack.assistantSearch({
         ...baseParams,
         channel_types: "public_channel,private_channel",
         cursor,
       });
       matches.push(...response.results?.messages || []);
-      cursor = response.response_metadata?.next_cursor;
-    } while (cursor && matches.length < maxResults);
+      const next = response.response_metadata?.next_cursor;
+      if (!next || seen.has(next) || matches.length >= maxResults) break;
+      seen.add(next);
+      cursor = next;
+    } while (true);
 
     return matches.slice(0, maxResults);
   },
components/slack_v2/actions/list-users/list-users.mjs (1)

44-49: Defensive default for missing members

Guard against undefined members to avoid pushing undefined.

-      users.push(...members);
+      users.push(...(members ?? []));
components/slack_v2/actions/send-large-message/send-large-message.mjs (3)

15-36: Props spread order causes duplication and overrides

You define slack, conversation, text, mrkdwn then spread ...common.props, which overrides them. Either remove duplicates or spread first and override intentionally.

-  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,
-  },
+  props: {
+    ...common.props,
+    // override here only if you need different labels/descriptions/defaults:
+    // conversation: { ... },
+    // text: { ... },
+    // mrkdwn: { ... },
+  },

44-47: Optional: safer “sent via Pipedream” text extraction

Guard against unexpected block structure when appending the flag text.

-      const sentViaPipedreamText = this._makeSentViaPipedreamBlock();
-      this.text += `\n\n\n${sentViaPipedreamText.elements[0].text}`;
+      const block = this._makeSentViaPipedreamBlock();
+      const text = block?.elements?.[0]?.text;
+      if (text) this.text += `\n\n\n${text}`;

66-87: Consider adding message chunking or file upload fallback for optimal Slack delivery

Messages sent via this action can exceed Slack's recommended text length for best results. Slack recommends keeping top-level text to approximately 4,000 characters for readability, and messages longer than 40,000 characters are truncated with a warning returned. Consider chunking this.text into multiple messages or implementing a fallback to file upload when the message size exceeds the ~4,000 character threshold.

components/slack_v2/sources/new-message-in-channels/new-message-in-channels.mjs (1)

79-86: Tighten comparisons and parallelize name resolution.

Use strict equality and resolve names concurrently to reduce latency under load.

-      if ((this.ignoreBot) && (event.subtype == "bot_message" || event.bot_id)) {
+      if ((this.ignoreBot) && (event.subtype === "bot_message" || event.bot_id)) {
         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);
-        }
-      }
+      if (this.resolveNames) {
+        const promises = [];
+        if (event.user) {
+          event.user_id = event.user;
+          promises.push(this.getUserName(event.user).then((name) => { event.user = name; }));
+        } else if (event.bot_id) {
+          promises.push(this.getBotName(event.bot_id).then((name) => { event.bot = name; }));
+        }
+        event.channel_id = event.channel;
+        promises.push(this.getConversationName(event.channel).then((name) => { event.channel = name; }));
+        if (event.team) {
+          event.team_id = event.team;
+          promises.push(this.getTeamName(event.team).then((name) => { event.team = name; }));
+        }
+        await Promise.all(promises);
+      }

Also applies to: 88-101

components/slack_v2/actions/send-message-advanced/send-message-advanced.mjs (1)

18-19: Avoid duplicate slack prop; optionally expose raw blocks input.

  • slack is already provided via ...common.props; defining it again is redundant.
  • Expose the app’s blocks prop so users can paste raw Block Kit JSON when not using the builder.
   props: {
-    slack: common.props.slack,
+    // rely on ...common.props for `slack`
     conversation: {
       propDefinition: [
         common.props.slack,
         "conversation",
       ],
     },
@@
     link_names: {
       propDefinition: [
         common.props.slack,
         "link_names",
       ],
     },
+    blocks: {
+      propDefinition: [
+        common.props.slack,
+        "blocks",
+      ],
+    },
     ...common.props,
     ...buildBlocks.props,
   },

Also applies to: 56-58

components/slack_v2/actions/list-members-in-channel/list-members-in-channel.mjs (1)

49-56: Harden pagination and results (handle missing members, dedupe IDs).

Prevents runtime errors if members is undefined and avoids duplicate IDs across pages; minor efficiency win before username lookup.

   do {
     const {
-      members, response_metadata: { next_cursor: nextCursor },
+      members, response_metadata: { next_cursor: nextCursor },
     } = await this.slack.listChannelMembers(params);
-    channelMembers.push(...members);
+    channelMembers.push(...(members ?? []));
     params.cursor = nextCursor;
     page++;
   } while (params.cursor && page < this.numPages);

+  // Deduplicate before optional username lookup
+  channelMembers = [...new Set(channelMembers)];

Also applies to: 58-64

components/slack_v2/slack_v2.app.mjs (5)

628-651: Speed up ID lookups with a Set (avoid O(n²)).

Current ids.includes(user.id) scales poorly on large teams.

-  let cursor;
-  const userNames = {};
+  let cursor;
+  const userNames = {};
+  const target = new Set(ids);
   do {
@@
-    for (const user of users) {
-      if (ids.includes(user.id)) {
-        userNames[user.id] = user.name;
-      }
-    }
+    for (const user of users) {
+      if (target.has(user.id)) userNames[user.id] = user.name;
+    }

652-678: Same Set optimization for realNameLookup.

-  let cursor;
-  const realNames = {};
+  let cursor;
+  const realNames = {};
+  const idSet = new Set(ids);
+  const unameSet = new Set(usernames);
@@
-    for (const user of users) {
-      if (ids.includes(user.id)) {
-        realNames[user.id] = user.profile.real_name;
-      }
-      if (usernames.includes(user.name)) {
-        realNames[user.name] = user.profile.real_name;
-      }
-    }
+    for (const user of users) {
+      if (idSet.has(user.id)) realNames[user.id] = user.profile.real_name;
+      if (unameSet.has(user.name)) realNames[user.name] = user.profile.real_name;
+    }

476-487: Potential label bug: resource.user.name likely undefined.

For DMs, resource.user is usually a user ID string, not an object. This returns Direct Messaging with: @undefined.

 getChannelLabel(resource) {
-  if (resource.user) {
-    return `Direct Messaging with: @${resource.user.name}`;
-  }
+  if (resource.user) return `Direct Messaging with: @${resource.user}`;
   const {
     is_private: isPrivate,
     name,
   } = resource.channel;
   return `${isPrivate && "Private" || "Public"} channel #${name}`;
 }

502-511: Simplify display name fallback for DMs.

Avoid redundant profile.real_name || profile?.real_name and add display_name fallback.

-  return `@${profile.real_name || profile?.real_name}`;
+  return `@${profile?.real_name || profile?.display_name || "user"}`;

684-705: Optionally try conversations.join before inviting the bot.

For public channels, joining is simpler and avoids invite failures; fall back to invite on error.

-        for (const channel of channelIds) {
+        for (const channel of channelIds) {
           try {
-            // Note: Trying to add the app to DM or group DM channels results in
-            // the error: method_not_supported_for_channel_type
-            await this.inviteToConversation({
-              channel,
-              users: user_id,
-            });
+            // Attempt join (public channels). If it fails, fall back to invite.
+            await this.sdk({ asBot: true }).conversations.join({ channel });
+          } catch (errJoin) {
+            try {
+              await this.inviteToConversation({ channel, users: user_id });
+            } catch (error) {
+              console.log(`Unable to add app to channel ${channel}: ${error}`);
+            }
           } catch (error) {
             console.log(`Unable to add app to channel ${channel}: ${error}`);
           }
         }
components/slack_v2/actions/common/send-message.mjs (2)

205-206: Validate blocks JSON and surface a clear error.

-    } else if (typeof blocks === "string") {
-      blocks = JSON.parse(blocks);
+    } else if (typeof blocks === "string") {
+      try {
+        blocks = JSON.parse(blocks);
+      } catch (e) {
+        throw new Error(`Invalid JSON in "Blocks": ${e.message}`);
+      }
     }

250-253: Validate post_at (ISO-8601, not in the past) before scheduling.

   if (this.post_at) {
-    obj.post_at = Math.floor(new Date(this.post_at).getTime() / 1000);
+    const ts = Date.parse(this.post_at);
+    if (Number.isNaN(ts)) {
+      throw new Error('Invalid "Schedule message" datetime. Use ISO 8601 (e.g., 2014-01-01T00:00:00Z).');
+    }
+    if (ts < Date.now()) {
+      throw new Error('"Schedule message" time cannot be in the past.');
+    }
+    obj.post_at = Math.floor(ts / 1000);
     return await this.slack.scheduleMessage(obj);
   }
components/slack_v2/actions/common/build-blocks.mjs (3)

160-164: Defensive: ensure this.blocks is an array in “array” mode.

Prevents runtime issues if upstream passes a non‑array.

-    if (this.passArrayOrConfigure === "array") {
-      blocks = this.blocks;
+    if (this.passArrayOrConfigure === "array") {
+      blocks = Array.isArray(this.blocks) ? this.blocks : [];
     } else {

73-75: Avoid deprecated substr; prefer slice (or crypto.randomUUID() for stronger IDs).

Small DX fix; optional stronger ID generation shown below.

-          action_id: `actionId-${Math.random().toString(36)
-            .substr(2, 9)}`,  // Generates a random action_id
+          action_id: `actionId-${Math.random().toString(36).slice(2, 11)}`,  // Generates a random action_id

Optional stronger IDs:

// add at top of file:
import { randomUUID } from "crypto";
// and here:
action_id: `actionId-${randomUUID()}`,

39-82: Handle unknown block types explicitly.

Fail fast if an unsupported type is passed.

       } else if (type === "link_button") {
@@
         return {
           type: "actions",
           elements: buttons,
         };
       }
+      throw new Error(`Unsupported block type: ${type}`);
components/slack_v2/sources/common/base.mjs (2)

1-1: Use stable ID generation; avoid deprecated substr.

Prefer crypto.randomUUID() (Node 16+) for uniqueness and clarity; fallback to slice if needed.

+import { randomUUID } from "crypto";
@@
-        event.pipedream_msg_id = `pd_${Date.now()}_${Math.random()
-          .toString(36)
-          .substr(2, 10)}`;
+        event.pipedream_msg_id = `pd_${Date.now()}_${randomUUID()}`;

If crypto isn’t available, minimally:

-          .substr(2, 10)}`;
+          .slice(2, 12)}`;

Also applies to: 172-175


177-181: Harden $emit id fallback chain.

If event.channel is a string, .id is undefined. Use safe fallbacks.

-      this.$emit(event, {
-        id: event.client_msg_id || event.pipedream_msg_id || event.channel.id,
+      this.$emit(event, {
+        id: event.client_msg_id
+          || event.pipedream_msg_id
+          || (event.channel && (event.channel.id || event.channel))
+          || event.user
+          || String(event.event_ts || Date.now()),
         summary: this.getSummary(event),
         ts: event.event_ts || Date.now(),
       });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2df5de6 and f417859.

📒 Files selected for processing (57)
  • components/slack_v2/actions/add-emoji-reaction/add-emoji-reaction.mjs (1 hunks)
  • components/slack_v2/actions/approve-workflow/approve-workflow.mjs (1 hunks)
  • components/slack_v2/actions/archive-channel/archive-channel.mjs (1 hunks)
  • components/slack_v2/actions/common/build-blocks.mjs (1 hunks)
  • components/slack_v2/actions/common/send-message.mjs (1 hunks)
  • components/slack_v2/actions/create-channel/create-channel.mjs (1 hunks)
  • components/slack_v2/actions/create-reminder/create-reminder.mjs (1 hunks)
  • components/slack_v2/actions/delete-file/delete-file.mjs (1 hunks)
  • components/slack_v2/actions/delete-message/delete-message.mjs (1 hunks)
  • components/slack_v2/actions/find-message/find-message.mjs (1 hunks)
  • components/slack_v2/actions/find-user-by-email/find-user-by-email.mjs (1 hunks)
  • components/slack_v2/actions/get-current-user/get-current-user.mjs (1 hunks)
  • components/slack_v2/actions/get-file/get-file.mjs (1 hunks)
  • components/slack_v2/actions/invite-user-to-channel/invite-user-to-channel.mjs (1 hunks)
  • components/slack_v2/actions/kick-user/kick-user.mjs (1 hunks)
  • components/slack_v2/actions/list-channels/list-channels.mjs (1 hunks)
  • components/slack_v2/actions/list-files/list-files.mjs (1 hunks)
  • components/slack_v2/actions/list-group-members/list-group-members.mjs (1 hunks)
  • components/slack_v2/actions/list-members-in-channel/list-members-in-channel.mjs (1 hunks)
  • components/slack_v2/actions/list-replies/list-replies.mjs (1 hunks)
  • components/slack_v2/actions/list-users/list-users.mjs (1 hunks)
  • components/slack_v2/actions/reply-to-a-message/reply-to-a-message.mjs (1 hunks)
  • components/slack_v2/actions/send-block-kit-message/send-block-kit-message.mjs (1 hunks)
  • components/slack_v2/actions/send-large-message/send-large-message.mjs (1 hunks)
  • components/slack_v2/actions/send-message-advanced/send-message-advanced.mjs (1 hunks)
  • components/slack_v2/actions/send-message-to-channel/send-message-to-channel.mjs (1 hunks)
  • components/slack_v2/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs (1 hunks)
  • components/slack_v2/actions/send-message/send-message.mjs (1 hunks)
  • components/slack_v2/actions/set-channel-description/set-channel-description.mjs (1 hunks)
  • components/slack_v2/actions/set-channel-topic/set-channel-topic.mjs (1 hunks)
  • components/slack_v2/actions/set-status/set-status.mjs (1 hunks)
  • components/slack_v2/actions/update-group-members/update-group-members.mjs (1 hunks)
  • components/slack_v2/actions/update-message/update-message.mjs (1 hunks)
  • components/slack_v2/actions/update-profile/update-profile.mjs (1 hunks)
  • components/slack_v2/actions/upload-file/upload-file.mjs (1 hunks)
  • components/slack_v2/common/constants.mjs (1 hunks)
  • components/slack_v2/package.json (1 hunks)
  • components/slack_v2/slack_v2.app.mjs (1 hunks)
  • components/slack_v2/sources/common/base.mjs (1 hunks)
  • components/slack_v2/sources/common/constants.mjs (1 hunks)
  • components/slack_v2/sources/new-channel-created/new-channel-created.mjs (1 hunks)
  • components/slack_v2/sources/new-channel-created/test-event.mjs (1 hunks)
  • components/slack_v2/sources/new-interaction-event-received/README.md (1 hunks)
  • components/slack_v2/sources/new-interaction-event-received/new-interaction-event-received.mjs (1 hunks)
  • components/slack_v2/sources/new-interaction-event-received/test-event.mjs (1 hunks)
  • components/slack_v2/sources/new-keyword-mention/new-keyword-mention.mjs (1 hunks)
  • components/slack_v2/sources/new-keyword-mention/test-event.mjs (1 hunks)
  • components/slack_v2/sources/new-message-in-channels/new-message-in-channels.mjs (1 hunks)
  • components/slack_v2/sources/new-message-in-channels/test-event.mjs (1 hunks)
  • components/slack_v2/sources/new-reaction-added/new-reaction-added.mjs (1 hunks)
  • components/slack_v2/sources/new-reaction-added/test-event.mjs (1 hunks)
  • components/slack_v2/sources/new-saved-message/new-saved-message.mjs (1 hunks)
  • components/slack_v2/sources/new-saved-message/test-event.mjs (1 hunks)
  • components/slack_v2/sources/new-user-added/new-user-added.mjs (1 hunks)
  • components/slack_v2/sources/new-user-added/test-event.mjs (1 hunks)
  • components/slack_v2/sources/new-user-mention/new-user-mention.mjs (1 hunks)
  • components/slack_v2/sources/new-user-mention/test-event.mjs (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • components/slack_v2/sources/new-keyword-mention/test-event.mjs
  • components/slack_v2/sources/new-user-mention/test-event.mjs
🧰 Additional context used
🧬 Code graph analysis (26)
components/slack_v2/actions/list-channels/list-channels.mjs (4)
components/slack_v2/actions/list-group-members/list-group-members.mjs (3)
  • params (45-49)
  • page (50-50)
  • nextCursor (53-55)
components/slack_v2/actions/list-members-in-channel/list-members-in-channel.mjs (3)
  • params (43-46)
  • page (47-47)
  • nextCursor (50-52)
components/slack_v2/actions/list-replies/list-replies.mjs (3)
  • params (51-55)
  • page (56-56)
  • nextCursor (59-61)
components/slack_v2/actions/list-users/list-users.mjs (3)
  • params (38-41)
  • page (42-42)
  • nextCursor (45-47)
components/slack_v2/actions/set-channel-description/set-channel-description.mjs (3)
components/slack_v2/actions/archive-channel/archive-channel.mjs (1)
  • response (32-34)
components/slack_v2/actions/create-channel/create-channel.mjs (1)
  • response (33-36)
components/slack_v2/actions/set-channel-topic/set-channel-topic.mjs (1)
  • response (30-33)
components/slack_v2/actions/list-files/list-files.mjs (5)
components/slack_v2/actions/list-channels/list-channels.mjs (1)
  • params (31-33)
components/slack_v2/actions/list-group-members/list-group-members.mjs (1)
  • params (45-49)
components/slack_v2/actions/list-members-in-channel/list-members-in-channel.mjs (1)
  • params (43-46)
components/slack_v2/actions/list-replies/list-replies.mjs (1)
  • params (51-55)
components/slack_v2/actions/list-users/list-users.mjs (1)
  • params (38-41)
components/slack_v2/actions/add-emoji-reaction/add-emoji-reaction.mjs (1)
components/slack_v2/actions/delete-message/delete-message.mjs (1)
  • response (37-41)
components/slack_v2/actions/list-replies/list-replies.mjs (4)
components/slack_v2/actions/list-channels/list-channels.mjs (3)
  • params (31-33)
  • page (34-34)
  • nextCursor (37-39)
components/slack_v2/actions/list-group-members/list-group-members.mjs (3)
  • params (45-49)
  • page (50-50)
  • nextCursor (53-55)
components/slack_v2/actions/list-members-in-channel/list-members-in-channel.mjs (3)
  • params (43-46)
  • page (47-47)
  • nextCursor (50-52)
components/slack_v2/actions/list-users/list-users.mjs (3)
  • params (38-41)
  • page (42-42)
  • nextCursor (45-47)
components/slack_v2/sources/new-user-mention/new-user-mention.mjs (1)
components/slack_v2/sources/new-keyword-mention/new-keyword-mention.mjs (2)
  • msgType (61-66)
  • emitEvent (86-86)
components/slack_v2/actions/kick-user/kick-user.mjs (2)
components/slack_v2/actions/archive-channel/archive-channel.mjs (1)
  • response (32-34)
components/slack_v2/actions/invite-user-to-channel/invite-user-to-channel.mjs (1)
  • response (31-34)
components/slack_v2/actions/list-users/list-users.mjs (2)
components/slack_v2/slack_v2.app.mjs (4)
  • users (517-519)
  • users (632-640)
  • users (656-664)
  • page (293-298)
components/slack_v2/actions/list-channels/list-channels.mjs (3)
  • params (31-33)
  • page (34-34)
  • nextCursor (37-39)
components/slack_v2/actions/delete-message/delete-message.mjs (1)
components/slack_v2/actions/add-emoji-reaction/add-emoji-reaction.mjs (1)
  • response (40-44)
components/slack_v2/actions/send-block-kit-message/send-block-kit-message.mjs (1)
components/slack_v2/actions/common/send-message.mjs (2)
  • resp (254-254)
  • resp (255-257)
components/slack_v2/actions/set-channel-topic/set-channel-topic.mjs (1)
components/slack_v2/actions/set-channel-description/set-channel-description.mjs (1)
  • response (30-33)
components/slack_v2/slack_v2.app.mjs (13)
components/slack_v2/actions/common/send-message.mjs (3)
  • channelId (190-190)
  • resp (254-254)
  • resp (255-257)
components/slack_v2/actions/list-group-members/list-group-members.mjs (3)
  • members (44-44)
  • page (50-50)
  • nextCursor (53-55)
components/slack_v2/actions/list-members-in-channel/list-members-in-channel.mjs (3)
  • usernames (59-59)
  • page (47-47)
  • nextCursor (50-52)
components/slack_v2/actions/list-channels/list-channels.mjs (2)
  • page (34-34)
  • nextCursor (37-39)
components/slack_v2/actions/list-replies/list-replies.mjs (2)
  • page (56-56)
  • nextCursor (59-61)
components/slack_v2/actions/list-users/list-users.mjs (3)
  • page (42-42)
  • users (37-37)
  • nextCursor (45-47)
components/slack_v2/actions/add-emoji-reaction/add-emoji-reaction.mjs (1)
  • response (40-44)
components/slack_v2/actions/delete-file/delete-file.mjs (1)
  • response (33-35)
components/slack_v2/actions/delete-message/delete-message.mjs (1)
  • response (37-41)
components/slack_v2/actions/get-file/get-file.mjs (1)
  • response (53-55)
components/slack_v2/actions/invite-user-to-channel/invite-user-to-channel.mjs (1)
  • response (31-34)
components/slack_v2/actions/kick-user/kick-user.mjs (1)
  • response (42-45)
components/slack_v2/actions/send-large-message/send-large-message.mjs (2)
  • response (81-81)
  • response (88-90)
components/slack_v2/sources/new-channel-created/new-channel-created.mjs (1)
components/slack_v2/actions/create-channel/create-channel.mjs (1)
  • name (31-31)
components/slack_v2/actions/list-group-members/list-group-members.mjs (4)
components/slack_v2/actions/list-channels/list-channels.mjs (3)
  • params (31-33)
  • page (34-34)
  • nextCursor (37-39)
components/slack_v2/actions/list-members-in-channel/list-members-in-channel.mjs (3)
  • params (43-46)
  • page (47-47)
  • nextCursor (50-52)
components/slack_v2/actions/list-users/list-users.mjs (4)
  • params (38-41)
  • page (42-42)
  • nextCursor (45-47)
  • users (37-37)
components/slack_v2/slack_v2.app.mjs (4)
  • page (293-298)
  • users (517-519)
  • users (632-640)
  • users (656-664)
components/slack_v2/actions/invite-user-to-channel/invite-user-to-channel.mjs (1)
components/slack_v2/actions/kick-user/kick-user.mjs (1)
  • response (42-45)
components/slack_v2/actions/delete-file/delete-file.mjs (1)
components/slack_v2/actions/get-file/get-file.mjs (1)
  • response (53-55)
components/slack_v2/actions/list-members-in-channel/list-members-in-channel.mjs (4)
components/slack_v2/actions/list-channels/list-channels.mjs (3)
  • params (31-33)
  • page (34-34)
  • nextCursor (37-39)
components/slack_v2/actions/list-group-members/list-group-members.mjs (4)
  • params (45-49)
  • page (50-50)
  • nextCursor (53-55)
  • members (44-44)
components/slack_v2/actions/list-replies/list-replies.mjs (3)
  • params (51-55)
  • page (56-56)
  • nextCursor (59-61)
components/slack_v2/actions/list-users/list-users.mjs (3)
  • params (38-41)
  • page (42-42)
  • nextCursor (45-47)
components/slack_v2/actions/common/send-message.mjs (4)
components/slack_v2/actions/common/build-blocks.mjs (2)
  • props (86-86)
  • blocks (161-161)
components/slack_v2/actions/send-large-message/send-large-message.mjs (4)
  • sentViaPipedreamText (45-45)
  • metadataEventPayload (49-49)
  • obj (66-79)
  • channelName (91-91)
components/slack_v2/actions/send-block-kit-message/send-block-kit-message.mjs (1)
  • resp (43-45)
components/slack_v2/actions/send-message-advanced/send-message-advanced.mjs (1)
  • resp (70-72)
components/slack_v2/actions/common/build-blocks.mjs (2)
components/slack_v2/sources/new-user-mention/new-user-mention.mjs (1)
  • blocks (75-81)
components/slack_v2/actions/common/send-message.mjs (1)
  • blocks (198-198)
components/slack_v2/sources/common/base.mjs (1)
components/slack_v2/sources/common/constants.mjs (4)
  • NAME_CACHE_TIMEOUT (50-50)
  • NAME_CACHE_TIMEOUT (50-50)
  • NAME_CACHE_MAX_SIZE (49-49)
  • NAME_CACHE_MAX_SIZE (49-49)
components/slack_v2/actions/send-message-advanced/send-message-advanced.mjs (2)
components/slack_v2/actions/send-block-kit-message/send-block-kit-message.mjs (1)
  • resp (43-45)
components/slack_v2/actions/common/send-message.mjs (2)
  • resp (254-254)
  • resp (255-257)
components/slack_v2/actions/send-large-message/send-large-message.mjs (1)
components/slack_v2/actions/common/send-message.mjs (4)
  • sentViaPipedreamText (209-209)
  • metadataEventPayload (213-213)
  • obj (231-248)
  • channelName (258-258)
components/slack_v2/actions/find-message/find-message.mjs (1)
components/slack_v2/slack_v2.app.mjs (4)
  • cursor (187-196)
  • cursor (240-247)
  • cursor (629-629)
  • cursor (653-653)
components/slack_v2/actions/update-message/update-message.mjs (2)
components/slack_v2/actions/add-emoji-reaction/add-emoji-reaction.mjs (1)
  • response (40-44)
components/slack_v2/actions/delete-message/delete-message.mjs (1)
  • response (37-41)
components/slack_v2/actions/get-file/get-file.mjs (1)
components/slack_v2/actions/delete-file/delete-file.mjs (1)
  • response (33-35)
components/slack_v2/sources/new-keyword-mention/new-keyword-mention.mjs (1)
components/slack_v2/sources/new-user-mention/new-user-mention.mjs (2)
  • msgType (68-74)
  • emitEvent (101-101)
🪛 Gitleaks (8.28.0)
components/slack_v2/sources/new-interaction-event-received/test-event.mjs

[high] 11-11: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🪛 LanguageTool
components/slack_v2/sources/new-interaction-event-received/README.md

[style] ~11-~11: Consider using just “reply”.
Context: ...k with other APIs or services, and then reply back to the original message. # Getting Sta...

(RETURN_BACK)


[style] ~85-~85: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...hat this source is configured with. 1. Make sure that the message was sent via Pipe...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Lint Code Base
  • GitHub Check: pnpm publish
  • GitHub Check: Verify TypeScript components
🔇 Additional comments (24)
components/slack_v2/sources/new-user-added/test-event.mjs (1)

1-48: Fixture looks sanitized and complete.

No functional issues found. Safe defaults (empty strings) avoid PII exposure.

components/slack_v2/sources/new-reaction-added/new-reaction-added.mjs (1)

15-23: Confirm conversation prop supports multiple selection.

You set type: "string[]" but reuse a single conversation propDefinition. Ensure the underlying definition returns an array and UI supports multi-select; otherwise switch to a multi-select propDefinition or wrap with options() to enable multiple values.

components/slack_v2/actions/create-channel/create-channel.mjs (1)

29-39: LGTM! Clean channel name normalization and creation flow.

The channel name normalization (replacing whitespace with hyphens and lowercasing) correctly aligns with Slack's channel naming requirements. The API call structure and success summary are appropriate.

components/slack_v2/actions/approve-workflow/approve-workflow.mjs (1)

47-91: LGTM! Well-structured workflow approval with proper flow suspension.

The implementation correctly:

  • Suspends the flow to obtain resume/cancel URLs
  • Posts a formatted Slack message with interactive buttons
  • Links the buttons to the appropriate flow control endpoints
  • Handles the special "Channels" value to map to both PUBLIC and PRIVATE channel types
components/slack_v2/actions/set-channel-description/set-channel-description.mjs (1)

29-36: LGTM!

The implementation correctly maps the UI concept of "channel description" to Slack's API terminology of "purpose". The parameter mapping and API call are consistent with the Slack documentation.

components/slack_v2/sources/new-channel-created/test-event.mjs (1)

1-44: LGTM!

The test fixture provides a comprehensive static event payload that matches the Slack channel_created event structure, including all relevant channel properties and metadata.

components/slack_v2/sources/new-saved-message/test-event.mjs (1)

1-37: LGTM!

The test fixture correctly represents a Slack star_added event with appropriate nested structure for the item and message properties, including the blocks array for rich text formatting.

components/slack_v2/actions/list-replies/list-replies.mjs (1)

58-65: The original code is correct. No changes needed.

The Slack API conversations.replies endpoint returns response_metadata containing next_cursor as a standard part of the response structure for cursor-based pagination. The destructuring on line 60 is safe because the Slack API guarantees this field exists.

The pattern used in list-replies is consistent across all list actions in the codebase (list-channels, list-group-members, list-members-in-channel, list-users), and none include defensive handling. This systematic consistency reflects the proper reliance on the Slack API contract.

Likely an incorrect or invalid review comment.

components/slack_v2/actions/list-group-members/list-group-members.mjs (1)

52-59: Verify response_metadata structure for usergroups.users.list API call.

The web search results are conflicting on whether usergroups.users.list includes response_metadata in its response. One source indicates the response contains only {"ok": true, "users": [...]} without response_metadata, which would cause a destructuring error on line 54. However, pagination documentation references response_metadata.next_cursor for this endpoint.

The pattern is consistent across all list actions in the codebase, suggesting it may work in practice, but the API documentation ambiguity warrants verification—especially since find-message.mjs uses defensive optional chaining for response_metadata, indicating some endpoints handle this differently.

Recommended actions:

  • Test this action against the actual Slack API to confirm the response structure
  • If response_metadata is missing, add defensive handling: response_metadata: { next_cursor: nextCursor } = {}
  • Check Slack API changelog or test against a real workspace to resolve documentation ambiguity
components/slack_v2/actions/get-current-user/get-current-user.mjs (1)

25-31: ****

The usersInfo method does exist in the v2 wrapper (defined at line 865 of slack_v2.app.mjs). The review comment's assumption that it may not exist is unfounded. The codebase shows that while some methods follow a getXxx pattern (e.g., getUserProfile, getTeamInfo), others like usersInfo and authTest use different naming conventions—all valid in the v2 wrapper.

Likely an incorrect or invalid review comment.

components/slack_v2/actions/send-message-to-channel/send-message-to-channel.mjs (1)

16-44: Review comment is incorrect — props merge order concern is unfounded

The common.props object does not contain conversation, text, or mrkdwn properties. It only exports: slack, as_user, addToChannel, post_at, include_sent_via_pipedream_flag, customizeBotSettings, username, icon_emoji, icon_url, replyToThread, thread_ts, thread_broadcast, addMessageMetadata, metadata_event_type, metadata_event_payload, configureUnfurlSettings, unfurl_links, unfurl_media.

Since these keys don't exist in common.props, spreading ...common.props at the end cannot override the custom conversation, text, or mrkdwn props with their PUBLIC/PRIVATE channel restrictions. The current code structure is correct and doesn't require refactoring.

Likely an incorrect or invalid review comment.

components/slack_v2/actions/reply-to-a-message/reply-to-a-message.mjs (1)

16-53: Prop name is correct; no changes needed.

The thread_broadcast prop is correct. In send-message.mjs, it's intentionally mapped to the Slack API field reply_broadcast in the run method (reply_broadcast: this.thread_broadcast). The review comment's concern about prop naming is factually incorrect.

Regarding props override order: While moving ...common.props to the beginning would be more conventional, the current structure works correctly because explicit props after the spread override the spread defaults via JavaScript object semantics. This is a style suggestion only, not a critical issue.

Likely an incorrect or invalid review comment.

components/slack_v2/actions/set-channel-topic/set-channel-topic.mjs (1)

1-37: LGTM!

The action follows the standard Slack v2 pattern with proper prop definitions and a clean implementation. The API call and summary export are correctly structured.

components/slack_v2/sources/new-channel-created/new-channel-created.mjs (1)

1-32: LGTM!

The event source follows the standard Slack v2 pattern with proper extension of the common base, correct event name configuration, and a clear summary method.

components/slack_v2/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs (1)

1-85: LGTM!

The action properly handles both conversation selection and user list scenarios with appropriate validation. The ConfigurationError for missing inputs and the openConversation flow are well-implemented.

components/slack_v2/actions/list-channels/list-channels.mjs (1)

29-51: LGTM!

The pagination logic correctly accumulates channels using cursor-based pagination and respects the page limit. The implementation is consistent with other list actions in the codebase.

components/slack_v2/actions/send-message/send-message.mjs (1)

1-56: LGTM!

The action properly implements dynamic channel type filtering based on user selection. The conditional logic for constraining conversation types is clean and correct.

components/slack_v2/actions/find-message/find-message.mjs (1)

57-60: Verify sort_dir parameter support and consider exposing channel types

Based on Slack's documentation, the assistant.search.context API supports channel_types as a comma-separated list of public_channel, private_channel, mpim, and im. However, your code hardcodes only "public_channel,private_channel", which excludes direct messages and multi-party IMs that the API supports.

More critically, sort_dir is documented for Slack's classic search APIs but not explicitly documented for assistant.search.context. Your code passes sort_dir: this.sortDirection to the endpoint—verify that this parameter is actually supported by the assistant.search.context endpoint, as it may be silently ignored or cause unexpected behavior.

Recommendations:

  • Expose channelTypes as a configurable prop (defaulting to the current value to preserve behavior).
  • Test or confirm with Slack that sort_dir is supported by assistant.search.context before relying on it in production.
components/slack_v2/actions/get-file/get-file.mjs (1)

46-58: LGTM — action is minimal and correct.

Good use of maybeAddAppToChannels and clear summary. No blocking issues.

components/slack_v2/actions/delete-message/delete-message.mjs (1)

28-34: Fix description text only; as_user parameter is valid and supported by Slack API.

The as_user parameter is supported by Slack's chat.delete method (as of October 20, 2025). It's an optional boolean that, when true, deletes the message as the authed user. The only issue is the description text incorrectly says "update" instead of "delete".

Apply this fix:

    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.",
+      description: "Pass true to delete the message as the authed user. Bot users in this context are considered authed users.",
    },

Also applies to: 37-41

components/slack_v2/sources/new-user-mention/new-user-mention.mjs (1)

33-41: No actionable issues found. The code is correct.

The conversations prop contains an array of channel IDs (confirmed by the prop definition type "string[]" and description "Select one or more channels to monitor for new messages"). The eventNames() method correctly returns either:

  • The selected channel IDs when channels are chosen, or
  • ["message"] as the fallback event type when no channels are selected

Both are valid values per the Slack apphook contract. This pattern is consistently applied across related sources (new-message-in-channels, new-keyword-mention).

components/slack_v2/actions/archive-channel/archive-channel.mjs (1)

21-27: Review comment is incorrect — ignore this suggestion.

Slack's conversations.archive API does support archiving IM and MPIM conversations when those conversation types are supported and the token has corresponding scopes (im:write and mpim:write). The current code allowing MPIM is correct and will not error at runtime with appropriate token permissions. Do not apply the suggested diff.

Likely an incorrect or invalid review comment.

components/slack_v2/sources/common/base.mjs (1)

1-4: All required Slack app client methods are present.

Verification confirms that usersInfo, conversationsInfo, getConversationReplies, getBotInfo, and getTeamInfo all exist in the slack_v2.app.mjs wrapper. No integration breakage risk from missing methods.

components/slack_v2/actions/common/build-blocks.mjs (1)

99-102: Path verification successful; brittleness concern remains valid.

The path common.props.slack.propDefinitions.blocks exists and exposes the three accessed properties:

  • type"string"
  • label"Blocks"
  • description → populated string

However, the underlying concern about brittleness stands: deep nested property access is inherently fragile. If the structure of slack_v2.app.mjs or send-message.mjs changes during refactoring, these references could break without warning. Consider extracting this to a named constant or utility function to isolate the dependency path.

Comment on lines +65 to +76
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
}));

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Validate link_button input shape and guard keys.

Object.keys(text) will throw if text isn’t a plain object; also clarify error early.

-      } else if (type === "link_button") {
-        const buttons = Object.keys(text).map((buttonText) => ({
+      } else if (type === "link_button") {
+        if (!text || typeof text !== "object" || Array.isArray(text)) {
+          throw new Error("Link Button expects an object mapping button text → URL");
+        }
+        const buttons = Object.keys(text).map((buttonText) => ({
           type: "button",
@@
-          action_id: `actionId-${Math.random().toString(36)
-            .substr(2, 9)}`,  // Generates a random action_id
+          action_id: `actionId-${Math.random().toString(36).slice(2, 11)}`,  // Generates a random action_id
         }));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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
}));
if (!text || typeof text !== "object" || Array.isArray(text)) {
throw new Error("Link Button expects an object mapping button text → URL");
}
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).slice(2, 11)}`, // Generates a random action_id
}));
🤖 Prompt for AI Agents
In components/slack_v2/actions/common/build-blocks.mjs around lines 65 to 76,
the code uses Object.keys(text) without validating that text is a non-null plain
object which will throw for non-objects; update to first guard the input (if
typeof text !== "object" || text === null || Array.isArray(text) then throw a
clear TypeError or return an empty array), then iterate using
Object.entries(text) and filter to only own string key/value pairs (e.g. filter
([k,v]) => typeof k === "string" && typeof v === "string") before mapping to
buttons, and keep generating action_id safely (use slice instead of substr if
desired).

Comment on lines +42 to +51
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;
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add defensive null check before accessing nested property.

The summary export directly accesses response.reminder.id without verifying that response or response.reminder exists. If the Slack API returns an unexpected structure or fails gracefully, this will throw a TypeError.

Consider adding a check or optional chaining:

-    $.export("$summary", `Successfully created reminder with ID ${response.reminder.id}`);
+    $.export("$summary", `Successfully created reminder${response?.reminder?.id ? ` with ID ${response.reminder.id}` : ""}`);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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;
},
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${response?.reminder?.id ? ` with ID ${response.reminder.id}` : ""}`);
return response;
},
🤖 Prompt for AI Agents
In components/slack_v2/actions/create-reminder/create-reminder.mjs around lines
42 to 51, the code directly accesses response.reminder.id which can throw if
response or response.reminder is null/undefined; add a defensive null check or
use optional chaining to safely read the id (e.g., const id =
response?.reminder?.id) and export a safe summary using a fallback message when
id is missing, ensuring the function still returns the original response.

Comment on lines +33 to +38
const response = await this.slack.deleteFiles({
file: this.file,
});
$.export("$summary", `Successfully deleted file with ID ${this.file}`);
return response;
},
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Add response.ok guard and confirm API method name

Check the Slack API response and fail fast on errors. Also confirm deleteFiles matches the v2 wrapper.

Apply:

   const response = await this.slack.deleteFiles({
     file: this.file,
   });
-  $.export("$summary", `Successfully deleted file with ID ${this.file}`);
-  return response;
+  if (!response?.ok) {
+    throw new Error(`files.delete failed for ${this.file}: ${JSON.stringify(response)}`);
+  }
+  $.export("$summary", `Successfully deleted file with ID ${this.file}`);
+  return response;

Verify method presence:


🏁 Script executed:

#!/bin/bash
fd -a 'slack_v2.app.mjs' components | xargs -I{} rg -n 'deleteFiles|files\.delete' {}

Length of output: 70


🏁 Script executed:

#!/bin/bash
# Check how other methods/actions in the codebase handle response.ok or error handling
rg -n 'response\.ok|throw new Error' components/slack_v2/actions --type=mjs -C 2 | head -100

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Look at the complete delete-file.mjs file to understand current structure
cat components/slack_v2/actions/delete-file/delete-file.mjs

Length of output: 837


🏁 Script executed:

#!/bin/bash
# Check the slack_v2 wrapper to see how the deleteFiles method implementation handles responses
rg -A 10 'deleteFiles\(args' components/slack_v2/slack_v2.app.mjs

Length of output: 247


🏁 Script executed:

#!/bin/bash
# Find makeRequest implementation
rg -A 15 'makeRequest\(' components/slack_v2/slack_v2.app.mjs | head -50

Length of output: 1497


🏁 Script executed:

#!/bin/bash
# Check how other destructive actions (create, delete, update) handle responses
find components/slack_v2/actions -name "*.mjs" | xargs grep -l "response\.ok\|throw new Error" | head -10

Length of output: 313


🏁 Script executed:

#!/bin/bash
# Look at a similar destructive action to see pattern
find components/slack_v2/actions -name "*delete*" -o -name "*remove*" | head -5 | xargs cat

Length of output: 2021


🏁 Script executed:

#!/bin/bash
# Check response.ok pattern in upload-file action (similar destructive operation)
cat components/slack_v2/actions/upload-file/upload-file.mjs

Length of output: 2575


🏁 Script executed:

#!/bin/bash
# Check response.ok pattern in send-message action
cat components/slack_v2/actions/common/send-message.mjs

Length of output: 6738


Add response.ok guard and verify API method exists

The deleteFiles method correctly maps to Slack's files.delete API and is properly defined in the v2 wrapper. However, the action should verify the API response before exporting the summary. This aligns with error-handling patterns used in similar destructive operations like upload-file.mjs.

Apply:

   const response = await this.slack.deleteFiles({
     file: this.file,
   });
-  $.export("$summary", `Successfully deleted file with ID ${this.file}`);
-  return response;
+  if (!response?.ok) {
+    throw new Error(`files.delete failed for ${this.file}: ${JSON.stringify(response)}`);
+  }
+  $.export("$summary", `Successfully deleted file with ID ${this.file}`);
+  return response;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const response = await this.slack.deleteFiles({
file: this.file,
});
$.export("$summary", `Successfully deleted file with ID ${this.file}`);
return response;
},
const response = await this.slack.deleteFiles({
file: this.file,
});
if (!response?.ok) {
throw new Error(`files.delete failed for ${this.file}: ${JSON.stringify(response)}`);
}
$.export("$summary", `Successfully deleted file with ID ${this.file}`);
return response;
},
🤖 Prompt for AI Agents
In components/slack_v2/actions/delete-file/delete-file.mjs around lines 33 to
38, the action currently calls this.slack.deleteFiles and unconditionally
exports a success summary; add a guard to (1) verify the API method exists
(throw a clear error if typeof this.slack.deleteFiles !== 'function') before
calling, (2) after awaiting the call check response.ok and if false throw an
Error including response.error or the full response for debugging, and (3) only
export the success summary and return the response when response.ok is true.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.slack.deleteFiles throws an error if the API response is not ok.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@js07, thank you for the clarification! I understand that the this.slack.deleteFiles method (and other wrapper methods in the slack_v2 app) already handles error responses internally by throwing an error when the API response is not ok. The suggested guard would indeed be redundant in this case.


✏️ Learnings added
Learnt from: js07
PR: PipedreamHQ/pipedream#18744
File: components/slack_v2/actions/delete-file/delete-file.mjs:33-38
Timestamp: 2025-10-20T00:50:44.233Z
Learning: In the slack_v2 component (components/slack_v2/), wrapper methods like deleteFiles, postChatMessage, etc. already throw errors internally when the Slack API response is not `ok`, so explicit `response.ok` checks are unnecessary in action files.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +75 to +82
const [
{
elements: [
{ elements = [] } = {},
] = [],
} = {},
] = blocks;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Emit logic requires user AND keyword; should be OR. Also fragile block parsing and possible TypeError on undefined text.

Current code:

  • Scans only the first block/section; misses mentions in later blocks.
  • Requires userMatch && (!this.keyword || text.indexOf(this.keyword) !== -1) which contradicts “username or specific keyword” and can throw if text is undefined.

Fix: parse all blocks, add <@USER> text fallback, make keyword match safe and case-insensitive, and emit on (userMatch OR keywordMatch) OR PD history.

Apply:

@@
-      const [
-        {
-          elements: [
-            { elements = [] } = {},
-          ] = [],
-        } = {},
-      ] = blocks;
+      // Collect inner elements from all blocks / sections
+      const blockElements = [];
+      for (const b of (blocks || [])) {
+        for (const el of (b.elements || [])) {
+          if (el && Array.isArray(el.elements)) {
+            blockElements.push(...el.elements);
+          }
+        }
+      }
@@
-      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;
-      }
+      const t = typeof text === "string" ? text : "";
+      // Detect user mention via blocks and fallback to text markup
+      let userMatch = blockElements.some((item) => item && item.user_id === this.user);
+      if (!userMatch && this.user && t.includes(`<@${this.user}>`)) {
+        userMatch = true;
+      }
+      // Keyword match (case-insensitive), guarded against undefined text
+      const kw = this.keyword?.trim();
+      const keywordMatch = kw ? t.toLowerCase().includes(kw.toLowerCase()) : false;
+      // Emit if user OR keyword matches, or always for PD history
+      const emitEvent = userMatch || keywordMatch || (subtype === constants.SUBTYPE.PD_HISTORY_MESSAGE);
@@
-      if (emitEvent) {
+      if (emitEvent) {
         return event;
       }

Also applies to: 101-116

🤖 Prompt for AI Agents
In components/slack_v2/sources/new-user-mention/new-user-mention.mjs around
lines 75-82 (also apply same changes to 101-116), the current destructuring only
inspects the first block, can throw if text is undefined, and uses an AND when
it should be an OR for user vs keyword matches; change to iterate over all
blocks and their elements, safely extract text (fallback to the raw "<@USER>"
mention string if element text is missing), normalize text to lowercase before
matching to make keyword checks case-insensitive, compute userMatch =
Boolean(text.match(/<@(\w+)>/)) and keywordMatch = this.keyword &&
text.toLowerCase().includes(this.keyword.toLowerCase()), then emit when
userMatch OR keywordMatch is true (also include PD history emit as before), and
guard all accesses to avoid TypeErrors.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (7)
components/slack_v2/sources/new-interaction-event-received/README.md (1)

11-11: Remove redundant "back" from "reply back".

This issue was previously flagged and remains unresolved. Simplify to just "reply".

-With this trigger, you can build workflows that perform some work with other APIs or services, and then reply back to the original message.
+With this trigger, you can build workflows that perform some work with other APIs or services, and then reply to the original message.
components/slack_v2/actions/common/build-blocks.mjs (3)

65-76: Validate link_button input shape and guard against invalid types.

The code calls Object.keys(text) without validating that text is a plain object, which will throw a TypeError for non-objects. Additionally, substr() is deprecated in favor of slice().

Apply this diff:

       } else if (type === "link_button") {
+        if (!text || typeof text !== "object" || Array.isArray(text)) {
+          throw new Error("Link Button expects an object mapping button text → URL");
+        }
         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
+          action_id: `actionId-${Math.random().toString(36).slice(2, 11)}`,  // Generates a random action_id
         }));

97-103: Use strict equality for mode checks.

Loose equality (==) can cause unintended type coercion. Switch to ===.

-    if (this.passArrayOrConfigure == "array") {
+    if (this.passArrayOrConfigure === "array") {
       props.blocks = {

160-164: Use strict equality in run method.

-    if (this.passArrayOrConfigure === "array") {
+    if (this.passArrayOrConfigure === "array") {
       blocks = this.blocks;
components/slack_v2/sources/new-reaction-added/new-reaction-added.mjs (2)

20-22: Update prop description to match source intent.

The description incorrectly refers to "new messages" but should mention "new reactions".

-      description: "Select one or more channels to monitor for new messages.",
+      description: "Select one or more channels to monitor for new reactions.",

81-86: Bot filter logic ineffective for reaction events; also use strict equality.

Reaction events lack subtype and bot_id fields, so the bot filter will never match. Additionally, use === instead of == for type-safe comparisons. The bot check should run after fetching message context or by checking the reactor's identity.

       if (
-        ((this.ignoreBot) && (event.subtype == "bot_message" || event.bot_id)) ||
-        (iconEmojiParsed?.length > 0 && !iconEmojiParsed.includes(event.reaction))
+        (iconEmojiParsed?.length > 0 && !iconEmojiParsed.includes(event.reaction))
       ) {
         return;
       }

Then after fetching the message (line 106-113), add:

+      // Filter bots after we have context
+      if (this.ignoreBot) {
+        // Check if message author is a bot
+        if (event.message?.bot_id || event.message?.subtype === "bot_message") {
+          return;
+        }
+      }
components/slack_v2/actions/send-message-advanced/send-message-advanced.mjs (1)

9-9: Fix typo in description.

"setttings" should be "settings".

-  description: "Customize advanced settings 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",
+  description: "Customize advanced settings 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.",
🧹 Nitpick comments (7)
components/slack_v2/sources/new-interaction-event-received/README.md (1)

77-85: Reduce repetitive sentence structure in troubleshooting section.

All three steps in the troubleshooting list begin with "Make sure", which violates good writing practice. Vary the sentence openings for better readability.

-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. Verify that your `action_id` or **channels** filters apply to that message; remove the filters if you suspect they are blocking events.

-1. Make sure that the message comes from the same Slack account that this source is configured with.
+2. Confirm that the message originates 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-v2/actions/send-block-kit-message) Action) or via API call from the Pipedream app.
+3. Ensure that the message was sent via a Pipedream action (e.g. [Slack - Send Block Kit Message](https://pipedream.com/apps/slack-v2/actions/send-block-kit-message)) or via API call from the Pipedream app.
components/slack/actions/update-profile/update-profile.mjs (2)

69-78: Validation logic is correct; consider refactoring for maintainability.

The email field is correctly added to the "at least one value" validation check. With seven fields now, you might consider refactoring to an array-based check for better maintainability:

-    if (!this.displayName
-      && !this.firstName
-      && !this.lastName
-      && !this.phone
-      && !this.pronouns
-      && !this.title
-      && !this.email
-    ) {
+    const fields = [
+      this.displayName,
+      this.firstName,
+      this.lastName,
+      this.phone,
+      this.pronouns,
+      this.title,
+      this.email,
+    ];
+    if (!fields.some(Boolean)) {
       throw new ConfigurationError("Please provide at least one value to update");
     }

7-7: Consider updating the description to mention email.

The description lists "name or title" as examples but doesn't mention email, which is now supported.

-  description: "Update basic profile field such as name or title. [See the documentation](https://api.slack.com/methods/users.profile.set)",
+  description: "Update basic profile fields such as name, title, or email. [See the documentation](https://api.slack.com/methods/users.profile.set)",
components/slack_v2/actions/invite-user-to-channel/invite-user-to-channel.mjs (1)

37-43: Prefer error.message over string coercion for error checks.

While ${error} works, using error.message is more explicit and won't inadvertently match the error type in a stack trace.

Apply this diff:

     } catch (error) {
-      if (`${error}`.includes("already_in_channel")) {
+      if (error.message?.includes("already_in_channel")) {
         $.export("$summary", `The user ${this.user} is already in the channel`);
         return;
       }
       throw error;
     }
components/slack_v2/actions/get-current-user/get-current-user.mjs (1)

26-33: Log errors in catch blocks for debugging.

The empty catch block silences all errors, including network failures and bugs, not just scope issues. At minimum, log the error to aid debugging.

Apply this diff:

     } catch (error) {
-      // Gracefully degrade if scope not available
+      // Gracefully degrade if scope not available
+      console.log("Unable to fetch user info:", error.message);
     }

Apply similar changes to lines 36-42 and 45-49.

components/slack_v2/slack_v2.app.mjs (2)

486-486: Clarify conditional logic with ternary operator.

The expression isPrivate && "Private" || "Public" relies on operator precedence and short-circuit evaluation in a non-obvious way. Use a ternary for clarity.

-      return `${isPrivate && "Private" || "Public"} channel #${name}`;
+      return `${isPrivate ? "Private" : "Public"} channel #${name}`;

502-527: Log errors in getChannelDisplayName for debugging.

The catch blocks silently return fallback values without logging errors, making it difficult to diagnose API failures versus permission issues.

Apply this diff:

         } catch {
+          console.log("Unable to fetch user profile:", error.message);
           return "user";
         }
@@
         } catch {
+          console.log("Unable to fetch group members:", error.message);
           return `Group Messaging with: ${channel.purpose.value}`;
         }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f417859 and 8d72c9d.

📒 Files selected for processing (14)
  • components/slack/actions/update-profile/update-profile.mjs (2 hunks)
  • components/slack_v2/actions/add-emoji-reaction/add-emoji-reaction.mjs (1 hunks)
  • components/slack_v2/actions/common/build-blocks.mjs (1 hunks)
  • components/slack_v2/actions/find-message/find-message.mjs (1 hunks)
  • components/slack_v2/actions/get-current-user/get-current-user.mjs (1 hunks)
  • components/slack_v2/actions/invite-user-to-channel/invite-user-to-channel.mjs (1 hunks)
  • components/slack_v2/actions/kick-user/kick-user.mjs (1 hunks)
  • components/slack_v2/actions/list-users/list-users.mjs (1 hunks)
  • components/slack_v2/actions/send-message-advanced/send-message-advanced.mjs (1 hunks)
  • components/slack_v2/actions/update-group-members/update-group-members.mjs (1 hunks)
  • components/slack_v2/actions/upload-file/upload-file.mjs (1 hunks)
  • components/slack_v2/slack_v2.app.mjs (1 hunks)
  • components/slack_v2/sources/new-interaction-event-received/README.md (1 hunks)
  • components/slack_v2/sources/new-reaction-added/new-reaction-added.mjs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • components/slack_v2/actions/kick-user/kick-user.mjs
  • components/slack_v2/actions/find-message/find-message.mjs
  • components/slack_v2/actions/upload-file/upload-file.mjs
  • components/slack_v2/actions/update-group-members/update-group-members.mjs
  • components/slack_v2/actions/list-users/list-users.mjs
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-20T00:28:38.702Z
Learnt from: js07
PR: PipedreamHQ/pipedream#18744
File: components/slack/slack.app.mjs:0-0
Timestamp: 2025-10-20T00:28:38.702Z
Learning: In components/slack/slack.app.mjs, the assistantSearch method uses sdk().apiCall() directly instead of makeRequest because the slack/web-api npm package doesn't expose assistant.search.context as a method on WebClient. This direct usage is intentional and necessary when Slack API methods are not yet exposed in the SDK.

Applied to files:

  • components/slack_v2/slack_v2.app.mjs
🧬 Code graph analysis (4)
components/slack_v2/actions/send-message-advanced/send-message-advanced.mjs (2)
components/slack_v2/actions/send-block-kit-message/send-block-kit-message.mjs (1)
  • resp (43-45)
components/slack_v2/actions/common/send-message.mjs (2)
  • resp (254-254)
  • resp (255-257)
components/slack_v2/slack_v2.app.mjs (5)
components/slack_v2/actions/common/send-message.mjs (3)
  • channelId (190-190)
  • resp (254-254)
  • resp (255-257)
components/slack_v2/actions/list-group-members/list-group-members.mjs (3)
  • members (44-44)
  • page (50-50)
  • nextCursor (53-55)
components/slack_v2/actions/list-members-in-channel/list-members-in-channel.mjs (3)
  • usernames (59-59)
  • page (47-47)
  • nextCursor (50-52)
components/slack_v2/actions/delete-message/delete-message.mjs (1)
  • response (37-41)
components/slack_v2/actions/get-file/get-file.mjs (1)
  • response (53-55)
components/slack_v2/actions/invite-user-to-channel/invite-user-to-channel.mjs (1)
components/slack_v2/actions/kick-user/kick-user.mjs (1)
  • response (42-45)
components/slack_v2/actions/common/build-blocks.mjs (2)
components/slack_v2/sources/new-user-mention/new-user-mention.mjs (1)
  • blocks (75-81)
components/slack_v2/actions/common/send-message.mjs (1)
  • blocks (198-198)
🪛 LanguageTool
components/slack_v2/sources/new-interaction-event-received/README.md

[style] ~11-~11: Consider using just “reply”.
Context: ...k with other APIs or services, and then reply back to the original message. # Getting Sta...

(RETURN_BACK)


[style] ~85-~85: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...hat this source is configured with. 1. Make sure that the message was sent via Pipe...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Publish TypeScript components
  • GitHub Check: pnpm publish
  • GitHub Check: Lint Code Base
🔇 Additional comments (6)
components/slack/actions/update-profile/update-profile.mjs (2)

53-58: LGTM! Clear documentation of email field constraints.

The email prop is correctly defined with clear documentation about the admin-only restriction and self-update limitation. The API will enforce these constraints server-side.


79-90: LGTM! Email correctly added to profile payload.

The email field is properly included in the profile payload sent to Slack's API, consistent with the pattern used for other profile fields.

components/slack_v2/actions/add-emoji-reaction/add-emoji-reaction.mjs (1)

1-47: LGTM!

The import path is correct (slack_v2.app.mjs), and the action properly integrates with the Slack v2 app. Props are well-defined, and the run method correctly invokes addReactions with appropriate parameters.

components/slack_v2/sources/new-reaction-added/new-reaction-added.mjs (1)

88-104: LGTM!

The user data fetching correctly distinguishes between the reactor (event.user) and the message author (event.item_user), and assigns them to the appropriate properties.

components/slack_v2/slack_v2.app.mjs (2)

578-599: LGTM!

The retry logic properly uses async-retry with appropriate handling for rate limits, including the bail pattern to abort on unrecoverable errors.

Based on learnings


898-905: LGTM!

The direct use of sdk().apiCall() is intentional and necessary because the assistant.search.context method is not yet exposed in the Slack WebClient SDK.

Based on learnings

Comment on lines +19 to +29
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:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix malformed markdown formatting with excessive asterisks.

Multiple lines contain corrupted markdown with excessive asterisks (30–46 asterisks on each side) instead of standard bold formatting (**text**). This breaks the documentation rendering. These lines should either use proper bold syntax or plain text.

-Use the ******************Sending the message with an interactive element****************** guide below to send a message containing a button.
+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.**********************************************
+If you have already sent a message containing an element, skip to the **Configuring the source** section below.

-The easiest way is to send yourself a message using the ****************************Slack - Send Message Using Block Kit**************************** action:
+The easiest way is to send yourself a message using the **Slack - Send Message Using Block Kit** action:

-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:
+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:

-Your ******************Slack - Send Message Using Block Kit****************** should look like this:
+Your **Slack - Send Message Using Block Kit** should look like this:

-You can filter these events by selecting a specific **************channel************** and/or a specific **********action_id.**********
+You can filter these events by selecting a specific **channel** and/or a specific **action_id**.

-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.
+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.

-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 your `action_id` or **channels** filters apply to that message, remove the filters to make sure that's not the case.

Also applies to: 51-51, 59-59, 63-63, 81-81

🤖 Prompt for AI Agents
In components/slack_v2/sources/new-interaction-event-received/README.md around
lines 19 to 29 (and also at lines 51, 59, 63, 81), several markdown fragments
use excessive asterisks (30–46) instead of normal bold or plain text; replace
these corrupted sequences with proper markdown (e.g., **bold text**) or plain
text as appropriate, ensuring links and headings keep correct formatting and
removing any stray asterisks so the documentation renders correctly.

js07 added 3 commits October 20, 2025 12:27
instead update slack v2 update profile action
Send Message actions can post to public channels as the bot without
joining the channel

The bot needs to be added to private channels before posting
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
components/slack_v2/actions/common/send-message.mjs (2)

252-255: Add validation for scheduled message timestamp.

The post_at date parsing (line 253) could fail silently or produce invalid timestamps if this.post_at is not a valid date string. Consider adding validation.

Apply this diff to add validation:

     if (this.post_at) {
+      const timestamp = Math.floor(new Date(this.post_at).getTime() / 1000);
+      if (isNaN(timestamp)) {
+        throw new Error(`Invalid date format for post_at: ${this.post_at}`);
+      }
-      obj.post_at = Math.floor(new Date(this.post_at).getTime() / 1000);
+      obj.post_at = timestamp;
       return await this.slack.scheduleMessage(obj);
     }

256-262: Add error handling for API response access.

Lines 257-260 assume resp.channel exists and that conversationsInfo succeeds. If the API response is unexpected or the call fails, accessing these properties could throw.

Consider adding defensive checks:

     const resp = await this.slack.postChatMessage(obj);
-    const { channel } = await this.slack.conversationsInfo({
-      channel: resp.channel,
-    });
-    const channelName = await this.slack.getChannelDisplayName(channel);
-    $.export("$summary", `Successfully sent a message to ${channelName}`);
+    if (resp?.channel) {
+      const { channel } = await this.slack.conversationsInfo({
+        channel: resp.channel,
+      });
+      const channelName = await this.slack.getChannelDisplayName(channel);
+      $.export("$summary", `Successfully sent a message to ${channelName}`);
+    } else {
+      $.export("$summary", "Successfully sent a message");
+    }
     return resp;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ff4b476 and 1f27d2b.

📒 Files selected for processing (2)
  • components/slack_v2/actions/common/send-message.mjs (1 hunks)
  • components/slack_v2/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/slack_v2/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs
🧰 Additional context used
🧬 Code graph analysis (1)
components/slack_v2/actions/common/send-message.mjs (2)
components/slack_v2/slack_v2.app.mjs (2)
  • channel (505-507)
  • channel (514-516)
components/slack_v2/actions/send-large-message/send-large-message.mjs (4)
  • sentViaPipedreamText (45-45)
  • metadataEventPayload (49-49)
  • obj (66-79)
  • channelName (91-91)
🔇 Additional comments (4)
components/slack_v2/actions/common/send-message.mjs (4)

169-186: Good defensive coding for text serialization.

The type checking and JSON serialization for non-primitive text values (lines 174-176) is a good practice that prevents runtime errors when the Slack SDK expects a string.


217-231: Fix metadata payload assignment logic.

When metadata_event_payload is already an object (not a string), it's never assigned to metadataEventPayload (declared on line 215), which means line 229 will use undefined for the event_payload.

Apply this diff to handle both string and object inputs:

     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}`);
         }
+      } else {
+        metadataEventPayload = this.metadata_event_payload;
       }
 
       this.metadata = {
         event_type: this.metadata_event_type,
         event_payload: metadataEventPayload,
       };
     }
⛔ Skipped due to learnings
Learnt from: js07
PR: PipedreamHQ/pipedream#18744
File: components/slack_v2/actions/send-large-message/send-large-message.mjs:49-64
Timestamp: 2025-10-20T01:01:02.946Z
Learning: In components/slack_v2/actions/send-large-message/send-large-message.mjs, the metadata_event_payload prop is typed as string, so the code only needs to handle string-to-JSON parsing and does not need to handle object inputs.

233-250: Payload construction uses undefined properties.

The object construction references several properties not defined in props: text (line 234), attachments (line 236), parse (line 239), mrkdwn (line 244), link_names (line 246), and metadata (line 249, though this one is assigned earlier in the method on line 227).

These undefined values will be sent to the Slack API, potentially causing API errors or unexpected behavior.

⛔ Skipped due to learnings
Learnt from: js07
PR: PipedreamHQ/pipedream#18744
File: components/slack_v2/actions/send-large-message/send-large-message.mjs:49-64
Timestamp: 2025-10-20T01:01:02.946Z
Learning: In components/slack_v2/actions/send-large-message/send-large-message.mjs, the metadata_event_payload prop is typed as string, so the code only needs to handle string-to-JSON parsing and does not need to handle object inputs.

3-126: The review comment is based on a misunderstanding of the architecture and is incorrect.

The common/send-message.mjs file is an intentional base module designed to be extended by other actions. The properties flagged as missing (conversation, text, blocks, mrkdwn, attachments, parse, link_names, metadata) are intentionally not defined in the base module and are instead defined by each extending action.

Every action that extends this module (send-message, send-large-message, send-message-advanced, send-message-to-user-or-group, send-message-to-channel, send-block-kit-message, reply-to-a-message, and build-blocks) properly defines these properties via propDefinition from the slack component. Each action uses the pattern:

import common from "../common/send-message.mjs";
export default {
  ...common,
  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,
  }
}

This allows each action to customize these properties independently (different descriptions, filters, optional/required status). The base module is not meant to be used standalone—it provides shared business logic that all these actions inherit and extend.

Likely an incorrect or invalid review comment.

@js07 js07 merged commit 7b76ded into master Oct 22, 2025
10 checks passed
@js07 js07 deleted the slack-v2-support branch October 22, 2025 16:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants