Devlog-Seite: Bildergalerie, Projekt-Chips, Monats-Trenner
- Bild-Attachments werden beim Archivieren lokal gespeichert (Discord-CDN-URLs laufen ab) und unter /devlog-assets/ ausgeliefert; DB-Migration: images-Spalte - Frontend: Bildergalerie im Grid (responsive nach Anzahl), Embed-Titel als Projekt-Chip, Commit-Fußzeile abgesetzt, Monats-Trenner in der Timeline - Backfill lädt Bilder alter Devlogs nach (Dedupe verhindert Doppel-Downloads) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+2
-2
@@ -31,10 +31,10 @@ export async function startBot() {
|
||||
});
|
||||
|
||||
// Live-Archivierung: neue Devlogs (Webhook-Posts im Devlog-Kanal) sofort sichern
|
||||
client.on(Events.MessageCreate, (message) => {
|
||||
client.on(Events.MessageCreate, async (message) => {
|
||||
if (message.channelId !== config.devlogChannelId) return;
|
||||
try {
|
||||
if (archiveDevlogMessage(message)) {
|
||||
if (await archiveDevlogMessage(message)) {
|
||||
console.log(`[devlog] Neues Devlog archiviert (${message.id})`);
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -30,7 +30,7 @@ export async function execute(interaction) {
|
||||
if (message.webhookId) counts.webhook++;
|
||||
else if (message.author?.bot) counts.bot++;
|
||||
else counts.user++;
|
||||
if (archiveDevlogMessage(message)) saved++;
|
||||
if (await archiveDevlogMessage(message)) saved++;
|
||||
}
|
||||
before = batch.last().id;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
// Devlog-Archivierung: Webhook-Nachrichten aus dem Devlog-Kanal in SQLite sichern.
|
||||
// Bilder werden lokal gespeichert (Discord-CDN-Links laufen ab!) und vom
|
||||
// Webserver unter /devlog-assets/ ausgeliefert.
|
||||
// Wird vom Live-Listener (MessageCreate) und vom /devlog-backfill-Command genutzt.
|
||||
import { saveDevlog } from '../db.js';
|
||||
import { mkdirSync } from 'node:fs';
|
||||
import { writeFile } from 'node:fs/promises';
|
||||
import { join, dirname, resolve } from 'node:path';
|
||||
import { saveDevlog, hasDevlog } from '../db.js';
|
||||
import { config } from '../config.js';
|
||||
|
||||
const MAX_IMAGES = 4;
|
||||
|
||||
// Bilder liegen neben der SQLite: data/devlog_images/
|
||||
export const imagesDir = join(dirname(resolve(config.dbPath)), 'devlog_images');
|
||||
mkdirSync(imagesDir, { recursive: true });
|
||||
|
||||
/** Text aus einer Nachricht ziehen — Plain-Content plus Embed-Titel/-Beschreibungen */
|
||||
function extractContent(message) {
|
||||
@@ -15,12 +27,34 @@ function extractContent(message) {
|
||||
return parts.join('\n\n');
|
||||
}
|
||||
|
||||
/** Bild-Attachments herunterladen, gespeicherte Dateinamen zurückgeben */
|
||||
async function downloadImages(message) {
|
||||
const files = [];
|
||||
const attachments = [...(message.attachments?.values?.() ?? [])];
|
||||
for (const att of attachments) {
|
||||
if (files.length >= MAX_IMAGES) break;
|
||||
if (!att.contentType?.startsWith('image/')) continue;
|
||||
|
||||
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(imagesDir, file), Buffer.from(await res.arrayBuffer()));
|
||||
files.push(file);
|
||||
} catch (error) {
|
||||
console.error(`[devlog] Bild-Download fehlgeschlagen (${att.url}):`, error.message);
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Nachricht archivieren, falls sie ein Devlog ist (Webhook-Post mit Text).
|
||||
* Nachricht archivieren, falls sie ein Devlog ist (Webhook-/Bot-Post mit Text).
|
||||
* @param {import('discord.js').Message} message
|
||||
* @returns {boolean} true, wenn neu gespeichert
|
||||
* @returns {Promise<boolean>} true, wenn neu gespeichert
|
||||
*/
|
||||
export function archiveDevlogMessage(message) {
|
||||
export async function archiveDevlogMessage(message) {
|
||||
// Devlogs kommen von Webhooks oder fremden Bots/Apps (je nachdem, wie
|
||||
// devlog.py postet). User-Chatter und der EcoBot selbst werden ignoriert.
|
||||
const isWebhook = Boolean(message.webhookId);
|
||||
@@ -31,11 +65,17 @@ export function archiveDevlogMessage(message) {
|
||||
const content = extractContent(message);
|
||||
if (!content) return false;
|
||||
|
||||
// Schon archiviert? Dann Bild-Downloads sparen (Backfill ist wiederholbar)
|
||||
if (hasDevlog(message.id)) return false;
|
||||
|
||||
const images = await downloadImages(message);
|
||||
|
||||
return saveDevlog({
|
||||
message_id: message.id,
|
||||
channel_id: message.channelId,
|
||||
content,
|
||||
author_name: message.author?.username ?? null,
|
||||
posted_at: message.createdAt.toISOString(),
|
||||
images: JSON.stringify(images),
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user