Level-Embeds aufgewertet, Server-Footer korrigiert
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:
+56
-14
@@ -4,6 +4,7 @@ import { Events } from 'discord.js';
|
||||
import { getLevelRow, setLevelRow } from '../db.js';
|
||||
import { levelsEnabled, levelsAnnounce, levelRewards } from '../runtime-settings.js';
|
||||
import { renderTemplate } from '../templates.js';
|
||||
import { brandEmbed } from '../embeds.js';
|
||||
import { tuningMs, xpRange } from '../tuning.js';
|
||||
|
||||
/** Kumulierte XP-Schwelle, ab der ein Level erreicht ist (MEE6-Formel pro Stufe) */
|
||||
@@ -35,16 +36,28 @@ export function awardXp(userId, username, now = Date.now()) {
|
||||
return { xp, level, leveledUp };
|
||||
}
|
||||
|
||||
/** Beim Level-Up fällige Belohnungs-Rollen (alle Schwellen ≤ Level) vergeben */
|
||||
/**
|
||||
* Beim Level-Up fällige Belohnungs-Rollen (alle Schwellen ≤ Level) vergeben.
|
||||
* @returns {Promise<string[]>} Rollen-IDs, die neu dazukamen — fürs Embed
|
||||
*/
|
||||
async function grantRewards(member, level) {
|
||||
const neu = [];
|
||||
for (const reward of levelRewards()) {
|
||||
if (reward.level > level) break;
|
||||
if (!member.roles.cache.has(reward.roleId)) {
|
||||
await member.roles.add(reward.roleId).catch((e) =>
|
||||
console.error(`[level] Belohnungs-Rolle ${reward.roleId} fehlgeschlagen: ${e.message}`)
|
||||
);
|
||||
const ok = await member.roles.add(reward.roleId).then(() => true).catch((e) => {
|
||||
console.error(`[level] Belohnungs-Rolle ${reward.roleId} fehlgeschlagen: ${e.message}`);
|
||||
return false;
|
||||
});
|
||||
if (ok) neu.push(reward.roleId);
|
||||
}
|
||||
}
|
||||
return neu;
|
||||
}
|
||||
|
||||
/** Nächste Belohnungs-Stufe oberhalb des aktuellen Levels (oder null) */
|
||||
function naechsteBelohnung(level) {
|
||||
return levelRewards().find((r) => r.level > level) ?? null;
|
||||
}
|
||||
|
||||
export function registerLevels(client) {
|
||||
@@ -59,19 +72,48 @@ export function registerLevels(client) {
|
||||
);
|
||||
if (!result?.leveledUp) return;
|
||||
|
||||
if (message.member) await grantRewards(message.member, result.level);
|
||||
const belohnt = message.member ? await grantRewards(message.member, result.level) : [];
|
||||
if (levelsAnnounce()) {
|
||||
const name = message.member?.displayName ?? message.author.username;
|
||||
const bisNaechstes = xpForLevel(result.level + 1) - result.xp;
|
||||
const naechste = naechsteBelohnung(result.level);
|
||||
|
||||
// Der Text bleibt die Vorlage aus dem Panel — nur der Rahmen ist
|
||||
// jetzt ein Embed, damit es zu Geburtstagen und Verlosungen passt.
|
||||
// Leere Vorlage: discord.js wirft bei setDescription('') und der
|
||||
// Aufstieg fiele still aus. Lieber eine schlichte Zeile.
|
||||
const text = renderTemplate('level.up', {
|
||||
user: name,
|
||||
mention: `<@${message.author.id}>`,
|
||||
level: result.level,
|
||||
xp: result.xp,
|
||||
}).trim() || `**${name}** ist jetzt **Level ${result.level}**!`;
|
||||
|
||||
const embed = brandEmbed(client, 'LEVEL')
|
||||
.setDescription(text)
|
||||
.setThumbnail(message.author.displayAvatarURL({ size: 128 }))
|
||||
.addFields(
|
||||
{ name: 'Gesamt', value: `${result.xp.toLocaleString('de-DE')} XP`, inline: true },
|
||||
{ name: `Bis Level ${result.level + 1}`, value: `${bisNaechstes.toLocaleString('de-DE')} XP`, inline: true },
|
||||
);
|
||||
if (belohnt.length > 0) {
|
||||
embed.addFields({
|
||||
name: belohnt.length > 1 ? 'Neue Rollen' : 'Neue Rolle',
|
||||
value: belohnt.map((id) => `<@&${id}>`).join(' '),
|
||||
inline: true,
|
||||
});
|
||||
} else if (naechste) {
|
||||
embed.addFields({
|
||||
name: 'Nächste Belohnung',
|
||||
value: `<@&${naechste.roleId}> ab Level ${naechste.level}`,
|
||||
inline: true,
|
||||
});
|
||||
}
|
||||
|
||||
await message.channel
|
||||
.send({
|
||||
content: renderTemplate('level.up', {
|
||||
user: name,
|
||||
mention: `<@${message.author.id}>`,
|
||||
level: result.level,
|
||||
xp: result.xp,
|
||||
}),
|
||||
allowedMentions: { parse: [] },
|
||||
})
|
||||
// Rollen-Erwähnungen im Embed pingen ohnehin nicht — der
|
||||
// Schalter hier hält auch den Nachrichtentext still.
|
||||
.send({ embeds: [embed], allowedMentions: { parse: [] } })
|
||||
.catch(() => {});
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user