Files
d4rkbot/src/bot/bilder.js
T
D4rkst3randClaude Opus 5 b3758f1f76
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Screenshots zu Ideen, und der Bot legt die Forum-Tags selbst an
Zwei Fragen, zwei Antworten — eine davon ein Nein mit Umweg.

## Bilder im Eingabefenster: geht nicht

Discord-Modals koennen nur Textfelder. Kein Datei-Anhang, keine Umgehung — das
ist eine Grenze der Plattform, nicht unseres Codes.

Der Umweg ist besser als er klingt: Wer nach dem Einreichen einen Screenshot in
den Thread haengt, sieht ihn auf der Ideen-Seite. Die Bestaetigung nach dem
Absenden sagt das jetzt auch. Damit kann man auch nachtraeglich noch Bilder
dazulegen, was im Eingabefenster gar nicht ginge.

## Warum nicht einfach der Discord-Link

Discord haengt seit einiger Zeit eine Signatur an die Anhang-Links
(?ex=&is=&hm=), die nach etwa einem Tag ablaeuft. Ein gespeicherter Link zeigt
morgen ein kaputtes Bild. Deshalb laden Devlogs und Galerie seit dem ersten Tag
herunter — im Kopf von devlog-archive.js steht der Grund. Wuensche machen es
jetzt genauso, ausgeliefert unter /wish-assets/.

Beim Loeschen eines Beitrags oder eines ganzen Wunsches gehen die Dateien mit;
sonst waechst der Ordner mit Bildern, auf die nichts mehr zeigt.

Das Herunterladen stand vorher zweimal fast wortgleich im Code. Mit den
Wuenschen waeren es drei Kopien geworden — also einmal nach bot/bilder.js, und
Devlogs und Galerie ziehen mit um. Ein Fehler steckt jetzt an einer Stelle
statt an dreien.

## Forum-Tags per Knopf

Bisher musstest du sie von Hand anlegen. Jetzt macht es das Panel: die Bereiche
von dort plus die fuenf Staende, mit ihren Emojis. Vorhandene bleiben, auch
fremde — die Liste wird ergaenzt und nicht ersetzt. Discords Grenze von 20 Tags
wird eingehalten, was nicht mehr reinpasst, wird gemeldet statt still
verschluckt.

Geprueft: forumTagsAnlegen gegen eine Kanal-Attrappe — leeres Forum, teilweise
vorhandene, Gross/Kleinschreibung, fremde Tags, nichts zu tun (dann wird auch
nicht geschrieben), volles Forum, kein Forum, kein Kanal. Im Browser die
Ideen-Seite mit Bildern in einem Beitrag, einem Beitrag nur aus Bild ohne Text,
und der Lightbox darueber.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 00:17:01 +02:00

55 lines
2.1 KiB
JavaScript

// 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<string[]>} 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 });
}
}