Compare commits

...

3 Commits

Author SHA1 Message Date
Peter Steinberger
186fdf6b06 fix: refine slack uploadV2 payload (#1447) (thanks @jdrhyne) 2026-01-22 23:16:03 +00:00
Jonathan Rhyne
5022950664 docs: add changelog entry for PR #1447 2026-01-22 23:06:49 +00:00
Jonathan Rhyne
a685c7f399 fix(slack): remove deprecated filetype field from files.uploadV2
Slack's files.uploadV2 API no longer supports the filetype field and logs
deprecation warnings when it's included. Slack auto-detects the file type
from the file content, so this field is unnecessary.

This removes the warning:
[WARN] web-api:WebClient filetype is no longer a supported field in files.uploadV2.
2026-01-22 23:06:49 +00:00
3 changed files with 54 additions and 2 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.

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 } : {}),
...(contentType ? { filetype: contentType } : {}),
};
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" });
});
});