[deploy] Fix stats duplication and polish help embed
All checks were successful
Deploy Discord Bot / deploy (push) Successful in 37s

This commit is contained in:
Pascal Prießnitz
2025-12-04 18:41:57 +01:00
parent 544f04655c
commit c95444feac
2 changed files with 43 additions and 19 deletions

View File

@@ -4,15 +4,19 @@ import { SlashCommand } from '../../utils/types';
const command: SlashCommand = { const command: SlashCommand = {
data: new SlashCommandBuilder().setName('help').setDescription('Zeigt Befehle und Module.'), data: new SlashCommandBuilder().setName('help').setDescription('Zeigt Befehle und Module.'),
async execute(interaction: ChatInputCommandInteraction) { async execute(interaction: ChatInputCommandInteraction) {
const avatar = interaction.client.user?.displayAvatarURL({ size: 256 }) ?? null;
const embed = new EmbedBuilder() const embed = new EmbedBuilder()
.setTitle('Papo Hilfe') .setTitle('Papo Hilfe')
.setDescription('Multi-Guild ready | Admin, Tickets, Musik, Automod, Dashboard') .setColor(0xf97316)
.setThumbnail(avatar)
.setDescription('Dein All-in-One Assistant: Tickets, Automod, Musik, Stats, Dashboard.')
.addFields( .addFields(
{ name: 'Admin', value: '/ban /kick /mute /timeout /clear', inline: false }, { name: '🛡️ Admin', value: '`/ban` `/kick` `/mute` `/timeout` `/clear`', inline: false },
{ name: 'Tickets', value: '/ticket /ticketpanel /ticketpriority /ticketstatus /transcript', inline: false }, { name: '🎫 Tickets', value: '`/ticket` `/ticketpanel` `/ticketpriority` `/ticketstatus` `/transcript`', inline: false },
{ name: 'Musik', value: '/play /pause /resume /skip /stop /queue /loop', inline: false }, { name: '🎵 Musik', value: '`/play` `/pause` `/resume` `/skip` `/stop` `/queue` `/loop`', inline: false },
{ name: 'Utility', value: '/ping /configure /serverinfo /rank', inline: false } { name: '📊 Server-Tools', value: '`/configure` `/serverinfo` `/rank`', inline: false }
); )
.setFooter({ text: 'Tipp: Nutze /configure für Module & Dashboard-Link' });
await interaction.reply({ embeds: [embed], ephemeral: true }); await interaction.reply({ embeds: [embed], ephemeral: true });
} }
}; };

View File

@@ -49,6 +49,7 @@ export class StatsService {
private client: Client | null = null; private client: Client | null = null;
private interval?: NodeJS.Timeout; private interval?: NodeJS.Timeout;
private lastRun = new Map<string, number>(); private lastRun = new Map<string, number>();
private syncLocks = new Map<string, Promise<void>>();
public setClient(client: Client) { public setClient(client: Client) {
this.client = client; this.client = client;
@@ -72,21 +73,25 @@ export class StatsService {
} }
public async saveConfig(guildId: string, config: Partial<ServerStatsConfig>) { public async saveConfig(guildId: string, config: Partial<ServerStatsConfig>) {
return this.withGuildLock(guildId, async () => {
const previous = await this.getConfig(guildId); const previous = await this.getConfig(guildId);
const normalized = this.normalizeConfig({ ...previous, ...config }); const normalized = this.normalizeConfig({ ...previous, ...config });
const synced = await this.syncGuild(guildId, normalized, previous); const synced = await this.syncGuild(guildId, normalized, previous);
await settingsStore.set(guildId, { serverStatsEnabled: synced.enabled, serverStatsConfig: synced } as any); await settingsStore.set(guildId, { serverStatsEnabled: synced.enabled, serverStatsConfig: synced } as any);
this.lastRun.set(guildId, Date.now()); this.lastRun.set(guildId, Date.now());
return synced; return synced;
});
} }
public async refreshGuild(guildId: string) { public async refreshGuild(guildId: string) {
return this.withGuildLock(guildId, async () => {
const cfg = await this.getConfig(guildId); const cfg = await this.getConfig(guildId);
if (!cfg.enabled) return cfg; if (!cfg.enabled) return cfg;
const synced = await this.syncGuild(guildId, cfg, cfg); const synced = await this.syncGuild(guildId, cfg, cfg);
await settingsStore.set(guildId, { serverStatsEnabled: synced.enabled, serverStatsConfig: synced } as any); await settingsStore.set(guildId, { serverStatsEnabled: synced.enabled, serverStatsConfig: synced } as any);
this.lastRun.set(guildId, Date.now()); this.lastRun.set(guildId, Date.now());
return synced; return synced;
});
} }
public async disableGuild(guildId: string) { public async disableGuild(guildId: string) {
@@ -248,4 +253,19 @@ export class StatsService {
await this.refreshGuild(guildId).catch(() => undefined); await this.refreshGuild(guildId).catch(() => undefined);
} }
} }
private async withGuildLock<T>(guildId: string, task: () => Promise<T>): Promise<T> {
const waitFor = this.syncLocks.get(guildId) || Promise.resolve();
const run = (async () => {
await waitFor.catch(() => undefined);
return task();
})();
this.syncLocks.set(guildId, run.then(() => undefined, () => undefined));
try {
return await run;
} finally {
const current = this.syncLocks.get(guildId);
if (current === run) this.syncLocks.delete(guildId);
}
}
} }