+ Upload
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
|
||||
#Ignore vscode AI rules
|
||||
.github\instructions\codacy.instructions.md
|
54
cogs/announcement.py
Normal file
54
cogs/announcement.py
Normal 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
118
cogs/audit_log.py
Normal 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
90
cogs/custom_commands.py
Normal 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
103
cogs/giveaways.py
Normal 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
205
cogs/leveling.py
Normal 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
401
cogs/moderation.py
Normal 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
54
cogs/polls.py
Normal 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
104
cogs/reaction_roles.py
Normal 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
65
cogs/search.py
Normal 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
55
cogs/utility.py
Normal 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
26
cogs/welcome.py
Normal 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
28
features.txt
Normal 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
48
main.py
Normal 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
4
requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
discord.py>=2.0.0
|
||||
python-dotenv
|
||||
wikipedia-api
|
||||
googlesearch-python
|
Reference in New Issue
Block a user