#!/usr/bin/env node // Devlog aus DIESEM Repo posten. // // tools/devlog.py im EcoGame-Repo geht dafür nicht: es zählt EcoGame-Commits, // bricht ohne solche ab, betitelt den Post als „Devlog — EcoGame" und löscht // die Bilder nach dem Senden. Dieses Skript zählt die Commits von d4rkbot, // lässt die Bilder liegen und kann vorher trocken laufen. // // Aufruf: // node tools/post-devlog.mjs --text docs/devlog-hub.md --images A:/eco/devlog-hub-bilder // node tools/post-devlog.mjs --text docs/devlog-hub.md --dry // // Die Webhook-Adresse (enthält das Secret) kommt aus der Umgebungsvariable // DEVLOG_WEBHOOK oder aus einer Datei — Standard ist die vorhandene // A:/eco/EcoGame/tools/.devlog_webhook. Sie wird nie ausgegeben. import { execFileSync } from 'node:child_process'; import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs'; import { basename, dirname, join, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; const REPO = resolve(dirname(fileURLToPath(import.meta.url)), '..'); const MAX_IMAGES = 4; const IMAGE_PATTERN = /\.(png|jpe?g|gif|webp)$/i; function arg(name, fallback = null) { const i = process.argv.indexOf(`--${name}`); return i !== -1 && process.argv[i + 1] && !process.argv[i + 1].startsWith('--') ? process.argv[i + 1] : fallback; } const has = (name) => process.argv.includes(`--${name}`); function webhookUrl() { const fromEnv = String(process.env.DEVLOG_WEBHOOK ?? '').trim(); if (fromEnv) return fromEnv; const file = arg('webhook-file', 'A:/eco/EcoGame/tools/.devlog_webhook'); if (!existsSync(file)) { console.error( `Keine Webhook-Adresse gefunden.\n` + ` DEVLOG_WEBHOOK setzen oder --webhook-file angeben.\n` + ` Gesucht wurde: ${file}` ); process.exit(1); } return readFileSync(file, 'utf8').trim(); } /** Commits dieses Repos seit — nur für die Fußzeile */ function commitCount(since) { try { const out = execFileSync('git', ['-C', REPO, 'log', `--since=${since}`, '--oneline', '--no-merges'], { encoding: 'utf8', }); return out.split('\n').filter((l) => l.trim()).length; } catch { return 0; } } function collectImages(dir) { if (!dir) return []; if (!existsSync(dir) || !statSync(dir).isDirectory()) { console.error(`Bilder-Ordner nicht gefunden: ${dir}`); process.exit(1); } const files = readdirSync(dir).filter((f) => IMAGE_PATTERN.test(f)).sort(); if (files.length > MAX_IMAGES) { console.warn(`Hinweis: ${files.length} Bilder gefunden, es werden die ersten ${MAX_IMAGES} gepostet.`); } return files.slice(0, MAX_IMAGES).map((f) => join(dir, f)); } const textFile = arg('text'); if (!textFile) { console.error('Bitte --text angeben (Markdown mit der Prosa).'); process.exit(1); } if (!existsSync(textFile)) { console.error(`Textdatei nicht gefunden: ${textFile}`); process.exit(1); } const prose = readFileSync(textFile, 'utf8').trim(); if (!prose) { console.error('Die Textdatei ist leer — nicht gepostet.'); process.exit(1); } const project = arg('project', 'd4rkbot'); const since = arg('since', 'yesterday'); const images = collectImages(arg('images')); const n = commitCount(since); const description = `${prose}\n\n*${n} Commit${n === 1 ? '' : 's'} heute — Details im [Repo](https://git.d4rkst3r.de/D4rkst3r/${project})*`; const payload = { embeds: [{ title: `Devlog — ${project}`, description, url: `https://git.d4rkst3r.de/D4rkst3r/${project}`, }], }; if (description.length > 3900) { console.warn(`Achtung: Text ist ${description.length} Zeichen — Discord kürzt ab 4096.`); } console.log(`Projekt: ${project}`); console.log(`Text: ${textFile} (${prose.length} Zeichen)`); console.log(`Commits: ${n} seit ${since}`); console.log(`Bilder: ${images.length ? images.map((i) => basename(i)).join(', ') : '— keine —'}`); if (has('dry')) { console.log('\n--- Vorschau (nichts gesendet) ---\n'); console.log(description); process.exit(0); } const form = new FormData(); // Als Textfeld anhängen, nicht als Blob: ein Blob käme als Datei-Teil an und // würde vom Endpoint zusammen mit den Nicht-Bildern verworfen. form.append('payload_json', JSON.stringify(payload)); for (const [i, file] of images.entries()) { form.append(`files[${i}]`, new Blob([readFileSync(file)]), basename(file)); } const res = await fetch(webhookUrl(), { method: 'POST', body: form }); if (!res.ok) { // Die Adresse enthält das Secret — deshalb nur Status und Antworttext zeigen console.error(`\nFehlgeschlagen: HTTP ${res.status}`); console.error(await res.text().catch(() => '')); process.exit(1); } console.log(`\nGepostet (HTTP ${res.status}). Die Bilder bleiben liegen.`);