import discord from discord.ext import commands from discord import app_commands import sqlite3 import random import time class Leveling(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot self.db_connection = sqlite3.connect("xalos_data.db") self.db_cursor = self.db_connection.cursor() self.db_cursor.execute(""" CREATE TABLE IF NOT EXISTS levels ( guild_id INTEGER, user_id INTEGER, level INTEGER DEFAULT 1, xp INTEGER DEFAULT 0, last_message_timestamp INTEGER DEFAULT 0, PRIMARY KEY (guild_id, user_id) ) """) self.db_cursor.execute(""" CREATE TABLE IF NOT EXISTS level_rewards ( guild_id INTEGER, level INTEGER, role_id INTEGER, PRIMARY KEY (guild_id, level) ) """) def cog_unload(self): """Wird aufgerufen, wenn der Cog entladen wird, um die DB-Verbindung zu schließen.""" self.db_connection.close() def _calculate_xp_for_next_level(self, level: int) -> int: """Berechnet die benötigten XP für das nächste Level.""" return 5 * (level ** 2) + 50 * level + 100 @commands.Cog.listener() async def on_message(self, message: discord.Message): # Ignoriere Bots und Nachrichten in DMs if message.author.bot or not message.guild: return # Cooldown-System (z.B. alle 60 Sekunden XP) user_id = message.author.id guild_id = message.guild.id current_time = time.time() if self.cooldowns.get(user_id, 0) + 60 > current_time: return # User ist noch im Cooldown self.cooldowns[user_id] = current_time # XP vergeben xp_to_add = random.randint(15, 25) # User in DB suchen/erstellen self.db_cursor.execute("SELECT * FROM levels WHERE guild_id = ? AND user_id = ?", (guild_id, user_id)) user_data = self.db_cursor.fetchone() if user_data is None: self.db_cursor.execute("INSERT INTO levels (guild_id, user_id, xp) VALUES (?, ?, ?)", (guild_id, user_id, xp_to_add)) current_level, current_xp = 1, xp_to_add else: current_level = user_data[2] current_xp = user_data[3] + xp_to_add self.db_cursor.execute("UPDATE levels SET xp = ? WHERE guild_id = ? AND user_id = ?", (current_xp, guild_id, user_id)) # Level-Up-Check xp_for_next_level = 5 * (current_level ** 2) + 50 * current_level + 100 if current_xp >= xp_for_next_level: new_level = current_level + 1 remaining_xp = current_xp - xp_for_next_level self.db_cursor.execute("UPDATE levels SET level = ?, xp = ? WHERE guild_id = ? AND user_id = ?", (new_level, remaining_xp, guild_id, user_id)) await message.channel.send(f"🎉 Herzlichen Glückwunsch, {message.author.mention}! Du hast Level **{new_level}** erreicht!") # Check for role rewards self.db_cursor.execute("SELECT role_id FROM level_rewards WHERE guild_id = ? AND level = ?", (guild_id, new_level)) reward = self.db_cursor.fetchone() if reward: role_id = reward[0] role = message.guild.get_role(role_id) if role: await message.author.add_roles(role, reason=f"Level {new_level} erreicht") await message.channel.send(f"Als Belohnung hast du die Rolle **{role.name}** erhalten!") self.db_connection.commit() @app_commands.command(name="rank", description="Zeigt dein Level und deine XP an.") @app_commands.describe(member="Das Mitglied, dessen Rang du sehen möchtest.") async def rank(self, interaction: discord.Interaction, member: discord.Member = None): target = member or interaction.user self.db_cursor.execute("SELECT level, xp FROM levels WHERE guild_id = ? AND user_id = ?", (interaction.guild.id, target.id)) user_data = self.db_cursor.fetchone() if user_data is None: await interaction.response.send_message(f"{target.display_name} hat noch keine XP gesammelt.", ephemeral=True) return level, xp = user_data xp_for_next_level = self._calculate_xp_for_next_level(level) # Finde den Rang des Nutzers (effiziente SQL-Abfrage) self.db_cursor.execute( "SELECT COUNT(*) + 1 FROM levels WHERE guild_id = ? AND xp > ?", (interaction.guild.id, xp) ) rank_result = self.db_cursor.fetchone() rank = rank_result[0] if rank_result else -1 embed = discord.Embed( title=f"Rang von {target.display_name}", color=target.color ) embed.set_thumbnail(url=target.display_avatar.url) embed.add_field(name="Level", value=f"`{level}`", inline=True) embed.add_field(name="XP", value=f"`{xp} / {xp_for_next_level}`", inline=True) if rank > 0: embed.add_field(name="Rang", value=f"`#{rank}`", inline=True) await interaction.response.send_message(embed=embed) @app_commands.command(name="leaderboard", description="Zeigt die Top 10 Nutzer des Servers an.") async def leaderboard(self, interaction: discord.Interaction): self.db_cursor.execute("SELECT user_id, level, xp FROM levels WHERE guild_id = ? ORDER BY xp DESC LIMIT 10", (interaction.guild.id,)) top_users = self.db_cursor.fetchall() if not top_users: await interaction.response.send_message("Es gibt noch keine Rangliste für diesen Server.", ephemeral=True) return embed = discord.Embed( title=f"🏆 Rangliste für {interaction.guild.name}", color=discord.Color.gold() ) description = "" for i, (user_id, level, xp, *_) in enumerate(top_users): # Ignoriere zusätzliche Spalten # Versuche, das Mitgliedsobjekt zu bekommen. Wenn es den Server verlassen hat, wird es übersprungen. member = interaction.guild.get_member(user_id) if member: rank_emoji = ["🥇", "🥈", "🥉"] if i < 3: description += f"{rank_emoji[i]} **{member.display_name}** - Level {level} ({xp} XP)\n" else: description += f"`#{i+1}` **{member.display_name}** - Level {level} ({xp} XP)\n" if not description: description = "Konnte keine aktuellen Mitglieder für die Rangliste finden." embed.description = description await interaction.response.send_message(embed=embed) # --- Role Reward Management --- leveling_group = app_commands.Group(name="leveling", description="Verwaltet das Leveling-System.", default_permissions=discord.Permissions(manage_guild=True)) @leveling_group.command(name="addreward", description="Fügt eine Rollen-Belohnung für ein bestimmtes Level hinzu.") @app_commands.describe(level="Das Level, für das die Belohnung vergeben wird.", role="Die Rolle, die vergeben werden soll.") async def add_reward(self, interaction: discord.Interaction, level: int, role: discord.Role): if level <= 0: await interaction.response.send_message("Das Level muss größer als 0 sein.", ephemeral=True) return try: self.db_cursor.execute("INSERT OR REPLACE INTO level_rewards (guild_id, level, role_id) VALUES (?, ?, ?)", (interaction.guild.id, level, role.id)) self.db_connection.commit() await interaction.response.send_message(f"✅ Rolle `{role.name}` wird nun auf Level `{level}` vergeben.", ephemeral=True) except Exception as e: await interaction.response.send_message(f"Ein Fehler ist aufgetreten: {e}", ephemeral=True) @leveling_group.command(name="removereward", description="Entfernt eine Rollen-Belohnung von einem Level.") @app_commands.describe(level="Das Level, von dem die Belohnung entfernt werden soll.") async def remove_reward(self, interaction: discord.Interaction, level: int): self.db_cursor.execute("DELETE FROM level_rewards WHERE guild_id = ? AND level = ?", (interaction.guild.id, level)) if self.db_cursor.rowcount > 0: self.db_connection.commit() await interaction.response.send_message(f"✅ Die Belohnung für Level `{level}` wurde entfernt.", ephemeral=True) else: await interaction.response.send_message(f"❌ Für Level `{level}` war keine Belohnung konfiguriert.", ephemeral=True) @leveling_group.command(name="listrewards", description="Zeigt alle konfigurierten Rollen-Belohnungen an.") async def list_rewards(self, interaction: discord.Interaction): self.db_cursor.execute("SELECT level, role_id FROM level_rewards WHERE guild_id = ? ORDER BY level ASC", (interaction.guild.id,)) rewards = self.db_cursor.fetchall() if not rewards: await interaction.response.send_message("Es sind keine Rollen-Belohnungen für diesen Server konfiguriert.", ephemeral=True) return description = "" for level, role_id in rewards: role = interaction.guild.get_role(role_id) description += f"**Level {level}** → {role.mention if role else f'`Rolle nicht gefunden (ID: {role_id})`'}\n" embed = discord.Embed(title="Rollen-Belohnungen", description=description, color=discord.Color.purple()) await interaction.response.send_message(embed=embed, ephemeral=True) async def setup(bot: commands.Bot): cog = Leveling(bot) bot.tree.add_command(cog.leveling_group) await bot.add_cog(cog)