Willkommens-Karten, Geburtstage, Devlog-Permalinks mit OG-Vorschau, Auto-Publish
- Willkommens-Karten: gerendertes PNG (SVG → sharp) mit Avatar im Neon-Ring, Willkommens-Schriftzug und Member-Nummer; Fallback aufs bisherige Embed wenn das Rendering scheitert; Dockerfile installiert fonts-dejavu-core für die Text-Darstellung - Geburtstags-System: /geburtstag setzen|entfernen (birthdays-Tabelle), tägliche Runde ab 09:00 Europe/Berlin (Doppel-Post-Schutz über last_birthday_run), Gratulations-Embed + Tages-Rolle (wird am nächsten Morgen wieder abgeräumt); Kanal + Rolle im Community-Tab - Devlog-Permalinks: /devlogs/:id als eigene Seite (GET /api/devlogs/:id), Link-Symbol an jeder Karte, Link-kopieren-Button; der Server injiziert Open-Graph-Tags ins SPA-HTML — Devlog-Links zeigen Titel, Anriss und Bild, alle anderen Seiten bekommen Default-Tags (auch die Startseite) - Auto-Publish: maybeCrosspost() veröffentlicht Devlog-, Release-, Composer- und geplante Posts automatisch in Ankündigungs-Kanälen - brandEmbed: leere Avatar-URL crasht nicht mehr die Footer-Validierung Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
// Geburtstags-Runde: einmal täglich ab 09:00 (Europe/Berlin) gratulieren
|
||||
// und die Tages-Rolle umhängen (gestern Geburtstag → Rolle wieder weg).
|
||||
import { getSetting, setSetting, birthdaysToday } from '../db.js';
|
||||
import { birthdayChannelId, birthdayRoleId, discordGuildId, brandColor2 } from '../runtime-settings.js';
|
||||
import { brandEmbed } from '../embeds.js';
|
||||
|
||||
const CHECK_INTERVAL_MS = 15 * 60 * 1000;
|
||||
|
||||
/** Aktuelles Datum/Stunde in Europe/Berlin */
|
||||
function berlinNow() {
|
||||
const parts = new Intl.DateTimeFormat('de-DE', {
|
||||
timeZone: 'Europe/Berlin', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', hour12: false,
|
||||
}).formatToParts(new Date());
|
||||
const get = (type) => Number(parts.find((p) => p.type === type)?.value);
|
||||
return { day: get('day'), month: get('month'), hour: get('hour'), dateKey: `${get('year')}-${get('month')}-${get('day')}` };
|
||||
}
|
||||
|
||||
export async function birthdayTick(client) {
|
||||
const { day, month, hour, dateKey } = berlinNow();
|
||||
if (hour < 9) return; // erst ab 09:00
|
||||
if (getSetting('last_birthday_run') === dateKey) return; // heute schon gelaufen
|
||||
setSetting('last_birthday_run', dateKey);
|
||||
|
||||
const guildId = discordGuildId() ?? [...client.guilds.cache.keys()][0];
|
||||
const guild = guildId ? await client.guilds.fetch(guildId).catch(() => null) : null;
|
||||
const kids = birthdaysToday(day, month);
|
||||
|
||||
// Tages-Rolle: erst bei allen abräumen, dann den heutigen geben
|
||||
const roleId = birthdayRoleId();
|
||||
if (guild && roleId) {
|
||||
const role = await guild.roles.fetch(roleId).catch(() => null);
|
||||
if (role) {
|
||||
for (const member of role.members.values()) {
|
||||
await member.roles.remove(roleId).catch(() => {});
|
||||
}
|
||||
for (const b of kids) {
|
||||
const member = await guild.members.fetch(b.user_id).catch(() => null);
|
||||
await member?.roles.add(roleId).catch(() => {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (kids.length === 0) return;
|
||||
const channelId = birthdayChannelId();
|
||||
if (!channelId) return;
|
||||
const channel = await client.channels.fetch(channelId).catch(() => null);
|
||||
if (!channel?.isTextBased()) return;
|
||||
|
||||
const mentions = kids.map((b) => `<@${b.user_id}>`).join(' ');
|
||||
await channel.send({
|
||||
content: mentions,
|
||||
embeds: [
|
||||
brandEmbed(client, 'GEBURTSTAG')
|
||||
.setColor(brandColor2())
|
||||
.setTitle(kids.length === 1 ? '🎂 Alles Gute zum Geburtstag!' : '🎂 Heute wird gleich mehrfach gefeiert!')
|
||||
.setDescription(
|
||||
`${mentions} ${kids.length === 1 ? 'hat' : 'haben'} heute Geburtstag — ` +
|
||||
'lasst mal ordentlich 🎉 da!'
|
||||
),
|
||||
],
|
||||
allowedMentions: { users: kids.map((b) => b.user_id) },
|
||||
}).catch(() => {});
|
||||
console.log(`[birthday] ${kids.length} Gratulation(en) für den ${day}.${month}.`);
|
||||
}
|
||||
|
||||
export function startBirthdays(client) {
|
||||
setInterval(() => birthdayTick(client).catch((e) => console.error('[birthday]', e)), CHECK_INTERVAL_MS);
|
||||
birthdayTick(client).catch(() => {});
|
||||
}
|
||||
Reference in New Issue
Block a user