import discord from discord import app_commands from discord.ext import commands import json import os from dotenv import load_dotenv load_dotenv() TOKEN = os.getenv("DISCORD_TOKEN") DATA_FILE = "data.json" def load_data() -> dict: if os.path.exists(DATA_FILE): with open(DATA_FILE, "r", encoding="utf-8") as f: return json.load(f) return {} def save_data(data: dict): with open(DATA_FILE, "w", encoding="utf-8") as f: json.dump(data, f, indent=2, ensure_ascii=False) def get_mute_count(data: dict, guild_id: int, user_id: int) -> int: return data.get(str(guild_id), {}).get(str(user_id), 0) def increment_mute(data: dict, guild_id: int, user_id: int): gid = str(guild_id) uid = str(user_id) if gid not in data: data[gid] = {} data[gid][uid] = data[gid].get(uid, 0) + 1 intents = discord.Intents.default() intents.voice_states = True intents.members = True bot = commands.Bot(command_prefix="!", intents=intents) mute_data = load_data() @bot.event async def on_ready(): print(f"Bot gestartet als {bot.user} (ID: {bot.user.id})") try: synced = await bot.tree.sync() print(f"{len(synced)} Slash-Commands synchronisiert.") except Exception as e: print(f"Fehler beim Sync: {e}") @bot.event async def on_voice_state_update( member: discord.Member, before: discord.VoiceState, after: discord.VoiceState, ): # Bots ignorieren if member.bot: return guild = member.guild # AFK-Channel ausschließen: Ereignisse ignorieren, wenn Nutzer im AFK-Channel ist afk_channel = guild.afk_channel if after.channel is not None and afk_channel is not None and after.channel.id == afk_channel.id: return if before.channel is not None and afk_channel is not None and before.channel.id == afk_channel.id: return # Erkennen: Nutzer hat sich gerade selbst gemutet (war vorher nicht gemutet) just_muted = (not before.self_mute) and after.self_mute if just_muted: increment_mute(mute_data, guild.id, member.id) save_data(mute_data) print( f"{member.display_name} hat sich gemutet " f"(Gesamt: {get_mute_count(mute_data, guild.id, member.id)}x) " f"[Server: {guild.name}]" ) @bot.tree.command(name="mutescore", description="Zeigt das Mute-Leaderboard des Servers.") @app_commands.describe(limit="Wie viele Plätze anzeigen? (Standard: 10)") async def mutescore(interaction: discord.Interaction, limit: int = 10): limit = max(1, min(limit, 25)) guild = interaction.guild guild_data: dict = mute_data.get(str(guild.id), {}) if not guild_data: await interaction.response.send_message( "Noch keine Mutes aufgezeichnet.", ephemeral=True ) return sorted_entries = sorted(guild_data.items(), key=lambda x: x[1], reverse=True) top = sorted_entries[:limit] embed = discord.Embed( title="Mute-Leaderboard", description="Wer mutet sich am häufigsten?", color=discord.Color.blurple(), ) medals = {1: "🥇", 2: "🥈", 3: "🥉"} lines = [] for rank, (uid, count) in enumerate(top, start=1): member = guild.get_member(int(uid)) name = member.display_name if member else f"Unbekannt ({uid})" medal = medals.get(rank, f"**#{rank}**") lines.append(f"{medal} {name} — **{count}x** gemutet") embed.description = "\n".join(lines) embed.set_footer(text=f"Top {len(top)} von {len(sorted_entries)} Nutzern") await interaction.response.send_message(embed=embed) @bot.tree.command(name="mymutes", description="Zeigt deine eigene Mute-Anzahl.") async def mymutes(interaction: discord.Interaction): count = get_mute_count(mute_data, interaction.guild.id, interaction.user.id) await interaction.response.send_message( f"Du hast dich bisher **{count}x** gemutet.", ephemeral=True ) bot.run(TOKEN)