import type { WebClient as SlackWebClient } from "@slack/web-api"; import type { FetchLike } from "../../media/fetch.js"; import { fetchRemoteMedia } from "../../media/fetch.js"; import { saveMediaBuffer } from "../../media/store.js"; import type { SlackFile } from "../types.js"; export async function resolveSlackMedia(params: { files?: SlackFile[]; token: string; maxBytes: number; }): Promise<{ path: string; contentType?: string; placeholder: string; } | null> { const files = params.files ?? []; for (const file of files) { const url = file.url_private_download ?? file.url_private; if (!url) continue; try { const fetchImpl: FetchLike = (input, init) => { const headers = new Headers(init?.headers); headers.set("Authorization", `Bearer ${params.token}`); return fetch(input, { ...init, headers }); }; const fetched = await fetchRemoteMedia({ url, fetchImpl, filePathHint: file.name, }); if (fetched.buffer.byteLength > params.maxBytes) continue; const saved = await saveMediaBuffer( fetched.buffer, fetched.contentType ?? file.mimetype, "inbound", params.maxBytes, ); const label = fetched.fileName ?? file.name; return { path: saved.path, contentType: saved.contentType, placeholder: label ? `[Slack file: ${label}]` : "[Slack file]", }; } catch { // Ignore download failures and fall through to the next file. } } return null; } export type SlackThreadStarter = { text: string; userId?: string; ts?: string; }; const THREAD_STARTER_CACHE = new Map(); export async function resolveSlackThreadStarter(params: { channelId: string; threadTs: string; client: SlackWebClient; }): Promise { const cacheKey = `${params.channelId}:${params.threadTs}`; const cached = THREAD_STARTER_CACHE.get(cacheKey); if (cached) return cached; try { const response = (await params.client.conversations.replies({ channel: params.channelId, ts: params.threadTs, limit: 1, inclusive: true, })) as { messages?: Array<{ text?: string; user?: string; ts?: string }> }; const message = response?.messages?.[0]; const text = (message?.text ?? "").trim(); if (!message || !text) return null; const starter: SlackThreadStarter = { text, userId: message.user, ts: message.ts, }; THREAD_STARTER_CACHE.set(cacheKey, starter); return starter; } catch { return null; } }