Files
Xalos/cogs/leveling.py
2025-10-06 14:20:28 +02:00

205 lines
9.6 KiB
Python

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)