Level-Embeds aufgewertet, Server-Footer korrigiert
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled

Server-Monitor: Der Footer behauptete fest "alle 2 min", obwohl das Intervall
seit den einstellbaren Werten aus monitor_interval kommt. Wer im Panel etwas
anderes setzt, bekam eine falsche Angabe. Liest jetzt den echten Wert.

Level-Aufstieg: War als einziges im Community-Bereich reiner Text, waehrend
Geburtstage, Verlosungen und Bewerbungen laengst Embeds sind. Jetzt ueber
brandEmbed mit Avatar, Gesamt-XP und Rest bis zum naechsten Level. Der Text
bleibt die Vorlage aus dem Panel — nur der Rahmen ist neu.

Dazu zeigt das Embed die Belohnungs-Rolle: entweder die gerade vergebene oder
die naechste, auf die man hinspielt. grantRewards() gibt dafuer zurueck, was
wirklich dazukam.

Ist die Vorlage im Panel leer, wirft setDescription('') — der Aufstieg waere
still ausgefallen. Faellt jetzt auf eine schlichte Zeile zurueck.

/rank: Avatar als Thumbnail, Podest-Zeichen fuer die ersten drei, Rest-XP und
naechste Belohnung als eigene Felder. Das Feld erscheint nur, wenn es
Belohnungs-Rollen gibt — sonst stuende dort ein leeres Feld.

Nebenbei zwei veraltete ephemeral: true auf flags: MessageFlags.Ephemeral
umgestellt, wie es der Rest der Befehle schon macht.

Geprueft: alle vier Embeds wirklich gebaut und ausgegeben, discord.js
validiert dabei. Das Intervall im Footer zog den auf 7 gestellten Wert.
Die negative XP-Differenz im ersten Testlauf war ein inkonsistenter
Testdatensatz — mit 4200 XP ist man Level 9, nicht 7; ueber 0..60000 XP
durchgespielt wird die Differenz nie negativ.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-01 05:21:56 +02:00
co-authored by Claude Fable 5
parent 8fa43a3e47
commit 2d4afad3cb
3 changed files with 102 additions and 30 deletions
+40 -15
View File
@@ -1,8 +1,12 @@
// /rank — eigenes Level, XP-Fortschritt und Platzierung anzeigen
import { SlashCommandBuilder, EmbedBuilder } from 'discord.js';
import { SlashCommandBuilder, MessageFlags } from 'discord.js';
import { getLevelRow, rankOf } from '../../db.js';
import { xpForLevel } from '../levels.js';
import { levelsEnabled, publicUrl, brandColor, brandFooter } from '../../runtime-settings.js';
import { brandEmbed } from '../../embeds.js';
import { levelsEnabled, publicUrl, levelRewards } from '../../runtime-settings.js';
// Platz 13 bekommen ein Zeichen, danach schlicht die Nummer
const PODEST = ['🥇', '🥈', '🥉'];
export const data = new SlashCommandBuilder()
.setName('rank')
@@ -11,13 +15,19 @@ export const data = new SlashCommandBuilder()
export async function execute(interaction) {
if (!levelsEnabled()) {
await interaction.reply({ content: '❌ Das Level-System ist nicht aktiviert.', ephemeral: true });
await interaction.reply({
content: '❌ Das Level-System ist nicht aktiviert.',
flags: MessageFlags.Ephemeral,
});
return;
}
const user = interaction.options.getUser('member') ?? interaction.user;
const row = getLevelRow(user.id);
if (!row) {
await interaction.reply({ content: `**${user.username}** hat noch keine XP — einfach mitschreiben! ✍️`, ephemeral: true });
await interaction.reply({
content: `**${user.username}** hat noch keine XP — einfach mitschreiben! ✍️`,
flags: MessageFlags.Ephemeral,
});
return;
}
@@ -29,15 +39,30 @@ export async function execute(interaction) {
const barLen = 14;
const bar = '█'.repeat(Math.round((pct / 100) * barLen)).padEnd(barLen, '·');
await interaction.reply({
embeds: [
new EmbedBuilder()
.setColor(brandColor())
.setAuthor({ name: user.username, iconURL: user.displayAvatarURL({ size: 64 }) })
.setTitle(`Level ${row.level} · Platz #${rankOf(user.id)}`)
.setDescription(`\`${bar}\` ${pct}%\n**${progress} / ${needed} XP** bis Level ${row.level + 1} · gesamt ${row.xp} XP`)
.setURL(`${publicUrl()}/level`)
.setFooter({ text: brandFooter('LEVEL') }),
],
});
const platz = rankOf(user.id);
const zahl = (n) => n.toLocaleString('de-DE');
const naechste = levelRewards().find((r) => r.level > row.level) ?? null;
const embed = brandEmbed(interaction.client, 'LEVEL')
.setAuthor({ name: user.username, iconURL: user.displayAvatarURL({ size: 64 }) })
.setTitle(`${PODEST[platz - 1] ?? ''} Level ${row.level} · Platz #${platz}`.trim())
.setURL(`${publicUrl()}/level`)
.setThumbnail(user.displayAvatarURL({ size: 128 }))
.setDescription(`\`${bar}\` **${pct}%**`)
.addFields(
{ name: `Bis Level ${row.level + 1}`, value: `${zahl(needed - progress)} XP`, inline: true },
{ name: 'Gesamt', value: `${zahl(row.xp)} XP`, inline: true },
);
// Nur zeigen, was es auch gibt — ohne Belohnungs-Rollen bliebe hier ein
// leeres Feld stehen und das Embed sähe kaputt aus.
if (naechste) {
embed.addFields({
name: 'Nächste Belohnung',
value: `<@&${naechste.roleId}> ab Level ${naechste.level}`,
inline: true,
});
}
await interaction.reply({ embeds: [embed], allowedMentions: { parse: [] } });
}