mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:30:42 +00:00
fix(slack): forward media roots for uploads
This commit is contained in:
@@ -57,6 +57,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Slack/DMs: send text/block-only proactive DMs directly with `chat.postMessage(channel=<user id>)` while keeping conversation resolution for uploads and threaded sends. Fixes #62042. Thanks @MarkMolina.
|
||||
- Slack/routing: match route bindings written with Slack target syntax such as `channel:C...`, `user:U...`, or `<@U...>`, so bound Slack peers route to the configured agent instead of `main`. Fixes #41608. Thanks @Winnsolutionsadmin.
|
||||
- Slack/delivery: retry Slack Web API writes only when the SDK wraps a DNS request failure such as `EAI_AGAIN`, so transient resolver hiccups can recover without retrying platform errors that may duplicate messages. Fixes #68789. Thanks @sonnyb9.
|
||||
- Slack/message actions: forward agent-scoped media roots through the bundled upload-file action path, so workspace files can be attached without failing the local-media guard. Fixes #64625. Thanks @benpchandler.
|
||||
- Slack/mentions: resolve `<!subteam^...>` user-group mentions through Slack `usergroups.users.list` and treat them as explicit mentions only when the bot user is a member, so mention-gated agent channels wake for real user-group mentions without config-only allowlists. Fixes #73827. Thanks @CG-Intelligence-Agent-Jack.
|
||||
- Slack/message tool: let `read` fetch an exact Slack message timestamp, including a specific thread reply when paired with `threadId`, instead of returning only the parent thread or recent channel history. Fixes #53943. Thanks @zomars.
|
||||
- PDF/Gemini: send native PDF analysis API keys in the `x-goog-api-key` header instead of the request URL, keeping secrets out of proxy and access logs. Supersedes #60600. Thanks @garagon.
|
||||
|
||||
@@ -19,6 +19,21 @@ async function loadSlackActionRuntime() {
|
||||
return await slackActionRuntimePromise;
|
||||
}
|
||||
|
||||
function resolveSlackActionContext(params: {
|
||||
toolContext: unknown;
|
||||
mediaLocalRoots: readonly string[] | undefined;
|
||||
mediaReadFile: ((filePath: string) => Promise<Buffer>) | undefined;
|
||||
}): SlackActionContext | undefined {
|
||||
if (!params.toolContext && !params.mediaLocalRoots && !params.mediaReadFile) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
...(params.toolContext as SlackActionContext | undefined),
|
||||
...(params.mediaLocalRoots ? { mediaLocalRoots: params.mediaLocalRoots } : {}),
|
||||
...(params.mediaReadFile ? { mediaReadFile: params.mediaReadFile } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
export function createSlackActions(
|
||||
providerId: string,
|
||||
options?: { invoke?: SlackActionInvoke },
|
||||
@@ -32,14 +47,16 @@ export function createSlackActions(
|
||||
ctx,
|
||||
normalizeChannelId: resolveSlackChannelId,
|
||||
includeReadThreadId: true,
|
||||
invoke: async (action, cfg, toolContext) =>
|
||||
await (options?.invoke
|
||||
? options.invoke(action, cfg, toolContext)
|
||||
: (await loadSlackActionRuntime()).handleSlackAction(action, cfg, {
|
||||
...(toolContext as SlackActionContext | undefined),
|
||||
mediaLocalRoots: ctx.mediaLocalRoots,
|
||||
mediaReadFile: ctx.mediaReadFile,
|
||||
})),
|
||||
invoke: async (action, cfg, toolContext) => {
|
||||
const actionContext = resolveSlackActionContext({
|
||||
toolContext,
|
||||
mediaLocalRoots: ctx.mediaLocalRoots,
|
||||
mediaReadFile: ctx.mediaReadFile,
|
||||
});
|
||||
return await (options?.invoke
|
||||
? options.invoke(action, cfg, actionContext)
|
||||
: (await loadSlackActionRuntime()).handleSlackAction(action, cfg, actionContext));
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@@ -283,6 +283,47 @@ describe("slackPlugin actions", () => {
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it("forwards media access through the bundled Slack action invoke path", async () => {
|
||||
handleSlackActionMock.mockResolvedValueOnce({ ok: true });
|
||||
const handleAction = requireSlackHandleAction();
|
||||
const mediaLocalRoots = ["/tmp/workspace-agent"];
|
||||
const mediaReadFile = vi.fn(async () => Buffer.from("file"));
|
||||
|
||||
await handleAction({
|
||||
action: "upload-file",
|
||||
channel: "slack",
|
||||
accountId: "default",
|
||||
cfg: {},
|
||||
params: {
|
||||
to: "channel:C123",
|
||||
filePath: "/tmp/workspace-agent/renders/file.wav",
|
||||
initialComment: "render",
|
||||
},
|
||||
mediaLocalRoots,
|
||||
mediaReadFile,
|
||||
toolContext: {
|
||||
currentChannelId: "C123",
|
||||
replyToMode: "all",
|
||||
},
|
||||
} as never);
|
||||
|
||||
expect(handleSlackActionMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
action: "uploadFile",
|
||||
to: "channel:C123",
|
||||
filePath: "/tmp/workspace-agent/renders/file.wav",
|
||||
initialComment: "render",
|
||||
}),
|
||||
{},
|
||||
expect.objectContaining({
|
||||
currentChannelId: "C123",
|
||||
replyToMode: "all",
|
||||
mediaLocalRoots,
|
||||
mediaReadFile,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("slackPlugin status", () => {
|
||||
|
||||
Reference in New Issue
Block a user