@credal/actions 0.2.149 → 0.2.150
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.
|
@@ -14,9 +14,9 @@ export interface SlackSearchMessage {
|
|
|
14
14
|
userName?: string;
|
|
15
15
|
}>;
|
|
16
16
|
members?: Array<{
|
|
17
|
-
userId: string
|
|
18
|
-
userEmail
|
|
19
|
-
userName
|
|
17
|
+
userId: string;
|
|
18
|
+
userEmail?: string;
|
|
19
|
+
userName?: string;
|
|
20
20
|
}>;
|
|
21
21
|
}
|
|
22
22
|
declare const searchSlack: slackUserSearchSlackFunction;
|
|
@@ -11,7 +11,7 @@ import { WebClient } from "@slack/web-api";
|
|
|
11
11
|
import { MISSING_AUTH_TOKEN } from "../../util/missingAuthConstants.js";
|
|
12
12
|
import pLimit from "p-limit";
|
|
13
13
|
/* ===================== Constants ===================== */
|
|
14
|
-
const HIT_ENRICH_POOL =
|
|
14
|
+
const HIT_ENRICH_POOL = 2; // keep concurrency conservative to avoid 429s
|
|
15
15
|
const limitHit = pLimit(HIT_ENRICH_POOL);
|
|
16
16
|
const MENTION_USER_RE = /<@([UW][A-Z0-9]+)(?:\|[^>]+)?>/g;
|
|
17
17
|
const MENTION_CHANNEL_RE = /<#(C[A-Z0-9]+)(?:\|[^>]+)?>/g;
|
|
@@ -22,36 +22,39 @@ class SlackUserCache {
|
|
|
22
22
|
constructor(client) {
|
|
23
23
|
this.client = client;
|
|
24
24
|
this.cache = new Map();
|
|
25
|
-
this.
|
|
26
|
-
|
|
25
|
+
this.pending = new Map();
|
|
26
|
+
}
|
|
27
|
+
getSync(id) {
|
|
28
|
+
return this.cache.get(id);
|
|
27
29
|
}
|
|
28
30
|
get(id) {
|
|
29
31
|
return __awaiter(this, void 0, void 0, function* () {
|
|
30
32
|
const cached = this.cache.get(id);
|
|
31
33
|
if (cached)
|
|
32
34
|
return cached;
|
|
33
|
-
const pending = this.
|
|
35
|
+
const pending = this.pending.get(id);
|
|
34
36
|
if (pending)
|
|
35
37
|
return pending;
|
|
38
|
+
// fallback to users.info only if we have to
|
|
36
39
|
const promise = (() => __awaiter(this, void 0, void 0, function* () {
|
|
37
|
-
var _a, _b, _c, _d, _e
|
|
40
|
+
var _a, _b, _c, _d, _e;
|
|
38
41
|
try {
|
|
39
42
|
const res = yield this.client.users.info({ user: id });
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
if ((_a = res.user) === null || _a === void 0 ? void 0 : _a.id) {
|
|
44
|
+
const u = {
|
|
45
|
+
name: (_d = (_c = (_b = res.user.profile) === null || _b === void 0 ? void 0 : _b.display_name) !== null && _c !== void 0 ? _c : res.user.real_name) !== null && _d !== void 0 ? _d : res.user.name,
|
|
46
|
+
email: (_e = res.user.profile) === null || _e === void 0 ? void 0 : _e.email,
|
|
47
|
+
};
|
|
45
48
|
this.cache.set(id, u);
|
|
46
49
|
return u;
|
|
47
50
|
}
|
|
48
51
|
return undefined;
|
|
49
52
|
}
|
|
50
53
|
finally {
|
|
51
|
-
this.
|
|
54
|
+
this.pending.delete(id);
|
|
52
55
|
}
|
|
53
56
|
}))();
|
|
54
|
-
this.
|
|
57
|
+
this.pending.set(id, promise);
|
|
55
58
|
return promise;
|
|
56
59
|
});
|
|
57
60
|
}
|
|
@@ -89,7 +92,7 @@ function lookupUserIdsByEmail(client, emails, cache) {
|
|
|
89
92
|
return __awaiter(this, void 0, void 0, function* () {
|
|
90
93
|
const ids = [];
|
|
91
94
|
const settled = yield Promise.allSettled(emails.map((raw) => __awaiter(this, void 0, void 0, function* () {
|
|
92
|
-
var _a, _b, _c, _d, _e
|
|
95
|
+
var _a, _b, _c, _d, _e;
|
|
93
96
|
const email = raw.trim();
|
|
94
97
|
if (!email)
|
|
95
98
|
return null;
|
|
@@ -97,8 +100,8 @@ function lookupUserIdsByEmail(client, emails, cache) {
|
|
|
97
100
|
const id = (_a = res.user) === null || _a === void 0 ? void 0 : _a.id;
|
|
98
101
|
if (id && res.user) {
|
|
99
102
|
cache.set(id, {
|
|
100
|
-
name: (
|
|
101
|
-
email: (
|
|
103
|
+
name: (_d = (_c = (_b = res.user.profile) === null || _b === void 0 ? void 0 : _b.display_name) !== null && _c !== void 0 ? _c : res.user.real_name) !== null && _d !== void 0 ? _d : res.user.name,
|
|
104
|
+
email: (_e = res.user.profile) === null || _e === void 0 ? void 0 : _e.email,
|
|
102
105
|
});
|
|
103
106
|
}
|
|
104
107
|
return id !== null && id !== void 0 ? id : null;
|
|
@@ -146,7 +149,7 @@ function fetchOneMessage(client, channel, ts) {
|
|
|
146
149
|
function fetchThread(client, channel, threadTs) {
|
|
147
150
|
return __awaiter(this, void 0, void 0, function* () {
|
|
148
151
|
var _a;
|
|
149
|
-
const r = yield client.conversations.replies({ channel, ts: threadTs, limit:
|
|
152
|
+
const r = yield client.conversations.replies({ channel, ts: threadTs, limit: 20 });
|
|
150
153
|
return (_a = r.messages) !== null && _a !== void 0 ? _a : [];
|
|
151
154
|
});
|
|
152
155
|
}
|
|
@@ -157,10 +160,10 @@ function fetchContextWindow(client, channel, ts) {
|
|
|
157
160
|
const anchor = yield fetchOneMessage(client, channel, ts);
|
|
158
161
|
if (!anchor)
|
|
159
162
|
return out;
|
|
160
|
-
const before = yield client.conversations.history({ channel, latest: ts, inclusive: false, limit:
|
|
163
|
+
const before = yield client.conversations.history({ channel, latest: ts, inclusive: false, limit: 3 });
|
|
161
164
|
out.push(...((_a = before.messages) !== null && _a !== void 0 ? _a : []).reverse());
|
|
162
165
|
out.push(anchor);
|
|
163
|
-
const after = yield client.conversations.history({ channel, oldest: ts, inclusive: false, limit:
|
|
166
|
+
const after = yield client.conversations.history({ channel, oldest: ts, inclusive: false, limit: 3 });
|
|
164
167
|
out.push(...((_b = after.messages) !== null && _b !== void 0 ? _b : []));
|
|
165
168
|
return out;
|
|
166
169
|
});
|
|
@@ -170,26 +173,21 @@ function hasOverlap(messages, ids, minOverlap) {
|
|
|
170
173
|
const overlap = ids.filter(id => participants.has(id)).length;
|
|
171
174
|
return overlap >= minOverlap;
|
|
172
175
|
}
|
|
173
|
-
function expandSlackEntities(
|
|
176
|
+
function expandSlackEntities(cache, raw) {
|
|
174
177
|
return __awaiter(this, void 0, void 0, function* () {
|
|
175
178
|
let text = raw;
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
idToUser[id] = { name: u === null || u === void 0 ? void 0 : u.name };
|
|
179
|
+
const userIds = [...raw.matchAll(MENTION_USER_RE)].map(m => m[1]);
|
|
180
|
+
// resolve all in parallel: prefer cache, else users.info
|
|
181
|
+
const idToName = {};
|
|
182
|
+
yield Promise.all(userIds.map((id) => __awaiter(this, void 0, void 0, function* () {
|
|
183
|
+
const u = yield cache.get(id); // get() will call users.info if missing
|
|
184
|
+
if (u === null || u === void 0 ? void 0 : u.name)
|
|
185
|
+
idToName[id] = u.name;
|
|
184
186
|
})));
|
|
185
|
-
text = text.replace(MENTION_USER_RE, (_, id) => { var _a
|
|
186
|
-
// channels
|
|
187
|
+
text = text.replace(MENTION_USER_RE, (_, id) => { var _a; return `@${(_a = idToName[id]) !== null && _a !== void 0 ? _a : id}`; });
|
|
187
188
|
text = text.replace(MENTION_CHANNEL_RE, (_, id) => `#${id}`);
|
|
188
|
-
// special mentions
|
|
189
189
|
text = text.replace(SPECIAL_RE, (_, kind) => `@${kind}`);
|
|
190
|
-
// subteams
|
|
191
190
|
text = text.replace(SUBTEAM_RE, (_m, sid) => `@${sid}`);
|
|
192
|
-
// links
|
|
193
191
|
text = text.replace(/<([^>|]+)\|([^>]+)>/g, (_m, _url, label) => label);
|
|
194
192
|
text = text.replace(/<([^>|]+)>/g, (_m, url) => url);
|
|
195
193
|
return text;
|
|
@@ -247,96 +245,100 @@ const searchSlack = (_a) => __awaiter(void 0, [_a], void 0, function* ({ params,
|
|
|
247
245
|
const { user_id: myUserId } = yield client.auth.test();
|
|
248
246
|
if (!myUserId)
|
|
249
247
|
throw new Error("Failed to get my user ID.");
|
|
250
|
-
|
|
248
|
+
// preload myself
|
|
249
|
+
const meInfo = yield cache.get(myUserId);
|
|
250
|
+
// resolve targets by email
|
|
251
251
|
const targetIds = (emails === null || emails === void 0 ? void 0 : emails.length) ? yield lookupUserIdsByEmail(client, emails, cache) : [];
|
|
252
252
|
const filteredTargetIds = targetIds.filter(id => id !== myUserId);
|
|
253
253
|
const allMatches = [];
|
|
254
|
-
// --- Parallel search execution for better performance ---
|
|
255
254
|
const searchPromises = [];
|
|
256
|
-
// Scoped DM/MPIM searches
|
|
257
255
|
if (filteredTargetIds.length === 1) {
|
|
258
256
|
searchPromises.push(searchScoped({ client, scope: `<@${filteredTargetIds[0]}>`, topic, timeRange, limit }));
|
|
259
257
|
}
|
|
260
258
|
else if (filteredTargetIds.length >= 2) {
|
|
261
|
-
// Run MPIM lookup and individual DM searches in parallel
|
|
262
259
|
const searchMPIM = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
263
260
|
const mpimName = yield tryGetMPIMName(client, filteredTargetIds);
|
|
264
261
|
return mpimName ? searchScoped({ client, scope: mpimName, topic, timeRange, limit }) : [];
|
|
265
262
|
});
|
|
266
263
|
searchPromises.push(searchMPIM());
|
|
267
|
-
// Add individual DM searches
|
|
268
264
|
searchPromises.push(...filteredTargetIds.map(id => searchScoped({ client, scope: `<@${id}>`, topic, timeRange, limit })));
|
|
269
265
|
}
|
|
270
266
|
else if (channel) {
|
|
271
267
|
searchPromises.push(searchScoped({ client, scope: normalizeChannelOperand(channel), topic, timeRange, limit }));
|
|
272
268
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
269
|
+
if (topic) {
|
|
270
|
+
searchPromises.push(searchByTopic({ client, topic, timeRange, limit }));
|
|
271
|
+
}
|
|
276
272
|
const searchResults = yield Promise.all(searchPromises);
|
|
277
273
|
searchResults.forEach(matches => allMatches.push(...matches));
|
|
278
|
-
// --- Expand hits with context + filter overlap ---
|
|
279
|
-
// Create a channel info cache to avoid redundant API calls
|
|
280
274
|
const channelInfoCache = new Map();
|
|
281
275
|
const expanded = yield Promise.all(allMatches.map(m => limitHit(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
282
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
276
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
283
277
|
if (!m.ts || !((_a = m.channel) === null || _a === void 0 ? void 0 : _a.id))
|
|
284
278
|
return null;
|
|
285
279
|
const anchor = yield fetchOneMessage(client, m.channel.id, m.ts);
|
|
286
280
|
const rootTs = (anchor === null || anchor === void 0 ? void 0 : anchor.thread_ts) || m.ts;
|
|
287
|
-
|
|
288
|
-
// Check convo type (DM, MPIM, channel) with caching
|
|
281
|
+
// channel info
|
|
289
282
|
let channelInfo = channelInfoCache.get(m.channel.id);
|
|
290
283
|
if (!channelInfo) {
|
|
291
284
|
const convoInfo = yield client.conversations.info({ channel: m.channel.id });
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
285
|
+
const isIm = (_c = (_b = convoInfo.channel) === null || _b === void 0 ? void 0 : _b.is_im) !== null && _c !== void 0 ? _c : false;
|
|
286
|
+
const isMpim = (_e = (_d = convoInfo.channel) === null || _d === void 0 ? void 0 : _d.is_mpim) !== null && _e !== void 0 ? _e : false;
|
|
287
|
+
let members = [];
|
|
288
|
+
if (isIm || isMpim) {
|
|
289
|
+
const res = yield client.conversations.members({ channel: m.channel.id });
|
|
290
|
+
members = (_f = res.members) !== null && _f !== void 0 ? _f : [];
|
|
291
|
+
}
|
|
292
|
+
channelInfo = { isIm, isMpim, members };
|
|
296
293
|
channelInfoCache.set(m.channel.id, channelInfo);
|
|
297
294
|
}
|
|
298
|
-
|
|
295
|
+
// context + permalink
|
|
299
296
|
const [contextMsgs, permalink] = (anchor === null || anchor === void 0 ? void 0 : anchor.thread_ts)
|
|
300
|
-
? [
|
|
301
|
-
|
|
297
|
+
? [
|
|
298
|
+
yield fetchThread(client, m.channel.id, rootTs),
|
|
299
|
+
(_g = m.permalink) !== null && _g !== void 0 ? _g : (yield getPermalink(client, m.channel.id, rootTs)),
|
|
300
|
+
]
|
|
301
|
+
: [
|
|
302
|
+
yield fetchContextWindow(client, m.channel.id, m.ts),
|
|
303
|
+
(_h = m.permalink) !== null && _h !== void 0 ? _h : (yield getPermalink(client, m.channel.id, m.ts)),
|
|
304
|
+
];
|
|
305
|
+
// filter logic
|
|
302
306
|
let passesFilter = false;
|
|
303
|
-
if (isIm || isMpim) {
|
|
304
|
-
|
|
305
|
-
const membersRes = (_f = (yield client.conversations.members({ channel: m.channel.id })).members) !== null && _f !== void 0 ? _f : [];
|
|
306
|
-
members = yield Promise.all(membersRes.map((uid) => __awaiter(void 0, void 0, void 0, function* () {
|
|
307
|
-
const u = yield cache.get(uid);
|
|
308
|
-
return { userId: uid, userEmail: u === null || u === void 0 ? void 0 : u.email, userName: u === null || u === void 0 ? void 0 : u.name };
|
|
309
|
-
})));
|
|
310
|
-
const overlap = filteredTargetIds.filter(id => membersRes.includes(id)).length;
|
|
307
|
+
if (channelInfo.isIm || channelInfo.isMpim) {
|
|
308
|
+
const overlap = filteredTargetIds.filter(id => channelInfo.members.includes(id)).length;
|
|
311
309
|
passesFilter = overlap >= 1;
|
|
312
310
|
}
|
|
313
311
|
else {
|
|
314
|
-
// Channel: use authorship
|
|
315
312
|
passesFilter = hasOverlap(contextMsgs, filteredTargetIds, 1);
|
|
316
313
|
}
|
|
317
314
|
if (filteredTargetIds.length && !passesFilter)
|
|
318
315
|
return null;
|
|
319
316
|
const context = yield Promise.all(contextMsgs.map((t) => __awaiter(void 0, void 0, void 0, function* () {
|
|
320
|
-
var _a
|
|
321
|
-
|
|
317
|
+
var _a;
|
|
318
|
+
const u = t.user ? yield cache.get(t.user) : undefined;
|
|
319
|
+
return {
|
|
322
320
|
ts: t.ts,
|
|
323
|
-
text: t.text ? yield expandSlackEntities(
|
|
324
|
-
userEmail:
|
|
325
|
-
userName:
|
|
326
|
-
}
|
|
321
|
+
text: t.text ? yield expandSlackEntities(cache, t.text) : undefined,
|
|
322
|
+
userEmail: u === null || u === void 0 ? void 0 : u.email,
|
|
323
|
+
userName: (_a = u === null || u === void 0 ? void 0 : u.name) !== null && _a !== void 0 ? _a : t.username,
|
|
324
|
+
};
|
|
327
325
|
})));
|
|
326
|
+
const anchorUser = (anchor === null || anchor === void 0 ? void 0 : anchor.user) ? yield cache.get(anchor.user) : undefined;
|
|
328
327
|
return {
|
|
329
328
|
channelId: m.channel.id,
|
|
330
329
|
ts: rootTs,
|
|
331
|
-
text: (anchor === null || anchor === void 0 ? void 0 : anchor.text) ? yield expandSlackEntities(
|
|
332
|
-
userEmail:
|
|
333
|
-
userName: (
|
|
330
|
+
text: (anchor === null || anchor === void 0 ? void 0 : anchor.text) ? yield expandSlackEntities(cache, anchor.text) : undefined,
|
|
331
|
+
userEmail: anchorUser === null || anchorUser === void 0 ? void 0 : anchorUser.email,
|
|
332
|
+
userName: (_j = anchorUser === null || anchorUser === void 0 ? void 0 : anchorUser.name) !== null && _j !== void 0 ? _j : anchor === null || anchor === void 0 ? void 0 : anchor.username,
|
|
334
333
|
context,
|
|
335
|
-
permalink,
|
|
336
|
-
members
|
|
334
|
+
permalink: (_k = m.permalink) !== null && _k !== void 0 ? _k : permalink,
|
|
335
|
+
members: ((_l = channelInfo.members) !== null && _l !== void 0 ? _l : []).map(uid => {
|
|
336
|
+
const u = cache.getSync(uid);
|
|
337
|
+
return { userId: uid, userEmail: u === null || u === void 0 ? void 0 : u.email, userName: u === null || u === void 0 ? void 0 : u.name };
|
|
338
|
+
}),
|
|
337
339
|
};
|
|
338
340
|
}))));
|
|
339
|
-
const results = dedupeAndSort(expanded.filter(
|
|
341
|
+
const results = dedupeAndSort(expanded.filter(Boolean));
|
|
340
342
|
return {
|
|
341
343
|
query: topic !== null && topic !== void 0 ? topic : "",
|
|
342
344
|
results: results.map(r => ({
|
|
@@ -344,7 +346,7 @@ const searchSlack = (_a) => __awaiter(void 0, [_a], void 0, function* ({ params,
|
|
|
344
346
|
url: r.permalink || "",
|
|
345
347
|
contents: r,
|
|
346
348
|
})),
|
|
347
|
-
currentUser: { userId: myUserId, userName:
|
|
349
|
+
currentUser: { userId: myUserId, userName: meInfo === null || meInfo === void 0 ? void 0 : meInfo.name, userEmail: meInfo === null || meInfo === void 0 ? void 0 : meInfo.email },
|
|
348
350
|
};
|
|
349
351
|
});
|
|
350
352
|
export default searchSlack;
|