fix: refine slack uploadV2 payload (#1447) (thanks @jdrhyne)

This commit is contained in:
Peter Steinberger
2026-01-22 23:16:03 +00:00
parent 5022950664
commit 186fdf6b06
3 changed files with 54 additions and 3 deletions

View File

@@ -8,6 +8,7 @@ Docs: https://docs.clawd.bot
- BlueBubbles: stop typing indicator on idle/no-reply. (#1439) Thanks @Nicell.
- Auto-reply: only report a model switch when session state is available. (#1465) Thanks @robbyczgw-cla.
- Control UI: resolve local avatar URLs with basePath across injection + identity RPC. (#1457) Thanks @dlauer.
- Slack: remove deprecated `filetype` field from `files.uploadV2` to eliminate API warnings. (#1447) Thanks @jdrhyne.
- Agents: surface concrete API error details instead of generic AI service errors.
- Docs: fix gog auth services example to include docs scope. (#1454) Thanks @zerone0x.
@@ -15,7 +16,6 @@ Docs: https://docs.clawd.bot
### Fixes
- Control UI: ignore bootstrap identity placeholder text for avatar values and fall back to the default avatar. https://docs.clawd.bot/cli/agents https://docs.clawd.bot/web/control-ui
- Slack: remove deprecated `filetype` field from `files.uploadV2` to eliminate API warnings. (#1447)
## 2026.1.21

View File

@@ -88,13 +88,12 @@ async function uploadSlackFile(params: {
threadTs?: string;
maxBytes?: number;
}): Promise<string> {
const { buffer, contentType, fileName } = await loadWebMedia(params.mediaUrl, params.maxBytes);
const { buffer, fileName } = await loadWebMedia(params.mediaUrl, params.maxBytes);
const basePayload = {
channel_id: params.channelId,
file: buffer,
filename: fileName,
...(params.caption ? { initial_comment: params.caption } : {}),
// Note: filetype is deprecated in files.uploadV2, Slack auto-detects from file content
};
const payload: FilesUploadV2Arguments = params.threadTs
? { ...basePayload, thread_ts: params.threadTs }

View File

@@ -0,0 +1,52 @@
import type { WebClient } from "@slack/web-api";
import { describe, expect, it, vi } from "vitest";
import { loadConfig } from "../config/config.js";
import { loadWebMedia } from "../web/media.js";
import { sendMessageSlack } from "./send.js";
vi.mock("../config/config.js", () => ({
loadConfig: vi.fn(),
}));
vi.mock("../web/media.js", () => ({
loadWebMedia: vi.fn(),
}));
const loadConfigMock = vi.mocked(loadConfig);
const loadWebMediaMock = vi.mocked(loadWebMedia);
describe("slack send", () => {
it("omits filetype in files.uploadV2 payload", async () => {
loadConfigMock.mockReturnValue({ channels: { slack: {} } } as never);
loadWebMediaMock.mockResolvedValue({
buffer: Buffer.from("data"),
contentType: "image/png",
fileName: "test.png",
kind: "image",
});
const uploadV2 = vi.fn().mockResolvedValue({ files: [{ id: "F123" }] });
const postMessage = vi.fn().mockResolvedValue({ ts: "123.456" });
const client = {
files: { uploadV2 },
chat: { postMessage },
} as unknown as WebClient;
const result = await sendMessageSlack("channel:C123", "hello", {
mediaUrl: "https://example.com/test.png",
token: "xoxb-test",
client,
});
expect(uploadV2).toHaveBeenCalledTimes(1);
const payload = uploadV2.mock.calls[0]?.[0] as Record<string, unknown>;
expect(payload).toMatchObject({
channel_id: "C123",
filename: "test.png",
initial_comment: "hello",
});
expect("filetype" in payload).toBe(false);
expect(result).toEqual({ messageId: "F123", channelId: "C123" });
});
});