// Bilder aus Discord-Nachrichten lokal sichern. // // Discord-CDN-Links laufen ab — wer sie nur speichert, hat nach ein paar Wochen // eine Seite voller kaputter Vorschaubilder. Deshalb wird heruntergeladen und // vom Webserver selbst ausgeliefert. // // Stand vorher dreimal fast gleich im Code (Devlogs, Galerie, jetzt Wünsche); // hier einmal, damit ein Fehler auch nur einmal drinsteckt. import { mkdirSync } from 'node:fs'; import { writeFile, rm } from 'node:fs/promises'; import { join, dirname, resolve } from 'node:path'; import { config } from '../config.js'; /** Ordner neben der SQLite anlegen und zurückgeben */ export function bildOrdner(name) { const dir = join(dirname(resolve(config.dbPath)), name); mkdirSync(dir, { recursive: true }); return dir; } /** * Bild-Anhänge einer Nachricht herunterladen. * * @param {import('discord.js').Message} message * @param {{ dir: string, max?: number, tag?: string }} opts * @returns {Promise} gespeicherte Dateinamen */ export async function bilderSichern(message, { dir, max = 4, tag = 'bilder' }) { const files = []; for (const att of [...(message.attachments?.values?.() ?? [])]) { if (files.length >= max) break; if (!att.contentType?.startsWith('image/')) continue; // Endung säubern: der Name kommt vom Hochladenden und landet im Dateisystem const ext = (att.name?.split('.').pop() || 'png').toLowerCase().replace(/[^a-z0-9]/g, '') || 'png'; const file = `${message.id}_${files.length}.${ext}`; try { const res = await fetch(att.url); if (!res.ok) continue; await writeFile(join(dir, file), Buffer.from(await res.arrayBuffer())); files.push(file); } catch (error) { console.error(`[${tag}] Bild-Download fehlgeschlagen (${att.url}):`, error.message); } } return files; } /** Gespeicherte Bilder wieder wegräumen */ export async function bilderLoeschen(dir, files) { for (const file of files ?? []) { await rm(join(dir, file), { force: true }); } }