mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 14:00:47 +00:00
fix(slack): support exact message reads
This commit is contained in:
@@ -689,6 +689,29 @@ describe("handleSlackAction", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("passes messageId through to readSlackMessages", async () => {
|
||||
readSlackMessages.mockResolvedValueOnce({ messages: [], hasMore: false });
|
||||
|
||||
await handleSlackAction(
|
||||
{
|
||||
action: "readMessages",
|
||||
channelId: "C1",
|
||||
threadId: "1712345678.123456",
|
||||
messageId: "1712345678.654321",
|
||||
},
|
||||
slackConfig(),
|
||||
);
|
||||
|
||||
expect(readSlackMessages).toHaveBeenCalledWith(
|
||||
"C1",
|
||||
expect.objectContaining({
|
||||
cfg: expect.any(Object),
|
||||
threadId: "1712345678.123456",
|
||||
messageId: "1712345678.654321",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("adds normalized timestamps to pin payloads", async () => {
|
||||
listSlackPins.mockResolvedValueOnce([{ message: { ts: "1712345678.123456", text: "pin" } }]);
|
||||
|
||||
|
||||
@@ -366,12 +366,14 @@ export async function handleSlackAction(
|
||||
const before = readStringParam(params, "before");
|
||||
const after = readStringParam(params, "after");
|
||||
const threadId = readStringParam(params, "threadId");
|
||||
const messageId = readStringParam(params, "messageId");
|
||||
const result = await slackActionRuntime.readSlackMessages(channelId, {
|
||||
...readOpts,
|
||||
limit,
|
||||
before: before ?? undefined,
|
||||
after: after ?? undefined,
|
||||
threadId: threadId ?? undefined,
|
||||
messageId: messageId ?? undefined,
|
||||
});
|
||||
const messages = result.messages.map((message) =>
|
||||
withNormalizedTimestamp(
|
||||
|
||||
@@ -41,6 +41,35 @@ describe("readSlackMessages", () => {
|
||||
expect(result.messages.map((message) => message.ts)).toEqual(["171234.890", "171235.000"]);
|
||||
});
|
||||
|
||||
it("filters a specific thread reply by messageId", async () => {
|
||||
const client = createClient();
|
||||
client.conversations.replies.mockResolvedValueOnce({
|
||||
messages: [{ ts: "171234.567" }, { ts: "171234.890", text: "reply" }],
|
||||
has_more: true,
|
||||
});
|
||||
|
||||
const result = await readSlackMessages("C1", {
|
||||
client,
|
||||
threadId: "171234.567",
|
||||
messageId: "171234.890",
|
||||
limit: 20,
|
||||
token: "xoxb-test",
|
||||
});
|
||||
|
||||
expect(client.conversations.replies).toHaveBeenCalledWith({
|
||||
channel: "C1",
|
||||
ts: "171234.567",
|
||||
limit: 1,
|
||||
inclusive: true,
|
||||
latest: "171234.890",
|
||||
oldest: undefined,
|
||||
});
|
||||
expect(result).toEqual({
|
||||
messages: [{ ts: "171234.890", text: "reply" }],
|
||||
hasMore: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("uses conversations.history when threadId is missing", async () => {
|
||||
const client = createClient();
|
||||
client.conversations.history.mockResolvedValueOnce({
|
||||
@@ -63,4 +92,30 @@ describe("readSlackMessages", () => {
|
||||
expect(client.conversations.replies).not.toHaveBeenCalled();
|
||||
expect(result.messages.map((message) => message.ts)).toEqual(["1"]);
|
||||
});
|
||||
|
||||
it("filters a specific channel message by messageId", async () => {
|
||||
const client = createClient();
|
||||
client.conversations.history.mockResolvedValueOnce({
|
||||
messages: [{ ts: "171234.890", text: "exact" }, { ts: "171234.891" }],
|
||||
has_more: true,
|
||||
});
|
||||
|
||||
const result = await readSlackMessages("C1", {
|
||||
client,
|
||||
messageId: "171234.890",
|
||||
token: "xoxb-test",
|
||||
});
|
||||
|
||||
expect(client.conversations.history).toHaveBeenCalledWith({
|
||||
channel: "C1",
|
||||
limit: 1,
|
||||
inclusive: true,
|
||||
latest: "171234.890",
|
||||
oldest: undefined,
|
||||
});
|
||||
expect(result).toEqual({
|
||||
messages: [{ ts: "171234.890", text: "exact" }],
|
||||
hasMore: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -257,37 +257,55 @@ export async function readSlackMessages(
|
||||
before?: string;
|
||||
after?: string;
|
||||
threadId?: string;
|
||||
messageId?: string;
|
||||
} = {},
|
||||
): Promise<{ messages: SlackMessageSummary[]; hasMore: boolean }> {
|
||||
const client = await getClient(opts);
|
||||
const exactMessageId = opts.messageId?.trim();
|
||||
const readLimit = exactMessageId ? 1 : opts.limit;
|
||||
const exactBounds = exactMessageId
|
||||
? {
|
||||
inclusive: true,
|
||||
latest: exactMessageId,
|
||||
oldest: undefined,
|
||||
}
|
||||
: {
|
||||
latest: opts.before,
|
||||
oldest: opts.after,
|
||||
};
|
||||
|
||||
// Use conversations.replies for thread messages, conversations.history for channel messages.
|
||||
if (opts.threadId) {
|
||||
const result = await client.conversations.replies({
|
||||
channel: channelId,
|
||||
ts: opts.threadId,
|
||||
limit: opts.limit,
|
||||
latest: opts.before,
|
||||
oldest: opts.after,
|
||||
limit: readLimit,
|
||||
...exactBounds,
|
||||
});
|
||||
const messages = ((result.messages ?? []) as SlackMessageSummary[]).filter((message) => {
|
||||
if (exactMessageId) {
|
||||
return message.ts === exactMessageId;
|
||||
}
|
||||
// conversations.replies includes the parent message; drop it for replies-only reads.
|
||||
return message.ts !== opts.threadId;
|
||||
});
|
||||
return {
|
||||
// conversations.replies includes the parent message; drop it for replies-only reads.
|
||||
messages: (result.messages ?? []).filter(
|
||||
(message) => (message as SlackMessageSummary)?.ts !== opts.threadId,
|
||||
) as SlackMessageSummary[],
|
||||
hasMore: Boolean(result.has_more),
|
||||
messages,
|
||||
hasMore: exactMessageId ? false : Boolean(result.has_more),
|
||||
};
|
||||
}
|
||||
|
||||
const result = await client.conversations.history({
|
||||
channel: channelId,
|
||||
limit: opts.limit,
|
||||
latest: opts.before,
|
||||
oldest: opts.after,
|
||||
limit: readLimit,
|
||||
...exactBounds,
|
||||
});
|
||||
const messages = ((result.messages ?? []) as SlackMessageSummary[]).filter(
|
||||
(message) => !exactMessageId || message.ts === exactMessageId,
|
||||
);
|
||||
return {
|
||||
messages: (result.messages ?? []) as SlackMessageSummary[],
|
||||
hasMore: Boolean(result.has_more),
|
||||
messages,
|
||||
hasMore: exactMessageId ? false : Boolean(result.has_more),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -268,6 +268,7 @@ describe("slackPlugin actions", () => {
|
||||
params: {
|
||||
channelId: "C123",
|
||||
threadId: "1712345678.123456",
|
||||
messageId: "1712345678.654321",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -276,6 +277,7 @@ describe("slackPlugin actions", () => {
|
||||
action: "readMessages",
|
||||
channelId: "C123",
|
||||
threadId: "1712345678.123456",
|
||||
messageId: "1712345678.654321",
|
||||
}),
|
||||
{},
|
||||
undefined,
|
||||
|
||||
@@ -194,6 +194,32 @@ describe("handleSlackMessageAction", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("forwards messageId for read actions", async () => {
|
||||
const invoke = createInvokeSpy();
|
||||
|
||||
await handleSlackMessageAction({
|
||||
providerId: "slack",
|
||||
ctx: {
|
||||
action: "read",
|
||||
cfg: {},
|
||||
params: {
|
||||
channelId: "C1",
|
||||
messageId: "1712345678.654321",
|
||||
},
|
||||
} as never,
|
||||
invoke: invoke as never,
|
||||
});
|
||||
|
||||
expect(invoke).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
action: "readMessages",
|
||||
channelId: "C1",
|
||||
messageId: "1712345678.654321",
|
||||
}),
|
||||
{},
|
||||
);
|
||||
});
|
||||
|
||||
it("requires filePath, path, or media for upload-file", async () => {
|
||||
await expect(
|
||||
handleSlackMessageAction({
|
||||
|
||||
@@ -122,6 +122,7 @@ export async function handleSlackMessageAction(params: {
|
||||
limit,
|
||||
before: readStringParam(actionParams, "before"),
|
||||
after: readStringParam(actionParams, "after"),
|
||||
messageId: readStringParam(actionParams, "messageId"),
|
||||
accountId,
|
||||
};
|
||||
if (includeReadThreadId) {
|
||||
|
||||
Reference in New Issue
Block a user