@pipedream/slack_v2 0.6.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/actions/add-emoji-reaction/add-emoji-reaction.mjs +1 -1
  2. package/actions/add-reaction/add-reaction.mjs +2 -1
  3. package/actions/archive-channel/archive-channel.mjs +1 -1
  4. package/actions/browse-files/browse-files.mjs +2 -1
  5. package/actions/create-channel/create-channel.mjs +1 -1
  6. package/actions/create-reminder/create-reminder.mjs +1 -1
  7. package/actions/delete-file/delete-file.mjs +1 -1
  8. package/actions/delete-message/delete-message.mjs +49 -6
  9. package/actions/edit-message/edit-message.mjs +2 -1
  10. package/actions/find-message/find-message.mjs +1 -1
  11. package/actions/find-user-by-email/find-user-by-email.mjs +1 -1
  12. package/actions/find-user-by-id/find-user-by-id.mjs +1 -1
  13. package/actions/get-channel-details/get-channel-details.mjs +6 -3
  14. package/actions/get-channel-history/get-channel-history.mjs +20 -5
  15. package/actions/get-current-user/get-current-user.mjs +2 -2
  16. package/actions/get-file/get-file.mjs +1 -1
  17. package/actions/get-thread-replies/get-thread-replies.mjs +18 -2
  18. package/actions/get-user-details/get-user-details.mjs +9 -4
  19. package/actions/invite-user-to-channel/invite-user-to-channel.mjs +72 -14
  20. package/actions/kick-user/kick-user.mjs +1 -1
  21. package/actions/list-channels/list-channels.mjs +55 -6
  22. package/actions/list-emojis/list-emojis.mjs +1 -1
  23. package/actions/list-files/list-files.mjs +1 -1
  24. package/actions/list-group-members/list-group-members.mjs +1 -1
  25. package/actions/list-icon-emoji-options/list-icon-emoji-options.mjs +1 -1
  26. package/actions/list-members-in-channel/list-members-in-channel.mjs +7 -3
  27. package/actions/list-messages/list-messages.mjs +7 -3
  28. package/actions/list-reminder-options/list-reminder-options.mjs +1 -1
  29. package/actions/list-replies/list-replies.mjs +1 -1
  30. package/actions/list-user-group-options/list-user-group-options.mjs +1 -1
  31. package/actions/list-users/list-users.mjs +1 -1
  32. package/actions/post-message/post-message.mjs +2 -1
  33. package/actions/reply-to-a-message/reply-to-a-message.mjs +1 -1
  34. package/actions/search/search.mjs +2 -1
  35. package/actions/send-block-kit-message/send-block-kit-message.mjs +1 -1
  36. package/actions/send-large-message/send-large-message.mjs +1 -1
  37. package/actions/send-message/send-message.mjs +1 -1
  38. package/actions/send-message-advanced/send-message-advanced.mjs +1 -1
  39. package/actions/send-message-to-channel/send-message-to-channel.mjs +1 -1
  40. package/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs +1 -1
  41. package/actions/set-channel-description/set-channel-description.mjs +1 -1
  42. package/actions/set-channel-topic/set-channel-topic.mjs +7 -4
  43. package/actions/set-status/set-status.mjs +1 -1
  44. package/actions/update-group-members/update-group-members.mjs +1 -1
  45. package/actions/update-message/update-message.mjs +1 -1
  46. package/actions/update-profile/update-profile.mjs +1 -1
  47. package/actions/upload-file/upload-file.mjs +1 -1
  48. package/actions/verify-slack-signature/verify-slack-signature.mjs +1 -1
  49. package/common/utils.mjs +69 -0
  50. package/package.json +1 -1
  51. package/slack_v2.app.mjs +99 -4
  52. package/sources/new-channel-created/new-channel-created.mjs +1 -1
  53. package/sources/new-interaction-event-received/new-interaction-event-received.mjs +1 -1
  54. package/sources/new-keyword-mention/new-keyword-mention.mjs +1 -1
  55. package/sources/new-message-in-channels/new-message-in-channels.mjs +1 -1
  56. package/sources/new-private-channel-created/new-private-channel-created.mjs +1 -1
  57. package/sources/new-reaction-added/new-reaction-added.mjs +1 -1
  58. package/sources/new-saved-message/new-saved-message.mjs +1 -1
  59. package/sources/new-user-added/new-user-added.mjs +1 -1
  60. package/sources/new-user-mention/new-user-mention.mjs +1 -1
  61. package/actions/approve-workflow/approve-workflow.mjs +0 -92
@@ -4,7 +4,7 @@ export default {
4
4
  key: "slack_v2-list-icon-emoji-options",
5
5
  name: "List Icon (emoji) Options",
6
6
  description: "Retrieves available options for the Icon (emoji) field.",
7
- version: "0.0.1",
7
+ version: "0.0.3",
8
8
  type: "action",
9
9
  annotations: {
10
10
  destructiveHint: false,
@@ -3,8 +3,8 @@ import slack from "../../slack_v2.app.mjs";
3
3
  export default {
4
4
  key: "slack_v2-list-members-in-channel",
5
5
  name: "List Members in Channel",
6
- description: "Retrieve members of a channel. [See the documentation](https://api.slack.com/methods/conversations.members)",
7
- version: "0.0.29",
6
+ description: "Retrieve members of a channel. Accepts a channel ID or NAME (e.g. general or #general) — names are resolved automatically. [See the documentation](https://api.slack.com/methods/conversations.members)",
7
+ version: "0.1.1",
8
8
  annotations: {
9
9
  destructiveHint: false,
10
10
  openWorldHint: true,
@@ -40,8 +40,12 @@ export default {
40
40
  },
41
41
  async run({ $ }) {
42
42
  let channelMembers = [];
43
+ // Accept a channel NAME as well as an ID. Every AI-optimized tool in this app resolves
44
+ // names server-side, so an agent that read "#seinfeld-general" from the prompt
45
+ // reasonably passes it here too — and used to get channel_not_found.
46
+ const channel = await this.slack.resolveChannelId(this.conversation);
43
47
  const params = {
44
- channel: this.conversation,
48
+ channel,
45
49
  limit: this.pageSize,
46
50
  };
47
51
  let page = 0;
@@ -2,10 +2,14 @@ import slack from "../../slack_v2.app.mjs";
2
2
 
3
3
  export default {
4
4
  key: "slack_v2-list-messages",
5
- name: "List Messages",
5
+ name: "List Messages (Deprecated)",
6
6
  description:
7
- "Retrieve messages from a Slack conversation, including reactions. [See the documentation](https://api.slack.com/methods/conversations.history)",
8
- version: "0.0.4",
7
+ "DEPRECATED do NOT use for reading a channel's or DM's messages. Use **Get Channel History** instead:"
8
+ + " it resolves channel names, channel IDs, and direct messages (by user ID), supports `fields`"
9
+ + " selection to keep responses small, and returns the same message data."
10
+ + " This legacy tool remains only for existing workflows: it accepts a raw conversation ID and"
11
+ + " returns full, untrimmed message objects. [See the documentation](https://api.slack.com/methods/conversations.history)",
12
+ version: "0.0.6",
9
13
  annotations: {
10
14
  destructiveHint: false,
11
15
  openWorldHint: true,
@@ -4,7 +4,7 @@ export default {
4
4
  key: "slack_v2-list-reminder-options",
5
5
  name: "List Reminder Options",
6
6
  description: "Retrieves available options for the Reminder field.",
7
- version: "0.0.1",
7
+ version: "0.0.3",
8
8
  type: "action",
9
9
  annotations: {
10
10
  destructiveHint: false,
@@ -5,7 +5,7 @@ export default {
5
5
  key: "slack_v2-list-replies",
6
6
  name: "List Replies",
7
7
  description: "Retrieve a thread of messages posted to a conversation. [See the documentation](https://api.slack.com/methods/conversations.replies)",
8
- version: "0.0.29",
8
+ version: "0.0.31",
9
9
  annotations: {
10
10
  destructiveHint: false,
11
11
  openWorldHint: true,
@@ -4,7 +4,7 @@ export default {
4
4
  key: "slack_v2-list-user-group-options",
5
5
  name: "List User Group Options",
6
6
  description: "Retrieves available options for the User Group field.",
7
- version: "0.0.1",
7
+ version: "0.0.3",
8
8
  type: "action",
9
9
  annotations: {
10
10
  destructiveHint: false,
@@ -4,7 +4,7 @@ export default {
4
4
  key: "slack_v2-list-users",
5
5
  name: "List Users",
6
6
  description: "Return a list of all users in a workspace. [See the documentation](https://api.slack.com/methods/users.list)",
7
- version: "0.0.28",
7
+ version: "0.0.30",
8
8
  annotations: {
9
9
  destructiveHint: false,
10
10
  openWorldHint: true,
@@ -1,3 +1,4 @@
1
+ // x-pd-ai: optimized
1
2
  import slack from "../../slack_v2.app.mjs";
2
3
  import { ConfigurationError } from "@pipedream/platform";
3
4
 
@@ -10,7 +11,7 @@ export default {
10
11
  + " To reply to a thread, provide `thread_ts` from **Get Channel History**."
11
12
  + " Supports plain text with Slack mrkdwn formatting and Block Kit blocks."
12
13
  + " [See the documentation](https://api.slack.com/methods/chat.postMessage)",
13
- version: "0.0.1",
14
+ version: "0.0.3",
14
15
  type: "action",
15
16
  annotations: {
16
17
  destructiveHint: false,
@@ -6,7 +6,7 @@ export default {
6
6
  key: "slack_v2-reply-to-a-message",
7
7
  name: "Reply to a Message Thread",
8
8
  description: "Send a message as a threaded reply. See [postMessage](https://api.slack.com/methods/chat.postMessage) or [scheduleMessage](https://api.slack.com/methods/chat.scheduleMessage) docs here",
9
- version: "0.2.4",
9
+ version: "0.2.6",
10
10
  annotations: {
11
11
  destructiveHint: false,
12
12
  openWorldHint: true,
@@ -1,3 +1,4 @@
1
+ // x-pd-ai: optimized
1
2
  import slack from "../../slack_v2.app.mjs";
2
3
 
3
4
  export default {
@@ -9,7 +10,7 @@ export default {
9
10
  + " Use **Get User Details** first to find your user ID for filtering by 'my' messages."
10
11
  + " Returns matching messages with channel context, timestamps, and permalinks."
11
12
  + " [See the documentation](https://api.slack.com/methods/assistant.search.context)",
12
- version: "0.0.1",
13
+ version: "0.0.3",
13
14
  type: "action",
14
15
  annotations: {
15
16
  destructiveHint: false,
@@ -7,7 +7,7 @@ export default {
7
7
  key: "slack_v2-send-block-kit-message",
8
8
  name: "Build and Send a Block Kit Message",
9
9
  description: "Configure custom blocks and send to a channel, group, or user. [See the documentation](https://api.slack.com/tools/block-kit-builder).",
10
- version: "0.5.4",
10
+ version: "0.5.6",
11
11
  annotations: {
12
12
  destructiveHint: false,
13
13
  openWorldHint: true,
@@ -5,7 +5,7 @@ export default {
5
5
  key: "slack_v2-send-large-message",
6
6
  name: "Send a Large Message (3000+ characters)",
7
7
  description: "Send a large message (more than 3000 characters) to a channel, group or user. See [postMessage](https://api.slack.com/methods/chat.postMessage) or [scheduleMessage](https://api.slack.com/methods/chat.scheduleMessage) docs here",
8
- version: "0.1.4",
8
+ version: "0.1.6",
9
9
  annotations: {
10
10
  destructiveHint: false,
11
11
  openWorldHint: true,
@@ -6,7 +6,7 @@ export default {
6
6
  key: "slack_v2-send-message",
7
7
  name: "Send Message",
8
8
  description: "Send a message to a user, group, private channel or public channel. [See the documentation](https://api.slack.com/methods/chat.postMessage)",
9
- version: "0.1.4",
9
+ version: "0.1.6",
10
10
  annotations: {
11
11
  destructiveHint: false,
12
12
  openWorldHint: true,
@@ -7,7 +7,7 @@ export default {
7
7
  key: "slack_v2-send-message-advanced",
8
8
  name: "Send Message (Advanced)",
9
9
  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",
10
- version: "0.1.4",
10
+ version: "0.1.6",
11
11
  annotations: {
12
12
  destructiveHint: false,
13
13
  openWorldHint: true,
@@ -6,7 +6,7 @@ export default {
6
6
  key: "slack_v2-send-message-to-channel",
7
7
  name: "Send Message to Channel",
8
8
  description: "Send a message to a public or private channel. [See the documentation](https://api.slack.com/methods/chat.postMessage)",
9
- version: "0.1.4",
9
+ version: "0.1.6",
10
10
  annotations: {
11
11
  destructiveHint: false,
12
12
  openWorldHint: true,
@@ -7,7 +7,7 @@ export default {
7
7
  key: "slack_v2-send-message-to-user-or-group",
8
8
  name: "Send Message to User or Group",
9
9
  description: "Send a message to a user or group. [See the documentation](https://api.slack.com/methods/chat.postMessage)",
10
- version: "0.1.4",
10
+ version: "0.1.6",
11
11
  annotations: {
12
12
  destructiveHint: false,
13
13
  openWorldHint: true,
@@ -4,7 +4,7 @@ export default {
4
4
  key: "slack_v2-set-channel-description",
5
5
  name: "Set Channel Description",
6
6
  description: "Change the description or purpose of a channel. [See the documentation](https://api.slack.com/methods/conversations.setPurpose)",
7
- version: "0.0.14",
7
+ version: "0.0.16",
8
8
  annotations: {
9
9
  destructiveHint: false,
10
10
  openWorldHint: true,
@@ -3,8 +3,8 @@ import slack from "../../slack_v2.app.mjs";
3
3
  export default {
4
4
  key: "slack_v2-set-channel-topic",
5
5
  name: "Set Channel Topic",
6
- description: "Set the topic on a selected channel. [See the documentation](https://api.slack.com/methods/conversations.setTopic)",
7
- version: "0.0.29",
6
+ description: "Set the topic on a channel, specified by ID or by name — names are resolved automatically. [See the documentation](https://api.slack.com/methods/conversations.setTopic)",
7
+ version: "0.1.1",
8
8
  annotations: {
9
9
  destructiveHint: true,
10
10
  openWorldHint: true,
@@ -27,11 +27,14 @@ export default {
27
27
  },
28
28
  },
29
29
  async run({ $ }) {
30
+ // Accept a channel NAME as well as an ID. Slack answers a name here with a bare
31
+ // `internal_error`, which tells an agent nothing about what it did wrong.
32
+ const channel = await this.slack.resolveChannelId(this.conversation);
30
33
  const response = await this.slack.setChannelTopic({
31
- channel: this.conversation,
34
+ channel,
32
35
  topic: this.topic,
33
36
  });
34
- $.export("$summary", `Successfully set topic for channel with ID ${this.conversation}`);
37
+ $.export("$summary", `Successfully set topic for channel with ID ${channel}`);
35
38
  return response;
36
39
  },
37
40
  };
@@ -4,7 +4,7 @@ export default {
4
4
  key: "slack_v2-set-status",
5
5
  name: "Set Status",
6
6
  description: "Set the current status for a user. [See the documentation](https://api.slack.com/methods/users.profile.set)",
7
- version: "0.0.13",
7
+ version: "0.0.15",
8
8
  annotations: {
9
9
  destructiveHint: true,
10
10
  openWorldHint: true,
@@ -4,7 +4,7 @@ export default {
4
4
  key: "slack_v2-update-group-members",
5
5
  name: "Update Group Members",
6
6
  description: "Update the list of users for a User Group. [See the documentation](https://api.slack.com/methods/usergroups.users.update)",
7
- version: "0.0.14",
7
+ version: "0.0.16",
8
8
  annotations: {
9
9
  destructiveHint: true,
10
10
  openWorldHint: true,
@@ -4,7 +4,7 @@ export default {
4
4
  key: "slack_v2-update-message",
5
5
  name: "Update Message",
6
6
  description: "Update a message. [See the documentation](https://api.slack.com/methods/chat.update)",
7
- version: "0.2.4",
7
+ version: "0.2.6",
8
8
  annotations: {
9
9
  destructiveHint: true,
10
10
  openWorldHint: true,
@@ -5,7 +5,7 @@ export default {
5
5
  key: "slack_v2-update-profile",
6
6
  name: "Update Profile",
7
7
  description: "Update basic profile field such as name or title. [See the documentation](https://api.slack.com/methods/users.profile.set)",
8
- version: "0.0.29",
8
+ version: "0.0.31",
9
9
  annotations: {
10
10
  destructiveHint: true,
11
11
  openWorldHint: true,
@@ -8,7 +8,7 @@ export default {
8
8
  key: "slack_v2-upload-file",
9
9
  name: "Upload File",
10
10
  description: "Upload a file. [See the documentation](https://api.slack.com/messaging/files#uploading_files)",
11
- version: "0.1.8",
11
+ version: "0.1.10",
12
12
  annotations: {
13
13
  destructiveHint: false,
14
14
  openWorldHint: true,
@@ -5,7 +5,7 @@ export default {
5
5
  key: "slack_v2-verify-slack-signature",
6
6
  name: "Verify Slack Signature",
7
7
  description: "Verifying requests from Slack, slack signs its requests using a secret that's unique to your app. [See the documentation](https://api.slack.com/authentication/verifying-requests-from-slack)",
8
- version: "0.0.3",
8
+ version: "0.0.5",
9
9
  annotations: {
10
10
  destructiveHint: false,
11
11
  openWorldHint: true,
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Shared helpers for the `fields` projection offered by the list/history actions.
3
+ *
4
+ * Three actions (Get Channel History, Get Thread Replies, List Channels) let the caller
5
+ * name the properties they want back. Both halves of that feature — normalizing the prop
6
+ * value and applying it — were copy-pasted into each action; keeping one copy means a fix
7
+ * to either lands everywhere at once.
8
+ */
9
+
10
+ /**
11
+ * Normalize a `string[]` prop that an LLM caller may well send as a CSV string.
12
+ *
13
+ * `fields` is declared `string[]`, but MCP/LLM callers routinely pass `"text,ts,user"`.
14
+ * Spreading a bare string into a keep-set yields single characters, which match no key,
15
+ * so the caller would silently get back nothing at all.
16
+ *
17
+ * @param {string|string[]|undefined} value - the raw prop value
18
+ * @returns {string[]|null} the requested field names, or null when none were requested
19
+ */
20
+ export function parseFields(value) {
21
+ if (Array.isArray(value)) return value;
22
+ if (typeof value === "string" && value.length) {
23
+ return value.split(",")
24
+ .map((f) => f.trim())
25
+ .filter(Boolean);
26
+ }
27
+ return null;
28
+ }
29
+
30
+ /**
31
+ * Keep only the requested properties of a record.
32
+ *
33
+ * Unknown names are ignored rather than returned as `undefined`, so a typo shrinks the
34
+ * payload instead of corrupting it with null-valued keys.
35
+ *
36
+ * @param {object} record - a message or channel object
37
+ * @param {string[]} fields - property names to keep
38
+ * @returns {object} the projected record
39
+ */
40
+ export function pickFields(record, fields) {
41
+ const out = {};
42
+ for (const field of fields) {
43
+ if (record[field] !== undefined) out[field] = record[field];
44
+ }
45
+ return out;
46
+ }
47
+
48
+ /**
49
+ * Apply a `fields` projection to a list, or return it untouched when none was requested.
50
+ *
51
+ * The no-fields path returns the ORIGINAL array, which is what makes `fields` additive:
52
+ * a caller that omits it gets exactly what these actions have always returned.
53
+ *
54
+ * @param {object[]} records - the records to project
55
+ * @param {string|string[]|undefined} value - the raw `fields` prop value
56
+ * @returns {object[]} projected records, or `records` unchanged
57
+ */
58
+ export function projectFields(records, value) {
59
+ const fields = parseFields(value);
60
+ return fields?.length
61
+ ? records.map((r) => pickFields(r, fields))
62
+ : records;
63
+ }
64
+
65
+ export default {
66
+ parseFields,
67
+ pickFields,
68
+ projectFields,
69
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipedream/slack_v2",
3
- "version": "0.6.1",
3
+ "version": "0.8.0",
4
4
  "description": "Pipedream Slack_v2 Components",
5
5
  "main": "slack_v2.app.mjs",
6
6
  "keywords": [
package/slack_v2.app.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ // x-pd-ai: optimized
1
2
  import { WebClient } from "@slack/web-api";
2
3
  import constants from "./common/constants.mjs";
3
4
  import get from "lodash/get.js";
@@ -967,10 +968,15 @@ export default {
967
968
  assistantSearch(args = {}) {
968
969
  args.count ||= constants.LIMIT;
969
970
  // Uses apiCall directly since assistant.search.context is not exposed as
970
- // a method on WebClient
971
- return this.sdk().apiCall("assistant.search.context", {
971
+ // a method on WebClient — but it must STILL go through _withRetries. Calling
972
+ // apiCall bare was the one path in this app that skipped the retry wrapper, and
973
+ // the client is built with `rejectRateLimitedCalls: true`, so a 429 rejected
974
+ // instantly instead of backing off. assistant.search.context rate-limits readily
975
+ // (Slack returns retryAfter: 60), which surfaced to agents as a hard tool error
976
+ // on 8 of 12 search calls in one eval run.
977
+ return this._withRetries(() => this.sdk().apiCall("assistant.search.context", {
972
978
  ...args,
973
- });
979
+ }));
974
980
  },
975
981
  /**
976
982
  * Lists reactions made by a user.
@@ -1105,6 +1111,12 @@ export default {
1105
1111
  ...args,
1106
1112
  });
1107
1113
  },
1114
+ openConversation(args = {}) {
1115
+ return this.makeRequest({
1116
+ method: "conversations.open",
1117
+ ...args,
1118
+ });
1119
+ },
1108
1120
  inviteToConversation(args = {}) {
1109
1121
  return this.makeRequest({
1110
1122
  method: "conversations.invite",
@@ -1192,6 +1204,74 @@ export default {
1192
1204
  normalizeChannel(input) {
1193
1205
  return input.replace(/^#/, "");
1194
1206
  },
1207
+ /**
1208
+ * Accept a user ID, an email, or a display/real name and return the user ID.
1209
+ * Agents naturally pass whatever identifier the user said — an email in
1210
+ * "invite dylan@pipedream.com" — and the raw API answers `user_not_found`,
1211
+ * which reads as a broken tool rather than a wrong-shaped argument.
1212
+ */
1213
+ async resolveUserId(input) {
1214
+ if (/^[UW][A-Z0-9]{6,}$/i.test(input)) return input;
1215
+ if (/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(input)) {
1216
+ const { user } = await this.makeRequest({
1217
+ method: "users.lookupByEmail",
1218
+ email: input,
1219
+ });
1220
+ return user.id;
1221
+ }
1222
+ const name = input.replace(/^@/, "").toLowerCase();
1223
+ const matches = [];
1224
+ let cursor;
1225
+ do {
1226
+ const {
1227
+ members, response_metadata: metadata,
1228
+ } = await this.makeRequest({
1229
+ method: "users.list",
1230
+ limit: 200,
1231
+ cursor,
1232
+ });
1233
+ for (const member of members) {
1234
+ const isMatch = [
1235
+ member.name,
1236
+ member.real_name,
1237
+ member.profile?.display_name,
1238
+ ].filter(Boolean).some((n) => String(n).toLowerCase() === name);
1239
+ if (isMatch) matches.push(member);
1240
+ }
1241
+ cursor = metadata?.next_cursor;
1242
+ } while (cursor);
1243
+ if (matches.length === 1) return matches[0].id;
1244
+ if (matches.length > 1) {
1245
+ throw new ConfigurationError(`Multiple users match "${input}". Provide a user ID or an email address instead.`);
1246
+ }
1247
+ throw new ConfigurationError(`User "${input}" not found. Provide a user ID, an email address, or an exact display name.`);
1248
+ },
1249
+ /**
1250
+ * Resolve one OR MORE user identifiers to the comma-separated `users` string Slack's
1251
+ * conversations.* methods accept.
1252
+ *
1253
+ * The list form has to keep working: `users` is documented as comma-separated, the prop
1254
+ * is a plain string, and callers that predate resolveUserId() pass "U123,U456" straight
1255
+ * through. Resolving the whole string as a single identifier would send it to an
1256
+ * exhaustive users.list scan that cannot match, then throw.
1257
+ *
1258
+ * @param {string|string[]} input - id / email / display name, or a comma-separated list
1259
+ * or array of them
1260
+ * @returns {Promise<string>} Comma-separated user IDs
1261
+ */
1262
+ async resolveUserIds(input) {
1263
+ const raw = Array.isArray(input)
1264
+ ? input
1265
+ : String(input).split(",");
1266
+ const tokens = raw
1267
+ .map((t) => String(t).trim())
1268
+ .filter(Boolean);
1269
+ const ids = [];
1270
+ for (const token of tokens) {
1271
+ ids.push(await this.resolveUserId(token));
1272
+ }
1273
+ return ids.join(",");
1274
+ },
1195
1275
  /**
1196
1276
  * Resolves a channel name (e.g. "general", "#general") to its ID.
1197
1277
  * If the input already looks like an ID (starts with C/D/G/U + 8+ alphanums),
@@ -1205,7 +1285,22 @@ export default {
1205
1285
  * @returns {Promise<string>} The resolved channel ID
1206
1286
  */
1207
1287
  async resolveChannelId(input) {
1208
- if (/^[CDGU][A-Z0-9]{8,}$/i.test(input)) return input;
1288
+ // Slack ids are UPPERCASE-only (e.g. `C0123ABCD`, `U0123ABCD`). Match case-sensitively:
1289
+ // channel names are lowercased by Slack, so a `/i` match would misclassify an all-alnum
1290
+ // name like `welcomeaboard` or `developers` (no hyphen, 9+ chars) as an id.
1291
+ // Channel, private-group, and DM ids are already conversation ids — pass through.
1292
+ if (/^[CDG][A-Z0-9]{8,}$/.test(input)) return input;
1293
+ // A user id is NOT a conversation id — conversations.* answers `channel_not_found`
1294
+ // for it. Open (idempotently) the DM with that user and use the returned IM channel.
1295
+ // This is the read-side counterpart to chat.postMessage accepting a user id: an agent
1296
+ // asked to read "my DMs" resolves its own user id and passes it here, and history,
1297
+ // thread replies, and reactions now accept it the way posting already does.
1298
+ if (/^[UW][A-Z0-9]{8,}$/.test(input)) {
1299
+ const { channel } = await this.openConversation({
1300
+ users: input,
1301
+ });
1302
+ return channel.id;
1303
+ }
1209
1304
  const name = input.replace(/^#/, "").toLowerCase();
1210
1305
  let cursor;
1211
1306
  do {
@@ -5,7 +5,7 @@ export default {
5
5
  ...common,
6
6
  key: "slack_v2-new-channel-created",
7
7
  name: "New Channel Created (Instant)",
8
- version: "0.0.14",
8
+ version: "0.0.16",
9
9
  description: "Emit new event when a new channel is created.",
10
10
  type: "source",
11
11
  dedupe: "unique",
@@ -3,7 +3,7 @@ import sampleEmit from "./test-event.mjs";
3
3
 
4
4
  export default {
5
5
  name: "New Interaction Events (Instant)",
6
- version: "0.0.25",
6
+ version: "0.0.27",
7
7
  key: "slack_v2-new-interaction-event-received",
8
8
  description: "Emit new events on new Slack [interactivity events](https://api.slack.com/interactivity) sourced from [Block Kit interactive elements](https://api.slack.com/interactivity/components), [Slash commands](https://api.slack.com/interactivity/slash-commands), or [Shortcuts](https://api.slack.com/interactivity/shortcuts).",
9
9
  type: "source",
@@ -7,7 +7,7 @@ export default {
7
7
  ...common,
8
8
  key: "slack_v2-new-keyword-mention",
9
9
  name: "New Keyword Mention (Instant)",
10
- version: "0.1.4",
10
+ version: "0.1.6",
11
11
  description: "Emit new event when a specific keyword is mentioned in a channel",
12
12
  type: "source",
13
13
  dedupe: "unique",
@@ -7,7 +7,7 @@ export default {
7
7
  ...common,
8
8
  key: "slack_v2-new-message-in-channels",
9
9
  name: "New Message In Channels (Instant)",
10
- version: "1.1.4",
10
+ version: "1.1.6",
11
11
  description: "Emit new event when a new message is posted to one or more channels",
12
12
  type: "source",
13
13
  dedupe: "unique",
@@ -4,7 +4,7 @@ export default {
4
4
  ...common,
5
5
  key: "slack_v2-new-private-channel-created",
6
6
  name: "New Private Channel Created",
7
- version: "0.0.4",
7
+ version: "0.0.6",
8
8
  description: "Emit new event when a new private channel is created. [See the documentation](https://api.slack.com/methods/conversations.list)",
9
9
  type: "source",
10
10
  dedupe: "unique",
@@ -5,7 +5,7 @@ export default {
5
5
  ...common,
6
6
  key: "slack_v2-new-reaction-added",
7
7
  name: "New Reaction Added (Instant)",
8
- version: "1.2.4",
8
+ version: "1.2.6",
9
9
  description: "Emit new event when a member has added an emoji reaction to a message",
10
10
  type: "source",
11
11
  dedupe: "unique",
@@ -5,7 +5,7 @@ export default {
5
5
  ...common,
6
6
  key: "slack_v2-new-saved-message",
7
7
  name: "New Saved Message (Instant)",
8
- version: "0.0.10",
8
+ version: "0.0.12",
9
9
  description: "Emit new event when a message is saved. Note: The endpoint is marked as deprecated, and Slack might shut this off at some point down the line.",
10
10
  type: "source",
11
11
  dedupe: "unique",
@@ -5,7 +5,7 @@ export default {
5
5
  ...common,
6
6
  key: "slack_v2-new-user-added",
7
7
  name: "New User Added (Instant)",
8
- version: "0.0.8",
8
+ version: "0.0.10",
9
9
  description: "Emit new event when a new member joins a workspace.",
10
10
  type: "source",
11
11
  dedupe: "unique",
@@ -7,7 +7,7 @@ export default {
7
7
  ...common,
8
8
  key: "slack_v2-new-user-mention",
9
9
  name: "New User Mention (Instant)",
10
- version: "0.1.4",
10
+ version: "0.1.6",
11
11
  description: "Emit new event when a username or specific keyword is mentioned in a channel",
12
12
  type: "source",
13
13
  dedupe: "unique",