The common problem with sending Slack messages using their NodeJS SDK is that you cannot attach multiple images to a chat.postMessage. The options available are:
None of these links the image with the original message blocks. Even just sending them as thread replies results with separate messages.
On top of that you cannot update a message with privately uploaded file. File needs to be uploaded to at least one channel.
Is there a way to create Slack post that looks like normal post with attachments?
The way I found is to attach blocks along with the uploading of the files using filesUploadV2 . None of the LLM tools was able to come up with this approach, but this at least gives you ability to provide nicely formatted BlockKit message along with image attachments.
export async function postMessageWithImages(
input: {
text: string,
images: { buffer: Buffer; filename: string; mimetype?: string; title?: string }[],
blocks: (KnownBlock | Block)[],
channel: string
}
): Promise<(FilesCompleteUploadExternalResponse | undefined)[]> {
const channelId = await resolveChannelId(input.channel);
if (!channelId) {
return [];
}
const result = await client.filesUploadV2({
blocks: input.blocks, // pass blocks here
channel_id: channelId,
file_uploads: input.images.map((image) => ({
file: image.buffer,
filename: image.filename,
filetype: image.mimetype,
title: image.title,
})),
})
if (!result.ok) {
const error = (result as any).error ?? "unknown_error";
throw new Error(`[Slack] files.uploadV2 failed: ${error}`);
}
logger.info("[Slack] Image files uploaded", {
files: result.files,
channelId,
});
return result.files;
}
and to get channel id:
async function resolveChannelId(channelName: string): Promise<string | undefined> {
const availableChannels = await client.conversations.list({
exclude_archived: true,
types: "public_channel,private_channel",
limit: 1000,
});
if (!availableChannels.ok) {
const error = (availableChannels as any).error ?? "unknown_error";
throw new Error(`[Slack] conversations.list failed: ${error}`);
}
const channel = availableChannels.channels?.find((ch) => ch.name === channelName);
if (!channel || !channel.id) {
logger.error("[Slack] Channel not found", { channelName });
return undefined;
}
return channel.id;
}