From 186fdf6b0616a3080232cc5bbc213944924f3a2c Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 22 Jan 2026 23:16:03 +0000 Subject: [PATCH] fix: refine slack uploadV2 payload (#1447) (thanks @jdrhyne) --- CHANGELOG.md | 2 +- src/slack/send.ts | 3 +- .../send.uploadV2-omits-filetype.test.ts | 52 +++++++++++++++++++ 3 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 src/slack/send.uploadV2-omits-filetype.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 305150cea4..41f098cdd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/slack/send.ts b/src/slack/send.ts index cfc6c47072..1dba06c24a 100644 --- a/src/slack/send.ts +++ b/src/slack/send.ts @@ -88,13 +88,12 @@ async function uploadSlackFile(params: { threadTs?: string; maxBytes?: number; }): Promise { - 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 } diff --git a/src/slack/send.uploadV2-omits-filetype.test.ts b/src/slack/send.uploadV2-omits-filetype.test.ts new file mode 100644 index 0000000000..620889791b --- /dev/null +++ b/src/slack/send.uploadV2-omits-filetype.test.ts @@ -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; + 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" }); + }); +});