import { mkdir, writeFile } from "node:fs/promises"; import { dirname } from "node:path"; import { createHash } from "node:crypto"; import { mediaFiles } from "@cmbot/db"; import { newMediaPath, absoluteMediaPath } from "@cmbot/shared"; import { db } from "../db.js"; import { env } from "../env.js"; import { logger } from "../logger.js"; export type IngestInput = { operatorId: string; filenameOriginal: string; mimeType: string; buffer: Buffer; }; export type IngestResult = { mediaId: string; storagePath: string; }; export async function ingestMediaBuffer(input: IngestInput): Promise { const sha256 = createHash("sha256").update(input.buffer).digest("hex"); const storagePath = newMediaPath(input.filenameOriginal); const absolute = absoluteMediaPath(storagePath, env.MEDIA_DIR); await mkdir(dirname(absolute), { recursive: true }); await writeFile(absolute, input.buffer); const [row] = await db .insert(mediaFiles) .values({ operatorId: input.operatorId, filenameOriginal: input.filenameOriginal, mimeType: input.mimeType, sizeBytes: input.buffer.byteLength, sha256, storagePath, }) .returning({ id: mediaFiles.id }); logger.info( { mediaId: row!.id, sizeBytes: input.buffer.byteLength, sha256 }, "media: ingested", ); return { mediaId: row!.id, storagePath }; } /** * Download a Telegram file by file_id and ingest it. Returns the new media row. */ export async function ingestTelegramFile( operatorId: string, apiBase: string, botToken: string, fileId: string, defaultFilename: string, mimeType: string, ): Promise { // 1. getFile — Telegram returns a file_path const getFileUrl = `${apiBase}/bot${botToken}/getFile?file_id=${encodeURIComponent(fileId)}`; const getFileRes = await fetch(getFileUrl); if (!getFileRes.ok) { throw new Error(`Telegram getFile failed: ${getFileRes.status} ${getFileRes.statusText}`); } const getFileJson = (await getFileRes.json()) as { ok: boolean; result?: { file_path?: string }; }; if (!getFileJson.ok || !getFileJson.result?.file_path) { throw new Error("Telegram getFile: missing file_path in response"); } // 2. Download bytes const downloadUrl = `${apiBase}/file/bot${botToken}/${getFileJson.result.file_path}`; const dl = await fetch(downloadUrl); if (!dl.ok) { throw new Error(`Telegram file download failed: ${dl.status} ${dl.statusText}`); } const buffer = Buffer.from(await dl.arrayBuffer()); // The Telegram-side filename can be missing; fall back to defaultFilename. const filename = getFileJson.result.file_path.split("/").pop() ?? defaultFilename; return ingestMediaBuffer({ operatorId, filenameOriginal: filename, mimeType, buffer, }); }