205 lines
9.6 KiB
Python
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) |