89 lines
2.6 KiB
TypeScript
89 lines
2.6 KiB
TypeScript
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<string, SlackThreadStarter>();
|
|
|
|
export async function resolveSlackThreadStarter(params: {
|
|
channelId: string;
|
|
threadTs: string;
|
|
client: SlackWebClient;
|
|
}): Promise<SlackThreadStarter | null> {
|
|
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;
|
|
}
|
|
}
|