@credal/actions 0.2.154 → 0.2.155
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.
- package/dist/actions/autogen/templates.js +24 -0
- package/dist/actions/autogen/types.d.ts +22 -4
- package/dist/actions/autogen/types.js +24 -0
- package/dist/actions/providers/slack/getChannelMessages.js +98 -2
- package/dist/actions/providers/slack/messageTransformers.d.ts +53 -0
- package/dist/actions/providers/slack/messageTransformers.js +267 -0
- package/dist/actions/providers/slackUser/searchSlack.d.ts +0 -26
- package/dist/actions/providers/slackUser/searchSlack.js +5 -171
- package/package.json +2 -2
|
@@ -686,6 +686,22 @@ export const slackGetChannelMessagesDefinition = {
|
|
|
686
686
|
type: "string",
|
|
687
687
|
description: "Only messages after this Unix timestamp will be included in results",
|
|
688
688
|
},
|
|
689
|
+
latest: {
|
|
690
|
+
type: "string",
|
|
691
|
+
description: "Only messages before this Unix timestamp will be included. Default is the current time. Use with oldest to create a time range.",
|
|
692
|
+
},
|
|
693
|
+
limit: {
|
|
694
|
+
type: "number",
|
|
695
|
+
description: "Maximum number of messages to return (1-999). Default is 100. Slack recommends no more than 200 results at a time for performance.",
|
|
696
|
+
},
|
|
697
|
+
cursor: {
|
|
698
|
+
type: "string",
|
|
699
|
+
description: "Pagination cursor from a previous response's next_cursor value. Use to navigate through large result sets.",
|
|
700
|
+
},
|
|
701
|
+
includeThreadReplies: {
|
|
702
|
+
type: "boolean",
|
|
703
|
+
description: "If true, includes all replies for messages that are part of a thread. Default is false.",
|
|
704
|
+
},
|
|
689
705
|
},
|
|
690
706
|
},
|
|
691
707
|
output: {
|
|
@@ -715,6 +731,14 @@ export const slackGetChannelMessagesDefinition = {
|
|
|
715
731
|
},
|
|
716
732
|
},
|
|
717
733
|
},
|
|
734
|
+
hasMore: {
|
|
735
|
+
type: "boolean",
|
|
736
|
+
description: "Indicates if there are more messages available beyond the current result set",
|
|
737
|
+
},
|
|
738
|
+
nextCursor: {
|
|
739
|
+
type: "string",
|
|
740
|
+
description: "Cursor to use for fetching the next page of results. Only present when hasMore is true.",
|
|
741
|
+
},
|
|
718
742
|
},
|
|
719
743
|
},
|
|
720
744
|
name: "getChannelMessages",
|
|
@@ -825,14 +825,26 @@ export declare const slackGetChannelMessagesParamsSchema: z.ZodObject<{
|
|
|
825
825
|
channelId: z.ZodOptional<z.ZodString>;
|
|
826
826
|
channelName: z.ZodOptional<z.ZodString>;
|
|
827
827
|
oldest: z.ZodString;
|
|
828
|
+
latest: z.ZodOptional<z.ZodString>;
|
|
829
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
830
|
+
cursor: z.ZodOptional<z.ZodString>;
|
|
831
|
+
includeThreadReplies: z.ZodOptional<z.ZodBoolean>;
|
|
828
832
|
}, "strip", z.ZodTypeAny, {
|
|
829
833
|
oldest: string;
|
|
830
834
|
channelName?: string | undefined;
|
|
831
835
|
channelId?: string | undefined;
|
|
836
|
+
latest?: string | undefined;
|
|
837
|
+
limit?: number | undefined;
|
|
838
|
+
cursor?: string | undefined;
|
|
839
|
+
includeThreadReplies?: boolean | undefined;
|
|
832
840
|
}, {
|
|
833
841
|
oldest: string;
|
|
834
842
|
channelName?: string | undefined;
|
|
835
843
|
channelId?: string | undefined;
|
|
844
|
+
latest?: string | undefined;
|
|
845
|
+
limit?: number | undefined;
|
|
846
|
+
cursor?: string | undefined;
|
|
847
|
+
includeThreadReplies?: boolean | undefined;
|
|
836
848
|
}>;
|
|
837
849
|
export type slackGetChannelMessagesParamsType = z.infer<typeof slackGetChannelMessagesParamsSchema>;
|
|
838
850
|
export declare const slackGetChannelMessagesOutputSchema: z.ZodObject<{
|
|
@@ -849,18 +861,24 @@ export declare const slackGetChannelMessagesOutputSchema: z.ZodObject<{
|
|
|
849
861
|
user: string;
|
|
850
862
|
ts: string;
|
|
851
863
|
}>, "many">;
|
|
864
|
+
hasMore: z.ZodOptional<z.ZodBoolean>;
|
|
865
|
+
nextCursor: z.ZodOptional<z.ZodString>;
|
|
852
866
|
}, "strip", z.ZodTypeAny, {
|
|
853
867
|
messages: {
|
|
854
868
|
text: string;
|
|
855
869
|
user: string;
|
|
856
870
|
ts: string;
|
|
857
871
|
}[];
|
|
872
|
+
hasMore?: boolean | undefined;
|
|
873
|
+
nextCursor?: string | undefined;
|
|
858
874
|
}, {
|
|
859
875
|
messages: {
|
|
860
876
|
text: string;
|
|
861
877
|
user: string;
|
|
862
878
|
ts: string;
|
|
863
879
|
}[];
|
|
880
|
+
hasMore?: boolean | undefined;
|
|
881
|
+
nextCursor?: string | undefined;
|
|
864
882
|
}>;
|
|
865
883
|
export type slackGetChannelMessagesOutputType = z.infer<typeof slackGetChannelMessagesOutputSchema>;
|
|
866
884
|
export type slackGetChannelMessagesFunction = ActionFunction<slackGetChannelMessagesParamsType, AuthParamsType, slackGetChannelMessagesOutputType>;
|
|
@@ -872,18 +890,18 @@ export declare const slackUserSearchSlackParamsSchema: z.ZodObject<{
|
|
|
872
890
|
limit: z.ZodDefault<z.ZodNumber>;
|
|
873
891
|
fetchAdjacentMessages: z.ZodDefault<z.ZodBoolean>;
|
|
874
892
|
}, "strip", z.ZodTypeAny, {
|
|
875
|
-
timeRange: "latest" | "today" | "yesterday" | "last_7d" | "last_30d" | "all";
|
|
876
893
|
limit: number;
|
|
894
|
+
timeRange: "latest" | "today" | "yesterday" | "last_7d" | "last_30d" | "all";
|
|
877
895
|
fetchAdjacentMessages: boolean;
|
|
878
896
|
emails?: string[] | undefined;
|
|
879
897
|
channel?: string | undefined;
|
|
880
898
|
topic?: string | undefined;
|
|
881
899
|
}, {
|
|
900
|
+
limit?: number | undefined;
|
|
882
901
|
emails?: string[] | undefined;
|
|
883
902
|
channel?: string | undefined;
|
|
884
903
|
topic?: string | undefined;
|
|
885
904
|
timeRange?: "latest" | "today" | "yesterday" | "last_7d" | "last_30d" | "all" | undefined;
|
|
886
|
-
limit?: number | undefined;
|
|
887
905
|
fetchAdjacentMessages?: boolean | undefined;
|
|
888
906
|
}>;
|
|
889
907
|
export type slackUserSearchSlackParamsType = z.infer<typeof slackUserSearchSlackParamsSchema>;
|
|
@@ -10348,6 +10366,7 @@ export declare const githubListCommitsOutputSchema: z.ZodObject<{
|
|
|
10348
10366
|
}, "strip", z.ZodTypeAny, {
|
|
10349
10367
|
success: boolean;
|
|
10350
10368
|
error?: string | undefined;
|
|
10369
|
+
hasMore?: boolean | undefined;
|
|
10351
10370
|
commits?: {
|
|
10352
10371
|
url: string;
|
|
10353
10372
|
sha: string;
|
|
@@ -10380,10 +10399,10 @@ export declare const githubListCommitsOutputSchema: z.ZodObject<{
|
|
|
10380
10399
|
}[] | undefined;
|
|
10381
10400
|
}[] | undefined;
|
|
10382
10401
|
totalCount?: number | undefined;
|
|
10383
|
-
hasMore?: boolean | undefined;
|
|
10384
10402
|
}, {
|
|
10385
10403
|
success: boolean;
|
|
10386
10404
|
error?: string | undefined;
|
|
10405
|
+
hasMore?: boolean | undefined;
|
|
10387
10406
|
commits?: {
|
|
10388
10407
|
url: string;
|
|
10389
10408
|
sha: string;
|
|
@@ -10416,7 +10435,6 @@ export declare const githubListCommitsOutputSchema: z.ZodObject<{
|
|
|
10416
10435
|
}[] | undefined;
|
|
10417
10436
|
}[] | undefined;
|
|
10418
10437
|
totalCount?: number | undefined;
|
|
10419
|
-
hasMore?: boolean | undefined;
|
|
10420
10438
|
}>;
|
|
10421
10439
|
export type githubListCommitsOutputType = z.infer<typeof githubListCommitsOutputSchema>;
|
|
10422
10440
|
export type githubListCommitsFunction = ActionFunction<githubListCommitsParamsType, AuthParamsType, githubListCommitsOutputType>;
|
|
@@ -266,6 +266,22 @@ export const slackGetChannelMessagesParamsSchema = z.object({
|
|
|
266
266
|
.describe("Name of the channel to summarize. Either the channelId or channelName must be provided.")
|
|
267
267
|
.optional(),
|
|
268
268
|
oldest: z.string().describe("Only messages after this Unix timestamp will be included in results"),
|
|
269
|
+
latest: z
|
|
270
|
+
.string()
|
|
271
|
+
.describe("Only messages before this Unix timestamp will be included. Default is the current time. Use with oldest to create a time range.")
|
|
272
|
+
.optional(),
|
|
273
|
+
limit: z
|
|
274
|
+
.number()
|
|
275
|
+
.describe("Maximum number of messages to return (1-999). Default is 100. Slack recommends no more than 200 results at a time for performance.")
|
|
276
|
+
.optional(),
|
|
277
|
+
cursor: z
|
|
278
|
+
.string()
|
|
279
|
+
.describe("Pagination cursor from a previous response's next_cursor value. Use to navigate through large result sets.")
|
|
280
|
+
.optional(),
|
|
281
|
+
includeThreadReplies: z
|
|
282
|
+
.boolean()
|
|
283
|
+
.describe("If true, includes all replies for messages that are part of a thread. Default is false.")
|
|
284
|
+
.optional(),
|
|
269
285
|
});
|
|
270
286
|
export const slackGetChannelMessagesOutputSchema = z.object({
|
|
271
287
|
messages: z
|
|
@@ -277,6 +293,14 @@ export const slackGetChannelMessagesOutputSchema = z.object({
|
|
|
277
293
|
})
|
|
278
294
|
.describe("A message in the channel"))
|
|
279
295
|
.describe("The messages in the channel"),
|
|
296
|
+
hasMore: z
|
|
297
|
+
.boolean()
|
|
298
|
+
.describe("Indicates if there are more messages available beyond the current result set")
|
|
299
|
+
.optional(),
|
|
300
|
+
nextCursor: z
|
|
301
|
+
.string()
|
|
302
|
+
.describe("Cursor to use for fetching the next page of results. Only present when hasMore is true.")
|
|
303
|
+
.optional(),
|
|
280
304
|
});
|
|
281
305
|
export const slackUserSearchSlackParamsSchema = z.object({
|
|
282
306
|
emails: z
|
|
@@ -10,12 +10,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
import { WebClient } from "@slack/web-api";
|
|
11
11
|
import { getSlackChannels } from "./helpers.js";
|
|
12
12
|
import { MISSING_AUTH_TOKEN } from "../../util/missingAuthConstants.js";
|
|
13
|
+
import { extractMessageText, transformToSlackMessage, simplifyFile, removeRedundantFields, } from "./messageTransformers.js";
|
|
13
14
|
const getChannelMessages = (_a) => __awaiter(void 0, [_a], void 0, function* ({ params, authParams, }) {
|
|
15
|
+
var _b;
|
|
14
16
|
if (!authParams.authToken) {
|
|
15
17
|
throw new Error(MISSING_AUTH_TOKEN);
|
|
16
18
|
}
|
|
17
19
|
const client = new WebClient(authParams.authToken);
|
|
18
|
-
const { channelId: inputChannelId, channelName, oldest } = params;
|
|
20
|
+
const { channelId: inputChannelId, channelName, oldest, latest, limit, cursor, includeThreadReplies } = params;
|
|
19
21
|
if (!inputChannelId && !channelName) {
|
|
20
22
|
throw Error("Either channelId or channelName must be provided");
|
|
21
23
|
}
|
|
@@ -31,12 +33,106 @@ const getChannelMessages = (_a) => __awaiter(void 0, [_a], void 0, function* ({
|
|
|
31
33
|
const messages = yield client.conversations.history({
|
|
32
34
|
channel: channelId,
|
|
33
35
|
oldest: oldest,
|
|
36
|
+
latest: latest,
|
|
37
|
+
limit: limit,
|
|
38
|
+
cursor: cursor,
|
|
34
39
|
});
|
|
35
40
|
if (!messages.ok) {
|
|
36
41
|
throw Error(`Failed to fetch messages from channel ${channelName}, channelId: ${channelId}`);
|
|
37
42
|
}
|
|
43
|
+
let processedMessages = messages.messages;
|
|
44
|
+
// Fetch thread replies if requested
|
|
45
|
+
if (includeThreadReplies) {
|
|
46
|
+
processedMessages = yield Promise.all(processedMessages.map((msg) => __awaiter(void 0, void 0, void 0, function* () {
|
|
47
|
+
// If message has replies, fetch them
|
|
48
|
+
if (msg.reply_count && msg.reply_count > 0 && msg.ts) {
|
|
49
|
+
try {
|
|
50
|
+
const threadReplies = yield client.conversations.replies({
|
|
51
|
+
channel: channelId,
|
|
52
|
+
ts: msg.ts,
|
|
53
|
+
limit: 20,
|
|
54
|
+
});
|
|
55
|
+
if (threadReplies.ok && threadReplies.messages) {
|
|
56
|
+
// Skip the first message (it's the parent) and process replies
|
|
57
|
+
const replies = threadReplies.messages.slice(1);
|
|
58
|
+
// Process each reply: extract text, simplify files, simplify reactions, remove redundant fields
|
|
59
|
+
msg.replies = replies.map(reply => {
|
|
60
|
+
// Extract text from reply
|
|
61
|
+
const replyTransformed = transformToSlackMessage(reply);
|
|
62
|
+
const replyExtractedText = extractMessageText(replyTransformed);
|
|
63
|
+
if (replyExtractedText) {
|
|
64
|
+
reply.extractedText = replyExtractedText;
|
|
65
|
+
}
|
|
66
|
+
// Simplify files in reply
|
|
67
|
+
if (reply.files && Array.isArray(reply.files)) {
|
|
68
|
+
reply.files = reply.files.map((file) => simplifyFile(file));
|
|
69
|
+
}
|
|
70
|
+
// Simplify reactions in reply
|
|
71
|
+
if (reply.reactions && Array.isArray(reply.reactions)) {
|
|
72
|
+
reply.reactions = reply.reactions.map((reaction) => ({
|
|
73
|
+
name: reaction.name,
|
|
74
|
+
count: reaction.count,
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
// Remove redundant fields from reply
|
|
78
|
+
removeRedundantFields(reply);
|
|
79
|
+
return reply;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
console.error(`Failed to fetch replies for message ${msg.ts}:`, error);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return msg;
|
|
88
|
+
})));
|
|
89
|
+
}
|
|
90
|
+
// Transform and enrich messages with extracted text
|
|
91
|
+
processedMessages = processedMessages.map(msg => {
|
|
92
|
+
// Transform to structured message format
|
|
93
|
+
const transformed = transformToSlackMessage(msg);
|
|
94
|
+
// Extract readable text from blocks/attachments
|
|
95
|
+
const extractedText = extractMessageText(transformed);
|
|
96
|
+
if (extractedText) {
|
|
97
|
+
msg.extractedText = extractedText;
|
|
98
|
+
}
|
|
99
|
+
// Simplify files first (to extract highest quality thumb before removing thumb fields)
|
|
100
|
+
if (msg.files && Array.isArray(msg.files)) {
|
|
101
|
+
msg.files = msg.files.map(file => simplifyFile(file));
|
|
102
|
+
}
|
|
103
|
+
// Simplify reactions (remove users array, keep just name and count)
|
|
104
|
+
if (msg.reactions && Array.isArray(msg.reactions)) {
|
|
105
|
+
msg.reactions = msg.reactions.map((reaction) => ({
|
|
106
|
+
name: reaction.name,
|
|
107
|
+
count: reaction.count,
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
// Process root field for thread_broadcast messages
|
|
111
|
+
if (msg.root && typeof msg.root === "object") {
|
|
112
|
+
const root = msg.root;
|
|
113
|
+
// Simplify files in root
|
|
114
|
+
if (root.files && Array.isArray(root.files)) {
|
|
115
|
+
root.files = root.files.map((file) => simplifyFile(file));
|
|
116
|
+
}
|
|
117
|
+
// Simplify reactions in root
|
|
118
|
+
if (root.reactions && Array.isArray(root.reactions)) {
|
|
119
|
+
root.reactions = root.reactions.map((reaction) => ({
|
|
120
|
+
name: reaction.name,
|
|
121
|
+
count: reaction.count,
|
|
122
|
+
}));
|
|
123
|
+
}
|
|
124
|
+
// Remove redundant fields from root
|
|
125
|
+
removeRedundantFields(root);
|
|
126
|
+
}
|
|
127
|
+
// Remove all redundant fields recursively from the entire message
|
|
128
|
+
// Note: replies are already processed when fetched if includeThreadReplies is true
|
|
129
|
+
removeRedundantFields(msg);
|
|
130
|
+
return msg;
|
|
131
|
+
});
|
|
38
132
|
return {
|
|
39
|
-
messages:
|
|
133
|
+
messages: processedMessages,
|
|
134
|
+
hasMore: messages.has_more,
|
|
135
|
+
nextCursor: (_b = messages.response_metadata) === null || _b === void 0 ? void 0 : _b.next_cursor,
|
|
40
136
|
};
|
|
41
137
|
});
|
|
42
138
|
export default getChannelMessages;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { KnownBlock } from "@slack/web-api";
|
|
2
|
+
import type { Attachment } from "@slack/web-api/dist/types/response/ChannelsHistoryResponse.js";
|
|
3
|
+
import type { MessageElement } from "@slack/web-api/dist/types/response/ConversationsHistoryResponse.js";
|
|
4
|
+
export interface SlackMessage {
|
|
5
|
+
ts?: string;
|
|
6
|
+
text?: string;
|
|
7
|
+
user?: string;
|
|
8
|
+
username?: string;
|
|
9
|
+
thread_ts?: string;
|
|
10
|
+
blocks?: KnownBlock[];
|
|
11
|
+
attachments?: Attachment[];
|
|
12
|
+
reactions?: Array<{
|
|
13
|
+
name: string;
|
|
14
|
+
count: number;
|
|
15
|
+
users: string[];
|
|
16
|
+
}>;
|
|
17
|
+
files?: Array<{
|
|
18
|
+
name?: string;
|
|
19
|
+
title?: string;
|
|
20
|
+
mimetype?: string;
|
|
21
|
+
url_private?: string;
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
export type SimplifiedFile = {
|
|
25
|
+
id: string;
|
|
26
|
+
created?: number;
|
|
27
|
+
timestamp?: number;
|
|
28
|
+
name?: string;
|
|
29
|
+
title?: string;
|
|
30
|
+
mimetype?: string;
|
|
31
|
+
file_access?: string;
|
|
32
|
+
highest_thumb?: string;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Get the highest quality thumbnail from a file
|
|
36
|
+
*/
|
|
37
|
+
export declare function getHighestQualityThumb(file: Record<string, unknown>): string | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* Simplify file objects to only include essential fields and highest quality thumbnail
|
|
40
|
+
*/
|
|
41
|
+
export declare function simplifyFile(file: Record<string, unknown>): SimplifiedFile;
|
|
42
|
+
/**
|
|
43
|
+
* Recursively remove redundant fields from an object to reduce payload size
|
|
44
|
+
*/
|
|
45
|
+
export declare function removeRedundantFields(obj: Record<string, unknown>): void;
|
|
46
|
+
/**
|
|
47
|
+
* Extracts all visible text from a Slack message
|
|
48
|
+
*/
|
|
49
|
+
export declare function extractMessageText(m: SlackMessage | undefined): string | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Transforms a Slack MessageElement to a simplified SlackMessage
|
|
52
|
+
*/
|
|
53
|
+
export declare function transformToSlackMessage(message: MessageElement): SlackMessage;
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the highest quality thumbnail from a file
|
|
3
|
+
*/
|
|
4
|
+
export function getHighestQualityThumb(file) {
|
|
5
|
+
const thumbSizes = [
|
|
6
|
+
"thumb_1024",
|
|
7
|
+
"thumb_960",
|
|
8
|
+
"thumb_800",
|
|
9
|
+
"thumb_720",
|
|
10
|
+
"thumb_480",
|
|
11
|
+
"thumb_360",
|
|
12
|
+
"thumb_160",
|
|
13
|
+
"thumb_80",
|
|
14
|
+
"thumb_64",
|
|
15
|
+
];
|
|
16
|
+
for (const size of thumbSizes) {
|
|
17
|
+
const thumb = file[size];
|
|
18
|
+
if (thumb && typeof thumb === "string") {
|
|
19
|
+
return thumb;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Simplify file objects to only include essential fields and highest quality thumbnail
|
|
26
|
+
*/
|
|
27
|
+
export function simplifyFile(file) {
|
|
28
|
+
const simplified = {
|
|
29
|
+
id: file.id,
|
|
30
|
+
created: file.created,
|
|
31
|
+
timestamp: file.timestamp,
|
|
32
|
+
name: file.name,
|
|
33
|
+
title: file.title,
|
|
34
|
+
mimetype: file.mimetype,
|
|
35
|
+
file_access: file.file_access,
|
|
36
|
+
};
|
|
37
|
+
const highestThumb = getHighestQualityThumb(file);
|
|
38
|
+
if (highestThumb) {
|
|
39
|
+
simplified.highest_thumb = highestThumb;
|
|
40
|
+
}
|
|
41
|
+
return simplified;
|
|
42
|
+
}
|
|
43
|
+
// Redundant fields to remove from messages - not useful (fills up context window so we can ditch these)
|
|
44
|
+
const REDUNDANT_FIELDS = [
|
|
45
|
+
// Image/media metadata
|
|
46
|
+
"app_icons",
|
|
47
|
+
"image_width",
|
|
48
|
+
"image_height",
|
|
49
|
+
"image_bytes",
|
|
50
|
+
"fallback",
|
|
51
|
+
"is_animated",
|
|
52
|
+
// Message metadata (not content-related)
|
|
53
|
+
"team",
|
|
54
|
+
"client_msg_id",
|
|
55
|
+
"is_locked",
|
|
56
|
+
"subscribed",
|
|
57
|
+
"display_as_bot",
|
|
58
|
+
"upload",
|
|
59
|
+
// Block metadata
|
|
60
|
+
"block_id",
|
|
61
|
+
"unicode", // Emoji unicode values (name is sufficient)
|
|
62
|
+
// File metadata (duplicates or not useful)
|
|
63
|
+
"file_access",
|
|
64
|
+
"filetype", // Redundant with mimetype
|
|
65
|
+
"pretty_type", // Redundant with mimetype
|
|
66
|
+
"thumb_tiny", // Base64 preview, not needed
|
|
67
|
+
// Attachment metadata
|
|
68
|
+
"service_icon", // Icon URLs for link previews
|
|
69
|
+
"from_url", // Duplicate of original_url
|
|
70
|
+
];
|
|
71
|
+
/**
|
|
72
|
+
* Recursively remove redundant fields from an object to reduce payload size
|
|
73
|
+
*/
|
|
74
|
+
export function removeRedundantFields(obj) {
|
|
75
|
+
for (const key of REDUNDANT_FIELDS) {
|
|
76
|
+
delete obj[key];
|
|
77
|
+
}
|
|
78
|
+
// Recursively clean nested objects and arrays
|
|
79
|
+
for (const key in obj) {
|
|
80
|
+
const value = obj[key];
|
|
81
|
+
if (value && typeof value === "object") {
|
|
82
|
+
if (Array.isArray(value)) {
|
|
83
|
+
value.forEach(item => {
|
|
84
|
+
if (item && typeof item === "object") {
|
|
85
|
+
removeRedundantFields(item);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
removeRedundantFields(value);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Extracts all visible text from a Slack message
|
|
97
|
+
*/
|
|
98
|
+
export function extractMessageText(m) {
|
|
99
|
+
var _a, _b, _c, _d;
|
|
100
|
+
if (!m)
|
|
101
|
+
return undefined;
|
|
102
|
+
const pieces = [];
|
|
103
|
+
// ---- Rich text helpers ----
|
|
104
|
+
const walkRichTextInline = (el) => {
|
|
105
|
+
var _a;
|
|
106
|
+
const blockPieces = [];
|
|
107
|
+
switch (el.type) {
|
|
108
|
+
case "text":
|
|
109
|
+
blockPieces.push(el.text);
|
|
110
|
+
break;
|
|
111
|
+
case "link":
|
|
112
|
+
blockPieces.push(el.text || el.url);
|
|
113
|
+
break;
|
|
114
|
+
case "user":
|
|
115
|
+
blockPieces.push(`<@${el.user_id}>`);
|
|
116
|
+
break;
|
|
117
|
+
case "channel":
|
|
118
|
+
blockPieces.push(`<#${el.channel_id}>`);
|
|
119
|
+
break;
|
|
120
|
+
case "emoji":
|
|
121
|
+
blockPieces.push(`:${el.name}:`);
|
|
122
|
+
break;
|
|
123
|
+
case "broadcast":
|
|
124
|
+
blockPieces.push(`@${el.range}`);
|
|
125
|
+
break;
|
|
126
|
+
case "date":
|
|
127
|
+
blockPieces.push((_a = el.fallback) !== null && _a !== void 0 ? _a : `<date:${el.timestamp}>`);
|
|
128
|
+
break;
|
|
129
|
+
case "team":
|
|
130
|
+
blockPieces.push(`<team:${el.team_id}>`);
|
|
131
|
+
break;
|
|
132
|
+
case "usergroup":
|
|
133
|
+
blockPieces.push(`<usergroup:${el.usergroup_id}>`);
|
|
134
|
+
break;
|
|
135
|
+
case "color":
|
|
136
|
+
// Usually formatting only, skip
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
return blockPieces;
|
|
140
|
+
};
|
|
141
|
+
const walkRichTextElement = (el) => {
|
|
142
|
+
const result = [];
|
|
143
|
+
switch (el.type) {
|
|
144
|
+
case "rich_text_section":
|
|
145
|
+
case "rich_text_quote":
|
|
146
|
+
result.push(el.elements.map(walkRichTextInline).join("\n"));
|
|
147
|
+
break;
|
|
148
|
+
case "rich_text_list":
|
|
149
|
+
result.push(el.elements.map(section => section.elements.map(walkRichTextInline).join("\n")).join("\n"));
|
|
150
|
+
break;
|
|
151
|
+
case "rich_text_preformatted":
|
|
152
|
+
result.push(el.elements.map(walkRichTextInline).join("\n"));
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
return result;
|
|
156
|
+
};
|
|
157
|
+
// ---- Block helpers ----
|
|
158
|
+
const walkBlock = (block) => {
|
|
159
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
160
|
+
const blockPieces = [];
|
|
161
|
+
switch (block.type) {
|
|
162
|
+
case "section":
|
|
163
|
+
if ((_a = block.text) === null || _a === void 0 ? void 0 : _a.text)
|
|
164
|
+
blockPieces.push(block.text.text);
|
|
165
|
+
if (block.fields) {
|
|
166
|
+
for (const f of block.fields)
|
|
167
|
+
if (f.text)
|
|
168
|
+
blockPieces.push(f.text);
|
|
169
|
+
}
|
|
170
|
+
if (block.accessory && "text" in block.accessory && block.accessory.text) {
|
|
171
|
+
blockPieces.push(block.accessory.text.text);
|
|
172
|
+
}
|
|
173
|
+
break;
|
|
174
|
+
case "context":
|
|
175
|
+
if (Array.isArray(block.elements)) {
|
|
176
|
+
block.elements.forEach(el => {
|
|
177
|
+
if ("text" in el && el.text)
|
|
178
|
+
blockPieces.push(el.text);
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
break;
|
|
182
|
+
case "header":
|
|
183
|
+
if ((_b = block.text) === null || _b === void 0 ? void 0 : _b.text)
|
|
184
|
+
blockPieces.push(block.text.text);
|
|
185
|
+
break;
|
|
186
|
+
case "rich_text":
|
|
187
|
+
blockPieces.push(block.elements.map(walkRichTextElement).join("\n"));
|
|
188
|
+
break;
|
|
189
|
+
case "markdown":
|
|
190
|
+
if (block.text)
|
|
191
|
+
blockPieces.push(block.text);
|
|
192
|
+
break;
|
|
193
|
+
case "video":
|
|
194
|
+
if ((_c = block.title) === null || _c === void 0 ? void 0 : _c.text)
|
|
195
|
+
blockPieces.push(block.title.text);
|
|
196
|
+
if ((_d = block.description) === null || _d === void 0 ? void 0 : _d.text)
|
|
197
|
+
blockPieces.push(block.description.text);
|
|
198
|
+
break;
|
|
199
|
+
case "image":
|
|
200
|
+
if ((_e = block.title) === null || _e === void 0 ? void 0 : _e.text)
|
|
201
|
+
blockPieces.push(block.title.text);
|
|
202
|
+
break;
|
|
203
|
+
case "input":
|
|
204
|
+
if ((_f = block.label) === null || _f === void 0 ? void 0 : _f.text)
|
|
205
|
+
blockPieces.push(block.label.text);
|
|
206
|
+
if ((_g = block.hint) === null || _g === void 0 ? void 0 : _g.text)
|
|
207
|
+
blockPieces.push(block.hint.text);
|
|
208
|
+
break;
|
|
209
|
+
// divider, file, actions, input don't contribute visible text
|
|
210
|
+
case "divider":
|
|
211
|
+
case "file":
|
|
212
|
+
case "actions":
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
return blockPieces;
|
|
216
|
+
};
|
|
217
|
+
let blockText = "";
|
|
218
|
+
if (Array.isArray(m.blocks)) {
|
|
219
|
+
const blockPieces = m.blocks.map(b => walkBlock(b));
|
|
220
|
+
blockText = blockPieces.join("\n");
|
|
221
|
+
}
|
|
222
|
+
if (blockText) {
|
|
223
|
+
pieces.push(blockText);
|
|
224
|
+
}
|
|
225
|
+
else if (m.text) {
|
|
226
|
+
pieces.push(m.text);
|
|
227
|
+
}
|
|
228
|
+
// 3. Attachments
|
|
229
|
+
if (m.attachments) {
|
|
230
|
+
for (const att of m.attachments) {
|
|
231
|
+
if (att.pretext)
|
|
232
|
+
pieces.push(att.pretext);
|
|
233
|
+
if (att.title)
|
|
234
|
+
pieces.push(att.title);
|
|
235
|
+
if (att.text)
|
|
236
|
+
pieces.push(att.text);
|
|
237
|
+
if (att.fields) {
|
|
238
|
+
for (const f of att.fields) {
|
|
239
|
+
const title = (_b = (_a = f.title) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : "";
|
|
240
|
+
const value = (_d = (_c = f.value) === null || _c === void 0 ? void 0 : _c.trim()) !== null && _d !== void 0 ? _d : "";
|
|
241
|
+
if (title || value) {
|
|
242
|
+
pieces.push(title && value ? `${title}: ${value}` : title || value);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// Deduplicate and join
|
|
249
|
+
const out = Array.from(new Set(pieces.map(s => s.trim()).filter(Boolean))).join("\n");
|
|
250
|
+
return out || undefined;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Transforms a Slack MessageElement to a simplified SlackMessage
|
|
254
|
+
*/
|
|
255
|
+
export function transformToSlackMessage(message) {
|
|
256
|
+
return {
|
|
257
|
+
ts: message.ts,
|
|
258
|
+
text: message.text,
|
|
259
|
+
user: message.user,
|
|
260
|
+
username: message.username,
|
|
261
|
+
thread_ts: message.thread_ts,
|
|
262
|
+
blocks: message.blocks,
|
|
263
|
+
attachments: message.attachments,
|
|
264
|
+
reactions: message.reactions,
|
|
265
|
+
files: message.files,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { type KnownBlock } from "@slack/web-api";
|
|
2
1
|
import { type slackUserSearchSlackFunction } from "../../autogen/types.js";
|
|
3
|
-
import type { Attachment } from "@slack/web-api/dist/types/response/ChannelsHistoryResponse.js";
|
|
4
2
|
export type TimeRange = "latest" | "today" | "yesterday" | "last_7d" | "last_30d" | "all";
|
|
5
3
|
export interface SlackSearchMessage {
|
|
6
4
|
channelId: string;
|
|
@@ -21,29 +19,5 @@ export interface SlackSearchMessage {
|
|
|
21
19
|
userName?: string;
|
|
22
20
|
}>;
|
|
23
21
|
}
|
|
24
|
-
interface SlackMessage {
|
|
25
|
-
ts?: string;
|
|
26
|
-
text?: string;
|
|
27
|
-
user?: string;
|
|
28
|
-
username?: string;
|
|
29
|
-
thread_ts?: string;
|
|
30
|
-
blocks?: KnownBlock[];
|
|
31
|
-
attachments?: Attachment[];
|
|
32
|
-
reactions?: Array<{
|
|
33
|
-
name: string;
|
|
34
|
-
count: number;
|
|
35
|
-
users: string[];
|
|
36
|
-
}>;
|
|
37
|
-
files?: Array<{
|
|
38
|
-
name?: string;
|
|
39
|
-
title?: string;
|
|
40
|
-
mimetype?: string;
|
|
41
|
-
url_private?: string;
|
|
42
|
-
}>;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Extracts all visible text from a Slack message
|
|
46
|
-
*/
|
|
47
|
-
export declare function extractMessageText(m: SlackMessage | undefined): string | undefined;
|
|
48
22
|
declare const searchSlack: slackUserSearchSlackFunction;
|
|
49
23
|
export default searchSlack;
|
|
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
import { WebClient } from "@slack/web-api";
|
|
11
11
|
import { MISSING_AUTH_TOKEN } from "../../util/missingAuthConstants.js";
|
|
12
12
|
import pLimit from "p-limit";
|
|
13
|
+
import { extractMessageText, transformToSlackMessage, simplifyFile, } from "../slack/messageTransformers.js";
|
|
13
14
|
/* ===================== Constants ===================== */
|
|
14
15
|
const HIT_ENRICH_POOL = 2; // keep concurrency conservative to avoid 429s
|
|
15
16
|
const limitHit = pLimit(HIT_ENRICH_POOL);
|
|
@@ -63,164 +64,6 @@ class SlackUserCache {
|
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
/* ===================== Helpers ===================== */
|
|
66
|
-
/* ===================== Helpers ===================== */
|
|
67
|
-
/**
|
|
68
|
-
* Extracts all visible text from a Slack message
|
|
69
|
-
*/
|
|
70
|
-
export function extractMessageText(m) {
|
|
71
|
-
var _a, _b, _c, _d;
|
|
72
|
-
if (!m)
|
|
73
|
-
return undefined;
|
|
74
|
-
const pieces = [];
|
|
75
|
-
// ---- Rich text helpers ----
|
|
76
|
-
const walkRichTextInline = (el) => {
|
|
77
|
-
var _a;
|
|
78
|
-
const blockPieces = [];
|
|
79
|
-
switch (el.type) {
|
|
80
|
-
case "text":
|
|
81
|
-
blockPieces.push(el.text);
|
|
82
|
-
break;
|
|
83
|
-
case "link":
|
|
84
|
-
blockPieces.push(el.text || el.url);
|
|
85
|
-
break;
|
|
86
|
-
case "user":
|
|
87
|
-
blockPieces.push(`<@${el.user_id}>`);
|
|
88
|
-
break;
|
|
89
|
-
case "channel":
|
|
90
|
-
blockPieces.push(`<#${el.channel_id}>`);
|
|
91
|
-
break;
|
|
92
|
-
case "emoji":
|
|
93
|
-
blockPieces.push(`:${el.name}:`);
|
|
94
|
-
break;
|
|
95
|
-
case "broadcast":
|
|
96
|
-
blockPieces.push(`@${el.range}`);
|
|
97
|
-
break;
|
|
98
|
-
case "date":
|
|
99
|
-
blockPieces.push((_a = el.fallback) !== null && _a !== void 0 ? _a : `<date:${el.timestamp}>`);
|
|
100
|
-
break;
|
|
101
|
-
case "team":
|
|
102
|
-
blockPieces.push(`<team:${el.team_id}>`);
|
|
103
|
-
break;
|
|
104
|
-
case "usergroup":
|
|
105
|
-
blockPieces.push(`<usergroup:${el.usergroup_id}>`);
|
|
106
|
-
break;
|
|
107
|
-
case "color":
|
|
108
|
-
// Usually formatting only, skip
|
|
109
|
-
break;
|
|
110
|
-
}
|
|
111
|
-
return blockPieces;
|
|
112
|
-
};
|
|
113
|
-
const walkRichTextElement = (el) => {
|
|
114
|
-
const result = [];
|
|
115
|
-
switch (el.type) {
|
|
116
|
-
case "rich_text_section":
|
|
117
|
-
case "rich_text_quote":
|
|
118
|
-
result.push(el.elements.map(walkRichTextInline).join("\n"));
|
|
119
|
-
break;
|
|
120
|
-
case "rich_text_list":
|
|
121
|
-
result.push(el.elements.map(section => section.elements.map(walkRichTextInline).join("\n")).join("\n"));
|
|
122
|
-
break;
|
|
123
|
-
case "rich_text_preformatted":
|
|
124
|
-
result.push(el.elements.map(walkRichTextInline).join("\n"));
|
|
125
|
-
break;
|
|
126
|
-
}
|
|
127
|
-
return result;
|
|
128
|
-
};
|
|
129
|
-
// ---- Block helpers ----
|
|
130
|
-
const walkBlock = (block) => {
|
|
131
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
132
|
-
const blockPieces = [];
|
|
133
|
-
switch (block.type) {
|
|
134
|
-
case "section":
|
|
135
|
-
if ((_a = block.text) === null || _a === void 0 ? void 0 : _a.text)
|
|
136
|
-
blockPieces.push(block.text.text);
|
|
137
|
-
if (block.fields) {
|
|
138
|
-
for (const f of block.fields)
|
|
139
|
-
if (f.text)
|
|
140
|
-
blockPieces.push(f.text);
|
|
141
|
-
}
|
|
142
|
-
if (block.accessory && "text" in block.accessory && block.accessory.text) {
|
|
143
|
-
blockPieces.push(block.accessory.text.text);
|
|
144
|
-
}
|
|
145
|
-
break;
|
|
146
|
-
case "context":
|
|
147
|
-
if (Array.isArray(block.elements)) {
|
|
148
|
-
block.elements.forEach(el => {
|
|
149
|
-
if ("text" in el && el.text)
|
|
150
|
-
blockPieces.push(el.text);
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
break;
|
|
154
|
-
case "header":
|
|
155
|
-
if ((_b = block.text) === null || _b === void 0 ? void 0 : _b.text)
|
|
156
|
-
blockPieces.push(block.text.text);
|
|
157
|
-
break;
|
|
158
|
-
case "rich_text":
|
|
159
|
-
blockPieces.push(block.elements.map(walkRichTextElement).join("\n"));
|
|
160
|
-
break;
|
|
161
|
-
case "markdown":
|
|
162
|
-
if (block.text)
|
|
163
|
-
blockPieces.push(block.text);
|
|
164
|
-
break;
|
|
165
|
-
case "video":
|
|
166
|
-
if ((_c = block.title) === null || _c === void 0 ? void 0 : _c.text)
|
|
167
|
-
blockPieces.push(block.title.text);
|
|
168
|
-
if ((_d = block.description) === null || _d === void 0 ? void 0 : _d.text)
|
|
169
|
-
blockPieces.push(block.description.text);
|
|
170
|
-
break;
|
|
171
|
-
case "image":
|
|
172
|
-
if ((_e = block.title) === null || _e === void 0 ? void 0 : _e.text)
|
|
173
|
-
blockPieces.push(block.title.text);
|
|
174
|
-
break;
|
|
175
|
-
case "input":
|
|
176
|
-
if ((_f = block.label) === null || _f === void 0 ? void 0 : _f.text)
|
|
177
|
-
blockPieces.push(block.label.text);
|
|
178
|
-
if ((_g = block.hint) === null || _g === void 0 ? void 0 : _g.text)
|
|
179
|
-
blockPieces.push(block.hint.text);
|
|
180
|
-
break;
|
|
181
|
-
// divider, file, actions, input don’t contribute visible text
|
|
182
|
-
case "divider":
|
|
183
|
-
case "file":
|
|
184
|
-
case "actions":
|
|
185
|
-
break;
|
|
186
|
-
}
|
|
187
|
-
return blockPieces;
|
|
188
|
-
};
|
|
189
|
-
let blockText = "";
|
|
190
|
-
if (Array.isArray(m.blocks)) {
|
|
191
|
-
const blockPieces = m.blocks.map(b => walkBlock(b));
|
|
192
|
-
blockText = blockPieces.join("\n");
|
|
193
|
-
}
|
|
194
|
-
if (blockText) {
|
|
195
|
-
pieces.push(blockText);
|
|
196
|
-
}
|
|
197
|
-
else if (m.text) {
|
|
198
|
-
pieces.push(m.text);
|
|
199
|
-
}
|
|
200
|
-
// 3. Attachments
|
|
201
|
-
if (m.attachments) {
|
|
202
|
-
for (const att of m.attachments) {
|
|
203
|
-
if (att.pretext)
|
|
204
|
-
pieces.push(att.pretext);
|
|
205
|
-
if (att.title)
|
|
206
|
-
pieces.push(att.title);
|
|
207
|
-
if (att.text)
|
|
208
|
-
pieces.push(att.text);
|
|
209
|
-
if (att.fields) {
|
|
210
|
-
for (const f of att.fields) {
|
|
211
|
-
const title = (_b = (_a = f.title) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : "";
|
|
212
|
-
const value = (_d = (_c = f.value) === null || _c === void 0 ? void 0 : _c.trim()) !== null && _d !== void 0 ? _d : "";
|
|
213
|
-
if (title || value) {
|
|
214
|
-
pieces.push(title && value ? `${title}: ${value}` : title || value);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
// Deduplicate and join
|
|
221
|
-
const out = Array.from(new Set(pieces.map(s => s.trim()).filter(Boolean))).join("\n");
|
|
222
|
-
return out || undefined;
|
|
223
|
-
}
|
|
224
67
|
function normalizeChannelOperand(ch) {
|
|
225
68
|
const s = ch.trim();
|
|
226
69
|
if (/^[CGD][A-Z0-9]/i.test(s))
|
|
@@ -297,19 +140,6 @@ function getPermalink(client, channel, ts) {
|
|
|
297
140
|
}
|
|
298
141
|
});
|
|
299
142
|
}
|
|
300
|
-
function transformToSlackMessage(message) {
|
|
301
|
-
return {
|
|
302
|
-
ts: message.ts,
|
|
303
|
-
text: message.text,
|
|
304
|
-
user: message.user,
|
|
305
|
-
username: message.username,
|
|
306
|
-
thread_ts: message.thread_ts,
|
|
307
|
-
blocks: message.blocks,
|
|
308
|
-
attachments: message.attachments,
|
|
309
|
-
reactions: message.reactions,
|
|
310
|
-
files: message.files,
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
143
|
function fetchOneMessage(client, channel, ts) {
|
|
314
144
|
return __awaiter(this, void 0, void 0, function* () {
|
|
315
145
|
var _a;
|
|
@@ -542,6 +372,10 @@ const searchSlack = (_a) => __awaiter(void 0, [_a], void 0, function* ({ params,
|
|
|
542
372
|
var _a;
|
|
543
373
|
const u = t.user ? yield cache.get(t.user) : undefined;
|
|
544
374
|
const rawText = extractMessageText(t);
|
|
375
|
+
// Simplify files to only include highest quality thumbnail
|
|
376
|
+
if (t.files && Array.isArray(t.files)) {
|
|
377
|
+
t.files = t.files.map((file) => simplifyFile(file));
|
|
378
|
+
}
|
|
545
379
|
// Build interaction description
|
|
546
380
|
const interactions = [];
|
|
547
381
|
if (t.reactions && t.reactions.length > 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@credal/actions",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.155",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AI Actions by Credal AI",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"json-schema-to-zod": "^2.5.0",
|
|
65
65
|
"jsonwebtoken": "^9.0.2",
|
|
66
66
|
"limiter": "^3.0.0",
|
|
67
|
-
"mammoth": "^1.
|
|
67
|
+
"mammoth": "^1.11.00",
|
|
68
68
|
"mongodb": "^6.13.1",
|
|
69
69
|
"node-forge": "^1.3.1",
|
|
70
70
|
"officeparser": "^5.2.0",
|