Skip to content

Commit 8b99a4e

Browse files
committed
Use assistant search API in Find Message action
Fallback to search.messages API method if missing scopese
1 parent ce6dba8 commit 8b99a4e

File tree

2 files changed

+151
-39
lines changed

2 files changed

+151
-39
lines changed

components/slack/actions/find-message/find-message.mjs

Lines changed: 145 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import { axios } from "@pipedream/platform";
21
import slack from "../../slack.app.mjs";
32

43
export default {
54
key: "slack-find-message",
65
name: "Find Message",
76
description: "Find a Slack message. [See the documentation](https://api.slack.com/methods/assistant.search.context)",
8-
version: "0.0.26",
7+
version: "0.1.0",
98
annotations: {
109
destructiveHint: false,
1110
openWorldHint: true,
@@ -49,53 +48,160 @@ export default {
4948
},
5049
},
5150
methods: {
52-
async searchMessages($, params) {
53-
const data = await axios($, {
54-
method: "POST",
55-
url: "https://slack.com/api/assistant.search.context",
56-
data: {
57-
query: params.query,
58-
sort: params.sort,
59-
sort_dir: params.sort_dir,
60-
cursor: params.cursor,
61-
channel_types: params.channel_types,
62-
},
63-
headers: {
64-
"Authorization": `Bearer ${this.slack.getToken()}`,
65-
"Content-Type": "application/json",
66-
},
67-
});
68-
if (!data.ok) {
69-
throw new Error(data.error || "An error occurred while searching messages");
51+
normalizeAssistantMatch(match) {
52+
if (!match || typeof match !== "object") {
53+
return match;
7054
}
71-
return data;
55+
const {
56+
author_user_id: authorUserId,
57+
team_id: teamId,
58+
channel_id: channelId,
59+
message_ts: messageTs,
60+
content,
61+
permalink,
62+
is_author_bot: isAuthorBot,
63+
message,
64+
channel,
65+
...rest
66+
} = match;
67+
const baseMessage = typeof message === "object"
68+
? message
69+
: {};
70+
const channelInfo = channel && typeof channel === "object"
71+
? {
72+
...channel,
73+
id: channel.id || channelId,
74+
}
75+
: channelId
76+
? {
77+
id: channelId,
78+
}
79+
: undefined;
80+
const normalized = {
81+
type: "message",
82+
user: authorUserId,
83+
team: teamId,
84+
ts: messageTs,
85+
text: content,
86+
permalink,
87+
channel: channelInfo,
88+
...baseMessage,
89+
...rest,
90+
};
91+
if (isAuthorBot !== undefined && normalized.is_author_bot === undefined) {
92+
normalized.is_author_bot = isAuthorBot;
93+
}
94+
if (normalized.text == null) {
95+
normalized.text = baseMessage.text || content;
96+
}
97+
if (normalized.ts == null) {
98+
normalized.ts = baseMessage.ts || messageTs;
99+
}
100+
if (!normalized.channel && baseMessage.channel) {
101+
normalized.channel = baseMessage.channel;
102+
} else if (normalized.channel && baseMessage.channel && typeof baseMessage.channel === "object") {
103+
normalized.channel = {
104+
...normalized.channel,
105+
...baseMessage.channel,
106+
};
107+
}
108+
return normalized;
109+
},
110+
async searchWithAssistant(baseParams, maxResults) {
111+
const matches = [];
112+
let cursor;
113+
114+
do {
115+
const response = await this.slack.assistantSearch({
116+
...baseParams,
117+
channel_types: "public_channel,private_channel",
118+
cursor,
119+
});
120+
const messages = (response.results?.messages || [])
121+
.map((item) => this.normalizeAssistantMatch(item));
122+
matches.push(...messages);
123+
cursor = response.response_metadata?.next_cursor;
124+
} while (cursor && matches.length < maxResults);
125+
126+
return matches.slice(0, maxResults);
127+
},
128+
async searchWithSearchMessages(baseParams, maxResults) {
129+
const matches = [];
130+
let page = 1;
131+
const count = Math.min(Math.max(maxResults, 1), 100);
132+
133+
while (matches.length < maxResults) {
134+
const response = await this.slack.searchMessages({
135+
...baseParams,
136+
count,
137+
page,
138+
});
139+
const pageMatches = response.messages?.matches || [];
140+
matches.push(...pageMatches);
141+
142+
if (matches.length >= maxResults) {
143+
break;
144+
}
145+
146+
const pagination = response.messages?.pagination;
147+
const paging = response.messages?.paging;
148+
const hasMore = pagination
149+
? pagination.page < pagination.page_count
150+
: paging
151+
? paging.page < paging.pages
152+
: false;
153+
154+
if (!hasMore) {
155+
break;
156+
}
157+
158+
page += 1;
159+
}
160+
161+
return matches.slice(0, maxResults);
162+
},
163+
shouldFallbackToSearchMessages(error) {
164+
const errorCode = typeof error === "string"
165+
? error
166+
: error?.data?.error || error?.message;
167+
168+
if (!errorCode.includes("missing_scope")) {
169+
return false;
170+
}
171+
172+
const providedSources = [
173+
error?.data?.provided,
174+
error?.provided,
175+
error?.original?.data?.provided,
176+
].filter(Boolean);
177+
178+
const providedScopes = providedSources
179+
.flatMap((value) => Array.isArray(value)
180+
? value
181+
: String(value).split(","))
182+
.map((scope) => scope.trim())
183+
.filter(Boolean);
184+
185+
return providedScopes.includes("search:read");
72186
},
73187
},
74188
async run({ $ }) {
75-
const matches = [];
76-
const params = {
189+
const maxResults = Math.max(this.maxResults ?? 20, 1);
190+
const baseParams = {
77191
query: this.query,
78192
sort: this.sort,
79193
sort_dir: this.sortDirection,
80-
channel_types: "public_channel,private_channel",
81194
};
82-
let cursor;
195+
let matches;
83196

84-
do {
85-
if (cursor) {
86-
params.cursor = cursor;
197+
try {
198+
matches = await this.searchWithAssistant(baseParams, maxResults);
199+
} catch (error) {
200+
if (this.shouldFallbackToSearchMessages(error)) {
201+
matches = await this.searchWithSearchMessages(baseParams, maxResults);
202+
} else {
203+
throw error;
87204
}
88-
const response = await this.searchMessages($, params);
89-
const messages = response.results?.messages || [];
90-
matches.push(...messages);
91-
if (matches.length >= this.maxResults) {
92-
break;
93-
}
94-
cursor = response.response_metadata?.next_cursor;
95-
} while (cursor);
96-
97-
if (matches.length > this.maxResults) {
98-
matches.length = this.maxResults;
99205
}
100206

101207
$.export("$summary", `Found ${matches.length} matching message${matches.length === 1

components/slack/slack.app.mjs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,12 @@ export default {
884884
...args,
885885
});
886886
},
887+
assistantSearch(args = {}) {
888+
args.count ||= constants.LIMIT;
889+
return this.sdk().apiCall("assistant.search.context", {
890+
...args,
891+
});
892+
},
887893
/**
888894
* Lists reactions made by a user.
889895
* User Scopes: `reactions:read`

0 commit comments

Comments
 (0)