This commit is contained in:
Robin
2025-10-06 14:20:28 +02:00
commit f0c8fe734a
16 changed files with 1360 additions and 0 deletions

1
.env Normal file
View File

@@ -0,0 +1 @@
DISCORD_TOKEN="DEIN_SUPER_GEHEIMER_BOT_TOKEN"

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
#Ignore vscode AI rules
.github\instructions\codacy.instructions.md

54
cogs/announcement.py Normal file
View File

@@ -0,0 +1,54 @@
import discord
from discord.ext import commands
from discord import app_commands
class Announcement(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
@app_commands.command(name="announce", description="Sendet eine formatierte Ankündigung in einen Kanal.")
@app_commands.describe(
channel="Der Kanal, in den die Ankündigung gesendet werden soll.",
title="Der Titel der Ankündigung.",
message="Die Nachricht. Benutze '\\n' für Zeilenumbrüche.",
color="Die Farbe des Embeds als HEX-Code (z.B. #FF5733).",
mention="Eine Rolle, die mit der Ankündigung erwähnt werden soll."
)
@app_commands.checks.has_permissions(manage_messages=True)
async def announce(self, interaction: discord.Interaction, channel: discord.TextChannel, title: str, message: str, color: str = None, mention: discord.Role = None):
"""Erstellt und sendet eine Ankündigung."""
# Ersetze \n durch echte Zeilenumbrüche
message = message.replace("\\n", "\n")
# Verarbeite die Farbe
embed_color = discord.Color.blue() # Standardfarbe
if color:
try:
# Entferne '#' und konvertiere HEX zu Integer
embed_color = discord.Color(int(color.lstrip('#'), 16))
except ValueError:
await interaction.response.send_message("Ungültiger HEX-Farbcode. Bitte benutze ein Format wie `#FF5733`.", ephemeral=True)
return
embed = discord.Embed(
title=title,
description=message,
color=embed_color
)
embed.set_footer(text=f"Ankündigung von {interaction.user.display_name}")
# Erstelle den Inhalt der Nachricht (für den Ping)
content = mention.mention if mention else None
try:
await channel.send(content=content, embed=embed)
await interaction.response.send_message(f"✅ Ankündigung wurde erfolgreich in {channel.mention} gesendet.", ephemeral=True)
except discord.Forbidden:
await interaction.response.send_message("❌ Ich habe keine Berechtigung, in diesem Kanal zu schreiben.", ephemeral=True)
except Exception as e:
await interaction.response.send_message(f"Ein Fehler ist aufgetreten: {e}", ephemeral=True)
async def setup(bot: commands.Bot):
await bot.add_cog(Announcement(bot))

118
cogs/audit_log.py Normal file
View File

@@ -0,0 +1,118 @@
import discord
from discord.ext import commands
from discord import app_commands
import sqlite3
import datetime
class AuditLog(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()
# Tabelle für server-spezifische Einstellungen
self.db_cursor.execute("""
CREATE TABLE IF NOT EXISTS server_configs (
guild_id INTEGER PRIMARY KEY,
log_channel_id INTEGER
)
""")
async def get_log_channel(self, guild_id: int) -> discord.TextChannel | None:
"""Holt den Log-Kanal für einen Server aus der DB."""
self.db_cursor.execute("SELECT log_channel_id FROM server_configs WHERE guild_id = ?", (guild_id,))
result = self.db_cursor.fetchone()
if result and result[0]:
return self.bot.get_channel(result[0])
return None
@app_commands.command(name="setlogchannel", description="Setzt den Kanal für die Audit-Logs.")
@app_commands.describe(channel="Der Kanal, in den die Logs gesendet werden sollen.")
@app_commands.checks.has_permissions(manage_guild=True)
async def set_log_channel(self, interaction: discord.Interaction, channel: discord.TextChannel):
"""Speichert den Log-Kanal in der Datenbank."""
self.db_cursor.execute(
"INSERT OR REPLACE INTO server_configs (guild_id, log_channel_id) VALUES (?, ?)",
(interaction.guild.id, channel.id)
)
self.db_connection.commit()
await interaction.response.send_message(f"✅ Der Log-Kanal wurde auf {channel.mention} gesetzt.", ephemeral=True)
@commands.Cog.listener()
async def on_message_delete(self, message: discord.Message):
if message.author.bot or not message.guild:
return
log_channel = await self.get_log_channel(message.guild.id)
if log_channel:
embed = discord.Embed(
title="🗑️ Nachricht gelöscht",
description=f"**Autor:** {message.author.mention}\n"
f"**Kanal:** {message.channel.mention}",
color=discord.Color.orange(),
timestamp=datetime.datetime.now(datetime.timezone.utc)
)
# Füge den Inhalt hinzu, wenn er nicht zu lang ist
if message.content:
embed.add_field(name="Inhalt", value=message.content[:1024], inline=False)
await log_channel.send(embed=embed)
@commands.Cog.listener()
async def on_message_edit(self, before: discord.Message, after: discord.Message):
if before.author.bot or not before.guild or before.content == after.content:
return
log_channel = await self.get_log_channel(before.guild.id)
if log_channel:
embed = discord.Embed(
title="✏️ Nachricht bearbeitet",
description=f"**Autor:** {before.author.mention}\n"
f"**Kanal:** {before.channel.mention}\n"
f"[Zur Nachricht springen]({after.jump_url})",
color=discord.Color.blue(),
timestamp=datetime.datetime.now(datetime.timezone.utc)
)
# Füge den alten und neuen Inhalt hinzu, wenn er nicht zu lang ist
embed.add_field(name="Vorher", value=before.content[:1024], inline=False)
embed.add_field(name="Nachher", value=after.content[:1024], inline=False)
await log_channel.send(embed=embed)
@commands.Cog.listener()
async def on_member_ban(self, guild: discord.Guild, user: discord.User | discord.Member):
log_channel = await self.get_log_channel(guild.id)
if log_channel:
embed = discord.Embed(
title="🔨 Mitglied gebannt",
description=f"**Mitglied:** {user.mention} (`{user.id}`)",
color=discord.Color.dark_red(),
timestamp=datetime.datetime.now(datetime.timezone.utc)
)
await log_channel.send(embed=embed)
@commands.Cog.listener()
async def on_member_remove(self, member: discord.Member):
# Dieser Event wird auch bei einem Kick oder Bann ausgelöst. Wir warten kurz,
# um zu sehen, ob es ein Bann war, um doppelte Logs zu vermeiden.
await asyncio.sleep(2)
try:
# Wenn der Nutzer gebannt wurde, existiert ein Ban-Eintrag
await member.guild.fetch_ban(member)
return # Es war ein Bann, on_member_ban kümmert sich darum
except discord.NotFound:
# Es war kein Bann (also ein Kick oder freiwilliger Leave)
log_channel = await self.get_log_channel(member.guild.id)
if log_channel:
embed = discord.Embed(
title="🚪 Mitglied hat den Server verlassen",
description=f"**Mitglied:** {member.mention} (`{member.id}`)",
color=discord.Color.greyple(),
timestamp=datetime.datetime.now(datetime.timezone.utc)
)
await log_channel.send(embed=embed)
async def setup(bot: commands.Bot):
# Wir brauchen asyncio für das Warten im on_member_remove Event
import asyncio
await bot.add_cog(AuditLog(bot))

90
cogs/custom_commands.py Normal file
View File

@@ -0,0 +1,90 @@
import discord
from discord.ext import commands
from discord import app_commands
import sqlite3
class CustomCommands(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 custom_commands (
guild_id INTEGER,
command_name TEXT,
response_text TEXT,
PRIMARY KEY (guild_id, command_name)
)
""")
self.db_connection.commit()
# We define a static prefix for custom commands, as they are text-based.
self.prefix = "!"
@commands.Cog.listener()
async def on_message(self, message: discord.Message):
# Ignore bots, DMs, and messages that don't start with the prefix
if message.author.bot or not message.guild or not message.content.startswith(self.prefix):
return
# Extract command name (e.g., "!hello" -> "hello")
command_name = message.content[len(self.prefix):].split(' ')[0].lower()
if not command_name:
return
# Look up the command in the database
self.db_cursor.execute(
"SELECT response_text FROM custom_commands WHERE guild_id = ? AND command_name = ?",
(message.guild.id, command_name)
)
result = self.db_cursor.fetchone()
if result:
response_text = result[0]
await message.channel.send(response_text)
# --- Management Commands ---
customcmd_group = app_commands.Group(name="customcommand", description="Verwaltet benutzerdefinierte Befehle.", default_permissions=discord.Permissions(manage_guild=True))
@customcmd_group.command(name="add", description="Erstellt einen neuen benutzerdefinierten Befehl.")
@app_commands.describe(name="Der Name des Befehls (ohne '!').", response="Die Antwort, die der Bot senden soll.")
async def cc_add(self, interaction: discord.Interaction, name: str, response: str):
command_name = name.lower().split(' ')[0] # Ensure single word and lowercase
try:
self.db_cursor.execute("INSERT INTO custom_commands (guild_id, command_name, response_text) VALUES (?, ?, ?)", (interaction.guild.id, command_name, response))
self.db_connection.commit()
await interaction.response.send_message(f"✅ Der Befehl `{self.prefix}{command_name}` wurde erstellt.", ephemeral=True)
except sqlite3.IntegrityError:
await interaction.response.send_message(f"⚠️ Ein Befehl mit dem Namen `{command_name}` existiert bereits. Bitte entferne ihn zuerst.", ephemeral=True)
@customcmd_group.command(name="remove", description="Löscht einen benutzerdefinierten Befehl.")
@app_commands.describe(name="Der Name des Befehls, der gelöscht werden soll (ohne '!').")
async def cc_remove(self, interaction: discord.Interaction, name: str):
command_name = name.lower()
self.db_cursor.execute("DELETE FROM custom_commands WHERE guild_id = ? AND command_name = ?", (interaction.guild.id, command_name))
if self.db_cursor.rowcount > 0:
self.db_connection.commit()
await interaction.response.send_message(f"✅ Der Befehl `{self.prefix}{command_name}` wurde gelöscht.", ephemeral=True)
else:
await interaction.response.send_message(f"❌ Kein Befehl mit dem Namen `{command_name}` gefunden.", ephemeral=True)
@customcmd_group.command(name="list", description="Zeigt alle benutzerdefinierten Befehle an.")
async def cc_list(self, interaction: discord.Interaction):
self.db_cursor.execute("SELECT command_name FROM custom_commands WHERE guild_id = ?", (interaction.guild.id,))
commands_list = [row[0] for row in self.db_cursor.fetchall()]
if not commands_list:
await interaction.response.send_message("Es gibt keine benutzerdefinierten Befehle auf diesem Server.", ephemeral=True)
return
embed = discord.Embed(
title="Benutzerdefinierte Befehle",
description=", ".join(f"`{self.prefix}{cmd}`" for cmd in commands_list),
color=discord.Color.green()
)
await interaction.response.send_message(embed=embed, ephemeral=True)
async def setup(bot: commands.Bot):
cog = CustomCommands(bot)
bot.tree.add_command(cog.customcmd_group)
await bot.add_cog(cog)

103
cogs/giveaways.py Normal file
View File

@@ -0,0 +1,103 @@
import discord
from discord.ext import commands
from discord import app_commands
import datetime
import asyncio
import random
import re
def parse_duration(duration_str: str) -> int:
"""
Wandelt einen Dauer-String (z.B. "1d", "12h", "30m", "5s") in Sekunden um.
Gibt 0 zurück, wenn das Format ungültig ist.
"""
match = re.match(r"(\d+)([dhms])", duration_str.lower())
if not match:
return 0
value, unit = int(match.group(1)), match.group(2)
if unit == 'd':
return value * 86400
elif unit == 'h':
return value * 3600
elif unit == 'm':
return value * 60
elif unit == 's':
return value
return 0
class Giveaways(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
@app_commands.command(name="giveaway", description="Startet eine Verlosung.")
@app_commands.describe(
duration="Dauer (z.B. 10m, 1h, 2d).",
winners="Anzahl der Gewinner.",
prize="Der Preis, der verlost wird."
)
@app_commands.checks.has_permissions(manage_guild=True)
async def giveaway(self, interaction: discord.Interaction, duration: str, winners: int, prize: str):
"""Startet eine Verlosung."""
seconds = parse_duration(duration)
if seconds <= 0:
await interaction.response.send_message("Ungültiges Zeitformat! Bitte benutze `d`, `h`, `m` oder `s` (z.B. `10m` oder `2h`).", ephemeral=True)
return
if winners < 1:
await interaction.response.send_message("Die Anzahl der Gewinner muss mindestens 1 sein.", ephemeral=True)
return
end_time = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(seconds=seconds)
embed = discord.Embed(
title=f"🎉 Verlosung: {prize}",
description=f"Reagiere mit 🎉, um teilzunehmen!\n"
f"Endet: <t:{int(end_time.timestamp())}:R>\n"
f"Veranstalter: {interaction.user.mention}",
color=discord.Color.gold()
)
embed.set_footer(text=f"{winners} Gewinner")
# Sende die Nachricht und reagiere, um die Teilnahme zu starten
await interaction.response.send_message("Verlosung wird gestartet...", ephemeral=True)
giveaway_message = await interaction.channel.send(embed=embed)
await giveaway_message.add_reaction("🎉")
# Warte bis zum Ende der Verlosung
await asyncio.sleep(seconds)
# Hole die Nachricht erneut, um die aktuellen Reaktionen zu bekommen
try:
updated_message = await interaction.channel.fetch_message(giveaway_message.id)
except discord.NotFound:
# Nachricht wurde gelöscht, Verlosung abbrechen
return
# Finde alle Teilnehmer (ohne Bots)
reaction = discord.utils.get(updated_message.reactions, emoji="🎉")
participants = [user async for user in reaction.users() if not user.bot]
# Wähle den/die Gewinner
if not participants:
winner_text = "Niemand hat teilgenommen. 😢"
new_embed = embed.copy()
new_embed.description = f"Verlosung beendet.\n{winner_text}"
new_embed.color = discord.Color.dark_red()
await updated_message.edit(embed=new_embed)
return
chosen_winners = random.sample(participants, k=min(winners, len(participants)))
winner_mentions = ", ".join(w.mention for w in chosen_winners)
# Bearbeite die ursprüngliche Nachricht und verkünde die Gewinner
new_embed = embed.copy()
new_embed.description = f"Verlosung beendet!\n**Gewinner:** {winner_mentions}"
new_embed.color = discord.Color.green()
await updated_message.edit(embed=new_embed)
await interaction.channel.send(f"Herzlichen Glückwunsch {winner_mentions}! Ihr habt **{prize}** gewonnen!")
async def setup(bot: commands.Bot):
await bot.add_cog(Giveaways(bot))

205
cogs/leveling.py Normal file
View File

@@ -0,0 +1,205 @@
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)

401
cogs/moderation.py Normal file
View File

@@ -0,0 +1,401 @@
import discord
from discord.ext import commands
from discord import app_commands
import datetime
import sqlite3
import time
class Moderation(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 warnings (
warning_id INTEGER PRIMARY KEY AUTOINCREMENT,
guild_id INTEGER,
user_id INTEGER,
moderator_id INTEGER,
reason TEXT,
timestamp INTEGER
)
""")
self.db_cursor.execute("""
CREATE TABLE IF NOT EXISTS automod_configs (
guild_id INTEGER PRIMARY KEY,
badwords_enabled INTEGER DEFAULT 0
)
""")
self.db_cursor.execute("""
CREATE TABLE IF NOT EXISTS forbidden_words (
guild_id INTEGER,
word TEXT,
PRIMARY KEY (guild_id, word)
)
""")
self.db_connection.commit()
@app_commands.command(name="kick", description="Kickt ein Mitglied vom Server.")
@app_commands.describe(
member="Das Mitglied, das gekickt werden soll.",
reason="Der Grund fuer den Kick."
)
@app_commands.checks.has_permissions(kick_members=True)
async def kick(self, interaction: discord.Interaction, member: discord.Member, reason: str = "Kein Grund angegeben"):
if member == interaction.user:
await interaction.response.send_message("Du kannst dich nicht selbst kicken.", ephemeral=True)
return
if member == self.bot.user or member == interaction.guild.owner:
await interaction.response.send_message("Diese Person kann nicht gekickt werden.", ephemeral=True)
return
try:
await member.send(f"Du wurdest vom Server '{interaction.guild.name}' gekickt. Grund: {reason}")
except discord.Forbidden:
pass
await member.kick(reason=reason)
embed = discord.Embed(
title="Mitglied gekickt",
description=f"{member.mention} wurde erfolgreich gekickt.",
color=discord.Color.orange()
)
embed.add_field(name="Grund", value=reason, inline=False)
embed.add_field(name="Moderator", value=interaction.user.mention, inline=False)
await interaction.response.send_message(embed=embed)
@kick.error
async def kick_error(self, interaction: discord.Interaction, error: app_commands.AppCommandError):
if isinstance(error, app_commands.MissingPermissions):
await interaction.response.send_message("Dir fehlt die Berechtigung (`kick_members`), um diesen Befehl auszufuehren!", ephemeral=True)
else:
await interaction.response.send_message("Ein unerwarteter Fehler ist aufgetreten.", ephemeral=True)
print(error)
@app_commands.command(name="ban", description="Verbannt ein Mitglied permanent vom Server.")
@app_commands.describe(
member="Das Mitglied, das verbannt werden soll.",
reason="Der Grund für den Bann."
)
@app_commands.checks.has_permissions(ban_members=True)
async def ban(self, interaction: discord.Interaction, member: discord.Member, reason: str = "Kein Grund angegeben"):
"""Verbannt ein Mitglied und sendet eine Bestätigung."""
if member == interaction.user:
await interaction.response.send_message("Du kannst dich nicht selbst bannen.", ephemeral=True)
return
if member == self.bot.user or member == interaction.guild.owner:
await interaction.response.send_message("Diese Person kann nicht gebannt werden.", ephemeral=True)
return
try:
await member.send(f"Du wurdest vom Server '{interaction.guild.name}' permanent gebannt. Grund: {reason}")
except discord.Forbidden:
pass
await member.ban(reason=reason)
embed = discord.Embed(
title="Mitglied gebannt",
description=f"{member.mention} wurde erfolgreich gebannt.",
color=discord.Color.red()
)
embed.add_field(name="Grund", value=reason, inline=False)
embed.add_field(name="Moderator", value=interaction.user.mention, inline=False)
await interaction.response.send_message(embed=embed)
@ban.error
async def ban_error(self, interaction: discord.Interaction, error: app_commands.AppCommandError):
if isinstance(error, app_commands.MissingPermissions):
await interaction.response.send_message("Dir fehlt die Berechtigung (`ban_members`), um diesen Befehl auszuführen!", ephemeral=True)
else:
await interaction.response.send_message("Ein unerwarteter Fehler ist aufgetreten.", ephemeral=True)
print(error)
@app_commands.command(name="mute", description="Schaltet ein Mitglied für eine bestimmte Zeit stumm (Timeout).")
@app_commands.describe(
member="Das Mitglied, das stummgeschaltet werden soll.",
minutes="Die Dauer des Mutes in Minuten.",
reason="Der Grund für den Mute."
)
@app_commands.checks.has_permissions(moderate_members=True)
async def mute(self, interaction: discord.Interaction, member: discord.Member, minutes: int, reason: str = "Kein Grund angegeben"):
"""Versetzt ein Mitglied in einen Timeout."""
duration = datetime.timedelta(minutes=minutes)
await member.timeout(duration, reason=reason)
embed = discord.Embed(
title="Mitglied stummgeschaltet",
description=f"{member.mention} wurde für {minutes} Minute(n) stummgeschaltet.",
color=discord.Color.blue()
)
embed.add_field(name="Grund", value=reason, inline=False)
embed.add_field(name="Moderator", value=interaction.user.mention, inline=False)
await interaction.response.send_message(embed=embed)
@mute.error
async def mute_error(self, interaction: discord.Interaction, error: app_commands.AppCommandError):
if isinstance(error, app_commands.MissingPermissions):
await interaction.response.send_message("Dir fehlt die Berechtigung (`moderate_members`), um diesen Befehl auszuführen!", ephemeral=True)
else:
await interaction.response.send_message("Ein unerwarteter Fehler ist aufgetreten.", ephemeral=True)
print(error)
@app_commands.command(name="warn", description="Verwarnt ein Mitglied.")
@app_commands.describe(
member="Das Mitglied, das verwarnt werden soll.",
reason="Der Grund für die Verwarnung."
)
@app_commands.checks.has_permissions(moderate_members=True)
async def warn(self, interaction: discord.Interaction, member: discord.Member, reason: str):
"""Verwarnt ein Mitglied und speichert es in der Datenbank."""
guild_id = interaction.guild.id
user_id = member.id
moderator_id = interaction.user.id
timestamp = int(time.time())
self.db_cursor.execute(
"INSERT INTO warnings (guild_id, user_id, moderator_id, reason, timestamp) VALUES (?, ?, ?, ?, ?)",
(guild_id, user_id, moderator_id, reason, timestamp)
)
self.db_connection.commit()
warning_id = self.db_cursor.lastrowid
embed = discord.Embed(
title="⚠️ Mitglied verwarnt",
description=f"{member.mention} wurde verwarnt.",
color=discord.Color.yellow()
)
embed.add_field(name="Moderator", value=interaction.user.mention, inline=True)
embed.add_field(name="Grund", value=reason, inline=True)
embed.set_footer(text=f"Warnungs-ID: {warning_id}")
await interaction.response.send_message(embed=embed)
@app_commands.command(name="warnings", description="Zeigt alle Verwarnungen eines Mitglieds an.")
@app_commands.describe(member="Das Mitglied, dessen Verwarnungen du sehen möchtest.")
@app_commands.checks.has_permissions(moderate_members=True)
async def warnings(self, interaction: discord.Interaction, member: discord.Member):
"""Listet alle Verwarnungen eines Mitglieds auf."""
guild_id = interaction.guild.id
user_id = member.id
self.db_cursor.execute("SELECT warning_id, moderator_id, reason, timestamp FROM warnings WHERE guild_id = ? AND user_id = ?", (guild_id, user_id))
user_warnings = self.db_cursor.fetchall()
if not user_warnings:
await interaction.response.send_message(f"{member.display_name} hat keine Verwarnungen.", ephemeral=True)
return
embed = discord.Embed(
title=f"Verwarnungen für {member.display_name}",
color=member.color
)
for warn_id, mod_id, reason, ts in user_warnings:
moderator = interaction.guild.get_member(mod_id) or f"ID: {mod_id}"
embed.add_field(
name=f"Warnung #{warn_id} - <t:{ts}:d>",
value=f"**Grund:** {reason}\n**Moderator:** {moderator}",
inline=False
)
await interaction.response.send_message(embed=embed)
@app_commands.command(name="delwarn", description="Löscht eine spezifische Verwarnung.")
@app_commands.describe(warning_id="Die ID der Verwarnung, die gelöscht werden soll.")
@app_commands.checks.has_permissions(moderate_members=True)
async def delwarn(self, interaction: discord.Interaction, warning_id: int):
"""Löscht eine Verwarnung anhand ihrer ID."""
self.db_cursor.execute("DELETE FROM warnings WHERE warning_id = ? AND guild_id = ?", (warning_id, interaction.guild.id))
changes = self.db_connection.total_changes
self.db_connection.commit()
if changes > 0:
await interaction.response.send_message(f"✅ Verwarnung mit der ID `{warning_id}` wurde erfolgreich gelöscht.", ephemeral=True)
else:
await interaction.response.send_message(f"❌ Keine Verwarnung mit der ID `{warning_id}` auf diesem Server gefunden.", ephemeral=True)
@app_commands.command(name="lock", description="Sperrt einen Kanal, sodass niemand mehr schreiben kann.")
@app_commands.describe(channel="Der Kanal, der gesperrt werden soll (standardmäßig der aktuelle).")
@app_commands.checks.has_permissions(manage_channels=True)
async def lock(self, interaction: discord.Interaction, channel: discord.TextChannel = None):
"""Sperrt einen Kanal für die @everyone Rolle."""
target_channel = channel or interaction.channel
try:
# Bearbeite die Berechtigungen für die @everyone Rolle
await target_channel.set_permissions(
interaction.guild.default_role,
send_messages=False,
reason=f"Kanal gesperrt von {interaction.user}"
)
except discord.Forbidden:
await interaction.response.send_message("Ich habe nicht die nötigen Berechtigungen, um diesen Kanal zu sperren.", ephemeral=True)
return
embed = discord.Embed(
title="🔒 Kanal gesperrt",
description=f"Der Kanal {target_channel.mention} wurde von {interaction.user.mention} gesperrt.",
color=discord.Color.dark_grey()
)
await interaction.response.send_message(embed=embed)
@app_commands.command(name="unlock", description="Entsperrt einen Kanal, sodass jeder wieder schreiben kann.")
@app_commands.describe(channel="Der Kanal, der entsperrt werden soll (standardmäßig der aktuelle).")
@app_commands.checks.has_permissions(manage_channels=True)
async def unlock(self, interaction: discord.Interaction, channel: discord.TextChannel = None):
"""Entsperrt einen Kanal für die @everyone Rolle."""
target_channel = channel or interaction.channel
try:
# Setzt die Berechtigung für send_messages auf den Standard zurück
await target_channel.set_permissions(
interaction.guild.default_role,
send_messages=None,
reason=f"Kanal entsperrt von {interaction.user}"
)
except discord.Forbidden:
await interaction.response.send_message("Ich habe nicht die nötigen Berechtigungen, um diesen Kanal zu entsperren.", ephemeral=True)
return
embed = discord.Embed(
title="🔓 Kanal entsperrt",
description=f"Der Kanal {target_channel.mention} wurde von {interaction.user.mention} entsperrt.",
color=discord.Color.light_grey()
)
await interaction.response.send_message(embed=embed)
@app_commands.command(name="slowmode", description="Setzt den langsamen Modus für einen Kanal.")
@app_commands.describe(
seconds="Die Verzögerung in Sekunden (0 zum Deaktivieren).",
channel="Der Kanal, für den der Slowmode gesetzt werden soll (standardmäßig der aktuelle)."
)
@app_commands.checks.has_permissions(manage_channels=True)
async def slowmode(self, interaction: discord.Interaction, seconds: int, channel: discord.TextChannel = None):
"""Setzt den Slowmode für einen Kanal."""
target_channel = channel or interaction.channel
await target_channel.edit(slowmode_delay=seconds, reason=f"Slowmode gesetzt von {interaction.user}")
embed = discord.Embed(
title="⏳ Slowmode aktualisiert",
description=f"Der Slowmode für {target_channel.mention} wurde auf **{seconds} Sekunden** gesetzt.",
color=discord.Color.blue()
)
await interaction.response.send_message(embed=embed)
# --- Auto-Moderator ---
async def get_log_channel(self, guild_id: int) -> discord.TextChannel | None:
"""Helper to get the log channel from the server_configs table."""
# This table is managed by the audit_log cog
self.db_cursor.execute("SELECT log_channel_id FROM server_configs WHERE guild_id = ?", (guild_id,))
result = self.db_cursor.fetchone()
if result and result[0]:
return self.bot.get_channel(result[0])
return None
@commands.Cog.listener()
async def on_message(self, message: discord.Message):
if not message.guild or message.author.bot:
return
# Check if user is an admin/moderator - they should be immune
if message.author.guild_permissions.manage_messages:
return
# --- Bad Word Filter ---
self.db_cursor.execute("SELECT badwords_enabled FROM automod_configs WHERE guild_id = ?", (message.guild.id,))
config = self.db_cursor.fetchone()
if config and config[0] == 1:
self.db_cursor.execute("SELECT word FROM forbidden_words WHERE guild_id = ?", (message.guild.id,))
forbidden_words = [row[0] for row in self.db_cursor.fetchall()]
if any(word in message.content.lower() for word in forbidden_words):
try:
await message.delete()
await message.channel.send(f"{message.author.mention}, deine Nachricht wurde gelöscht, da sie ein verbotenes Wort enthielt.", delete_after=10)
# Log the action
log_channel = await self.get_log_channel(message.guild.id)
if log_channel:
embed = discord.Embed(
title="🛡️ Auto-Mod Aktion",
description=f"**Aktion:** Nachricht mit verbotenem Wort gelöscht.",
color=discord.Color.red()
)
embed.add_field(name="Mitglied", value=message.author.mention, inline=True)
embed.add_field(name="Kanal", value=message.channel.mention, inline=True)
embed.add_field(name="Inhalt", value=f"||{message.content[:1000]}||", inline=False)
await log_channel.send(embed=embed)
except discord.Forbidden:
# Bot has no permissions to delete messages
pass
except Exception as e:
print(f"Error in automod: {e}")
# --- Auto-Mod Commands ---
automod_group = app_commands.Group(name="automod", description="Konfiguriert den Auto-Moderator.", default_permissions=discord.Permissions(manage_guild=True))
@automod_group.command(name="togglewords", description="Aktiviert oder deaktiviert den Filter für verbotene Wörter.")
@app_commands.describe(enabled="Soll der Filter aktiviert werden?")
async def automod_toggle_words(self, interaction: discord.Interaction, enabled: bool):
self.db_cursor.execute("INSERT OR REPLACE INTO automod_configs (guild_id, badwords_enabled) VALUES (?, ?)", (interaction.guild.id, int(enabled)))
self.db_connection.commit()
status = "aktiviert" if enabled else "deaktiviert"
await interaction.response.send_message(f"✅ Der Filter für verbotene Wörter wurde **{status}**.", ephemeral=True)
badwords_group = app_commands.Group(name="badwords", description="Verwaltet die Liste der verbotenen Wörter.", parent=automod_group)
@badwords_group.command(name="add", description="Fügt ein verbotenes Wort hinzu.")
@app_commands.describe(word="Das Wort, das hinzugefügt werden soll.")
async def badwords_add(self, interaction: discord.Interaction, word: str):
word = word.lower()
try:
self.db_cursor.execute("INSERT INTO forbidden_words (guild_id, word) VALUES (?, ?)", (interaction.guild.id, word))
self.db_connection.commit()
await interaction.response.send_message(f"✅ Das Wort `{word}` wurde zur Liste hinzugefügt.", ephemeral=True)
except sqlite3.IntegrityError:
await interaction.response.send_message(f"⚠️ Das Wort `{word}` ist bereits auf der Liste.", ephemeral=True)
@badwords_group.command(name="remove", description="Entfernt ein verbotenes Wort.")
@app_commands.describe(word="Das Wort, das entfernt werden soll.")
async def badwords_remove(self, interaction: discord.Interaction, word: str):
word = word.lower()
self.db_cursor.execute("DELETE FROM forbidden_words WHERE guild_id = ? AND word = ?", (interaction.guild.id, word))
if self.db_cursor.rowcount > 0:
self.db_connection.commit()
await interaction.response.send_message(f"✅ Das Wort `{word}` wurde von der Liste entfernt.", ephemeral=True)
else:
await interaction.response.send_message(f"❌ Das Wort `{word}` wurde nicht auf der Liste gefunden.", ephemeral=True)
@badwords_group.command(name="list", description="Zeigt alle verbotenen Wörter an.")
async def badwords_list(self, interaction: discord.Interaction):
self.db_cursor.execute("SELECT word FROM forbidden_words WHERE guild_id = ?", (interaction.guild.id,))
words = [row[0] for row in self.db_cursor.fetchall()]
if not words:
await interaction.response.send_message("Die Liste der verbotenen Wörter ist leer.", ephemeral=True)
return
embed = discord.Embed(
title="🚫 Liste der verbotenen Wörter",
description=", ".join(f"`{w}`" for w in words),
color=discord.Color.red()
)
await interaction.response.send_message(embed=embed, ephemeral=True)
async def setup(bot: commands.Bot):
cog = Moderation(bot)
# Add the command groups to the bot's command tree
bot.tree.add_command(cog.automod_group)
await bot.add_cog(cog)

54
cogs/polls.py Normal file
View File

@@ -0,0 +1,54 @@
import discord
from discord.ext import commands
from discord import app_commands
class Polls(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
# Emojis für bis zu 20 Optionen
self.poll_emojis = [
"🇦", "🇧", "🇨", "🇩", "🇪", "🇫", "🇬", "🇭", "🇮", "🇯",
"🇰", "🇱", "🇲", "🇳", "🇴", "🇵", "🇶", "🇷", "🇸", "🇹"
]
@app_commands.command(name="poll", description="Erstellt eine einfache Umfrage.")
@app_commands.describe(
question="Die Frage der Umfrage.",
options="Die Antwortmöglichkeiten, getrennt durch ein Semikolon (;)."
)
async def poll(self, interaction: discord.Interaction, question: str, options: str):
"""Erstellt eine Umfrage mit Reaktionen."""
# Trenne die Optionen am Semikolon
split_options = [opt.strip() for opt in options.split(';')]
# Überprüfe die Anzahl der Optionen
if len(split_options) < 2:
await interaction.response.send_message("Du musst mindestens 2 Optionen angeben.", ephemeral=True)
return
if len(split_options) > 20:
await interaction.response.send_message("Du kannst maximal 20 Optionen angeben.", ephemeral=True)
return
# Erstelle die Beschreibung für das Embed
description = []
for i, option in enumerate(split_options):
description.append(f"{self.poll_emojis[i]} {option}")
embed = discord.Embed(
title=f"📊 Umfrage: {question}",
description="\n".join(description),
color=discord.Color.dark_purple()
)
embed.set_footer(text=f"Umfrage gestartet von {interaction.user.display_name}")
# Sende die Nachricht und speichere sie, um Reaktionen hinzuzufügen
await interaction.response.send_message(embed=embed)
poll_message = await interaction.original_response()
# Füge die Reaktionen hinzu
for i in range(len(split_options)):
await poll_message.add_reaction(self.poll_emojis[i])
async def setup(bot: commands.Bot):
await bot.add_cog(Polls(bot))

104
cogs/reaction_roles.py Normal file
View File

@@ -0,0 +1,104 @@
import discord
from discord.ext import commands
from discord import app_commands
import sqlite3
class ReactionRoles(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 reaction_roles (
message_id INTEGER,
guild_id INTEGER,
role_id INTEGER,
emoji TEXT,
PRIMARY KEY (message_id, emoji)
)
""")
@app_commands.group(name="reactionrole", description="Verwaltet Reaktions-Rollen.")
@app_commands.checks.has_permissions(manage_roles=True)
async def reactionrole(self, interaction: discord.Interaction):
# This base command is not invoked directly
pass
@reactionrole.command(name="add", description="Fügt eine neue Reaktions-Rolle zu einer Nachricht hinzu.")
@app_commands.describe(
message_id="Die ID der Nachricht, zu der die Rolle hinzugefügt werden soll.",
emoji="Das Emoji, das als Reaktion verwendet wird.",
role="Die Rolle, die vergeben werden soll."
)
async def add_reaction_role(self, interaction: discord.Interaction, message_id: str, emoji: str, role: discord.Role):
"""Fügt eine neue Reaktions-Rolle hinzu."""
try:
# Check if the message exists
message = await interaction.channel.fetch_message(int(message_id))
except (discord.NotFound, ValueError):
await interaction.response.send_message("Ungültige Nachrichten-ID. Bitte stelle sicher, dass die ID korrekt ist und der Befehl im selben Kanal wie die Nachricht ausgeführt wird.", ephemeral=True)
return
# Add the reaction to the message so users can see it
try:
await message.add_reaction(emoji)
except discord.HTTPException:
await interaction.response.send_message("Ungültiges Emoji. Ich konnte es nicht zur Nachricht hinzufügen.", ephemeral=True)
return
# Save to database
try:
self.db_cursor.execute(
"INSERT INTO reaction_roles (message_id, guild_id, role_id, emoji) VALUES (?, ?, ?, ?)",
(message.id, interaction.guild.id, role.id, emoji)
)
self.db_connection.commit()
except sqlite3.IntegrityError:
await interaction.response.send_message("Diese Emoji-Rollen-Kombination existiert bereits für diese Nachricht.", ephemeral=True)
return
await interaction.response.send_message(f"✅ Reaktions-Rolle hinzugefügt: Wenn jemand mit {emoji} auf die Nachricht reagiert, erhält er/sie die Rolle '{role.name}'.", ephemeral=True)
@commands.Cog.listener()
async def on_raw_reaction_add(self, payload: discord.RawReactionActionEvent):
# Ignore reactions from bots
if payload.member.bot:
return
# Check if this reaction is for a configured reaction role
self.db_cursor.execute("SELECT role_id FROM reaction_roles WHERE message_id = ? AND emoji = ?", (payload.message_id, str(payload.emoji)))
result = self.db_cursor.fetchone()
if result:
role_id = result[0]
guild = self.bot.get_guild(payload.guild_id)
role = guild.get_role(role_id)
if role:
try:
await payload.member.add_roles(role, reason="Reaction Role")
except discord.Forbidden:
# Bot has insufficient permissions
pass
@commands.Cog.listener()
async def on_raw_reaction_remove(self, payload: discord.RawReactionActionEvent):
# Check if this reaction is for a configured reaction role
self.db_cursor.execute("SELECT role_id FROM reaction_roles WHERE message_id = ? AND emoji = ?", (payload.message_id, str(payload.emoji)))
result = self.db_cursor.fetchone()
if result:
role_id = result[0]
guild = self.bot.get_guild(payload.guild_id)
member = guild.get_member(payload.user_id)
role = guild.get_role(role_id)
if guild and member and role:
try:
await member.remove_roles(role, reason="Reaction Role")
except discord.Forbidden:
# Bot has insufficient permissions
pass
async def setup(bot: commands.Bot):
await bot.add_cog(ReactionRoles(bot))

65
cogs/search.py Normal file
View File

@@ -0,0 +1,65 @@
import discord
from discord.ext import commands
from discord import app_commands
import wikipedia
from googlesearch import search
class Search(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
# Set Wikipedia language to German
wikipedia.set_lang("de")
@app_commands.command(name="google", description="Sucht bei Google und zeigt die Top-Ergebnisse an.")
@app_commands.describe(query="Wonach möchtest du suchen?")
async def google_search(self, interaction: discord.Interaction, query: str):
await interaction.response.defer(ephemeral=True)
try:
results = []
# We use a generator and stop after 5 results
for j in search(query, num=5, stop=5, pause=1.0):
results.append(j)
if not results:
await interaction.followup.send("Ich konnte keine Ergebnisse für deine Suche finden.")
return
description = ""
for i, result in enumerate(results):
description += f"{i+1}. [{result.split('//')[1].split('/')[0]}]({result})\n"
embed = discord.Embed(
title=f"Google-Ergebnisse für: `{query}`",
description=description,
color=discord.Color.from_rgb(66, 133, 244) # Google Blue
)
await interaction.followup.send(embed=embed)
except Exception as e:
await interaction.followup.send(f"Ein Fehler ist bei der Google-Suche aufgetreten: {e}")
@app_commands.command(name="wikipedia", description="Durchsucht Wikipedia nach einem Artikel.")
@app_commands.describe(query="Wonach möchtest du suchen?")
async def wikipedia_search(self, interaction: discord.Interaction, query: str):
await interaction.response.defer()
try:
# Get the summary of the first result, auto-suggesting corrections
page = wikipedia.page(query, auto_suggest=True, redirect=True)
summary = wikipedia.summary(query, sentences=3, auto_suggest=True, redirect=True)
embed = discord.Embed(
title=f"Wikipedia: {page.title}",
url=page.url,
description=summary,
color=discord.Color.light_grey()
)
await interaction.followup.send(embed=embed)
except wikipedia.exceptions.PageError:
await interaction.followup.send(f"Ich konnte keinen Wikipedia-Artikel für `{query}` finden.", ephemeral=True)
except wikipedia.exceptions.DisambiguationError as e:
options_list = "\n".join(e.options[:5])
await interaction.followup.send(f"Deine Suche ist mehrdeutig. Meintest du vielleicht:\n`{options_list}`", ephemeral=True)
async def setup(bot: commands.Bot):
await bot.add_cog(Search(bot))

55
cogs/utility.py Normal file
View File

@@ -0,0 +1,55 @@
import discord
from discord.ext import commands
from discord import app_commands
import time
class Utility(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
@app_commands.command(name="ping", description="Zeigt die Latenz des Bots an.")
async def ping(self, interaction: discord.Interaction):
"""Zeigt die Latenz des Bots an."""
# Die Latenz der Discord-API (Websocket)
api_latency = round(self.bot.latency * 1000)
# Sende eine Bestätigungsnachricht und messe die Zeit dafür
start_time = time.time()
await interaction.response.send_message("Pinging...", ephemeral=True)
end_time = time.time()
# Berechne die Antwortzeit
response_latency = round((end_time - start_time) * 1000)
embed = discord.Embed(title="Pong! 🏓", color=discord.Color.blurple())
embed.add_field(name="API Latenz", value=f"{api_latency}ms", inline=True)
embed.add_field(name="Antwortzeit", value=f"{response_latency}ms", inline=True)
# Bearbeite die ursprüngliche Nachricht mit dem Ergebnis
await interaction.edit_original_response(content=None, embed=embed)
@app_commands.command(name="userinfo", description="Zeigt Informationen über ein Mitglied an.")
@app_commands.describe(member="Das Mitglied, dessen Infos du sehen möchtest (optional).")
async def userinfo(self, interaction: discord.Interaction, member: discord.Member = None):
"""Zeigt Informationen über dich oder ein anderes Mitglied an."""
# Wenn kein Mitglied angegeben wird, nimm den Autor des Befehls
target = member or interaction.user
embed = discord.Embed(
title=f"Informationen über {target.display_name}",
color=target.color
)
embed.set_thumbnail(url=target.avatar.url if target.avatar else target.default_avatar.url)
embed.add_field(name="Name", value=f"{target.name}#{target.discriminator}", inline=True)
embed.add_field(name="ID", value=target.id, inline=True)
embed.add_field(name="Status", value=str(target.status).title(), inline=True)
embed.add_field(name="Server beigetreten am", value=target.joined_at.strftime("%d.%m.%Y, %H:%M:%S"), inline=False)
embed.add_field(name="Account erstellt am", value=target.created_at.strftime("%d.%m.%Y, %H:%M:%S"), inline=False)
await interaction.response.send_message(embed=embed)
async def setup(bot: commands.Bot):
await bot.add_cog(Utility(bot))

26
cogs/welcome.py Normal file
View File

@@ -0,0 +1,26 @@
import discord
from discord.ext import commands
from discord import app_commands
class Welcome(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
@commands.Cog.listener()
async def on_member_join(self, member: discord.Member):
"""Wird ausgelöst, wenn ein neues Mitglied dem Server beitritt."""
guild = member.guild
welcome_channel = guild.system_channel
if welcome_channel is not None:
embed = discord.Embed(
title=f"Willkommen auf {guild.name}!",
description=f"Hallo {member.mention}, wir freuen uns, dich hier zu sehen! Schau dich doch mal um.",
color=discord.Color.green()
)
embed.set_thumbnail(url=member.avatar.url if member.avatar else member.default_avatar.url)
embed.set_footer(text=f"Du bist das {guild.member_count}. Mitglied!")
await welcome_channel.send(embed=embed)
async def setup(bot: commands.Bot):
await bot.add_cog(Welcome(bot))

28
features.txt Normal file
View File

@@ -0,0 +1,28 @@
# Funktionssammlung für einen Multifunktions-Discord-Bot (inspiriert von MEE6 & Dyno)
## 1. Moderation (Kernfunktionen)
- **Auto-Moderator:** Automatisches Löschen/Bestrafen bei Spam, Schimpfwörtern, zu vielen Großbuchstaben, exzessiven Erwähnungen etc.
- **Audit-Log:** Protokollierung aller wichtigen Aktionen (gelöschte Nachrichten, bearbeitete Nachrichten, gebannte Mitglieder etc.) in einem bestimmten Kanal.
- **Warn-System:** Verwarnen von Mitgliedern mit Befehlen. Bei X Verwarnungen erfolgt eine automatische Aktion (Mute, Kick, Ban).
- **Mute/Timeout:** Mitglieder für eine bestimmte Zeit stummschalten.
- **Kick/Ban:** Mitglieder vom Server entfernen.
- **Slowmode:** Verlangsamt die Schreibgeschwindigkeit in einem Kanal.
- **Lock/Unlock:** Sperrt oder entsperrt einen Kanal für normale Mitglieder.
## 2. Community-Management & Engagement
- **Willkommensnachrichten:** Automatische Begrüßung neuer Mitglieder (mit Bannern, Texten, Rollen).
- **Leveling-System:** Mitglieder erhalten XP für Aktivität und steigen im Level auf. Belohnungen (Rollen) bei bestimmten Leveln.
- **Leaderboard:** Eine Rangliste der aktivsten Mitglieder.
- **Reaktions-Rollen (Reaction Roles):** Mitglieder können sich durch Reagieren auf eine Nachricht selbst Rollen zuweisen.
- **Custom Commands:** Server-Admins können eigene, einfache Text-Befehle erstellen.
- **Umfragen (Polls):** Einfaches Erstellen von Abstimmungen.
## 3. Benachrichtigungen & Ankündigungen
- **Social Media Alerts:** Benachrichtigungen, wenn ein verknüpfter Kanal (YouTube, Twitch, Twitter/X) live geht oder neuen Inhalt postet.
- **Ankündigungs-Befehl:** Ein formatierter Befehl, um wichtige Nachrichten an den ganzen Server zu senden.
## 4. Nützliches & Unterhaltung
- **Musik-Player:** Abspielen von Musik aus Quellen wie YouTube oder Spotify (Achtung: YouTube erschwert dies zunehmend).
- **Such-Befehle:** Suchen nach Informationen auf Google, Wikipedia, Urban Dictionary etc.
- **Statistiken:** Anzeigen von Server- oder Nutzerstatistiken.
- **Giveaways:** Erstellen und Verwalten von Verlosungen.

48
main.py Normal file
View File

@@ -0,0 +1,48 @@
import os
import discord
from discord.ext import commands
from dotenv import load_dotenv
# Lade die Umgebungsvariablen aus der .env-Datei
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
# Definiere die Intents (Berechtigungen), die dein Bot benötigt.
intents = discord.Intents.default()
intents.members = True # Notwendig für Willkommensnachrichten
intents.message_content = True # Notwendig, um Nachrichten zu lesen
# Erstelle die Bot-Instanz
bot = commands.Bot(command_prefix=None, intents=intents)
# Event, das beim Starten des Bots ausgeführt wird
@bot.event
async def on_ready():
"""Wird aufgerufen, wenn der Bot erfolgreich mit Discord verbunden ist."""
print(f'Erfolgreich eingeloggt als {bot.user.name} (ID: {bot.user.id})')
print('--------------------------------------------------')
await load_cogs()
# Synchronisiere die Slash-Commands mit Discord
try:
synced = await bot.tree.sync()
print(f'{len(synced)} Slash-Command(s) wurden synchronisiert.')
except Exception as e:
print(f'Fehler beim Synchronisieren der Commands: {e}')
async def load_cogs():
"""Sucht im 'cogs'-Ordner nach Python-Dateien und lädt sie als Erweiterungen."""
print("Lade Cogs...")
for filename in os.listdir('./cogs'):
if filename.endswith('.py'):
try:
await bot.load_extension(f'cogs.{filename[:-3]}')
print(f'-> Cog "{filename[:-3]}" wurde geladen.')
except Exception as e:
print(f'Fehler beim Laden von Cog "{filename[:-3]}": {e}')
# Starte den Bot mit dem Token
if __name__ == "__main__":
if TOKEN is None:
print("FEHLER: DISCORD_TOKEN wurde nicht in der .env-Datei gefunden!")
else:
bot.run(TOKEN)

4
requirements.txt Normal file
View File

@@ -0,0 +1,4 @@
discord.py>=2.0.0
python-dotenv
wikipedia-api
googlesearch-python