import { Collection, Message } from 'discord.js'; import { logger } from '../utils/logger'; import { settingsStore } from '../config/state'; import { prisma } from '../database'; interface LevelData { xp: number; level: number; } export class LevelService { private cache = new Collection(); private cooldown = new Set(); private key(guildId: string, userId: string) { return `${guildId}:${userId}`; } async handleMessage(message: Message) { if (!message.guild || message.author.bot) return; const guildConfig = settingsStore.get(message.guild.id); if (guildConfig?.levelingEnabled !== true) return; const key = this.key(message.guild.id, message.author.id); if (this.cooldown.has(key)) return; this.cooldown.add(key); setTimeout(() => this.cooldown.delete(key), 60_000); const entry = await this.loadLevel(message.guild.id, message.author.id); const xpGain = 10; entry.xp += xpGain; const nextLevel = Math.floor(0.2 * Math.sqrt(entry.xp)); if (nextLevel > entry.level) { entry.level = nextLevel; message.channel.send({ content: `${message.author} hat Level ${entry.level} erreicht!` }).catch(() => undefined); logger.info(`Level up: ${message.author.tag} -> ${entry.level}`); } this.cache.set(key, entry); await prisma.level.upsert({ where: { userId_guildId: { userId: message.author.id, guildId: message.guild.id } }, update: { xp: entry.xp, level: entry.level }, create: { userId: message.author.id, guildId: message.guild.id, xp: entry.xp, level: entry.level } }); } async getLevel(userId: string, guildId: string) { const key = this.key(guildId, userId); const cached = this.cache.get(key); if (cached) return cached; return this.loadLevel(guildId, userId); } private async loadLevel(guildId: string, userId: string): Promise { const key = this.key(guildId, userId); if (this.cache.has(key)) return this.cache.get(key)!; const row = await prisma.level.findUnique({ where: { userId_guildId: { guildId, userId } } }).catch(() => null); const entry: LevelData = { xp: row?.xp ?? 0, level: row?.level ?? 0 }; this.cache.set(key, entry); return entry; } }