modified: bot.py

This commit is contained in:
SimolZimol
2025-08-17 22:44:29 +02:00
parent c8a74a46e1
commit c7515e19b3

154
bot.py
View File

@@ -22,6 +22,8 @@ from bs4 import BeautifulSoup
from dotenv import load_dotenv from dotenv import load_dotenv
import random import random
import time import time
import hashlib
from urllib.parse import urlparse
load_dotenv() load_dotenv()
@@ -204,10 +206,17 @@ def close_database_connection(connection):
if connection and connection.is_connected(): if connection and connection.is_connected():
connection.close() connection.close()
def create_user_data_with_member(user_id, guild_id, member=None): async def create_user_data_with_member(user_id, guild_id, member=None):
"""Erstellt neue User-Daten mit korrekten Informationen vom Member-Objekt""" """Erstellt neue User-Daten mit korrekten Informationen vom Member-Objekt"""
nickname = member.display_name if member else "" nickname = member.display_name if member else ""
profile_picture = str(member.display_avatar.url) if member and member.display_avatar else None
# Profilbild herunterladen und lokal speichern
if member and member.display_avatar:
discord_avatar_url = str(member.display_avatar.url)
profile_picture = await download_and_save_profile_image(user_id, discord_avatar_url)
else:
profile_picture = "/static/default_profile.png"
join_date = member.joined_at.date() if member and member.joined_at else None join_date = member.joined_at.date() if member and member.joined_at else None
user_data = { user_data = {
@@ -242,7 +251,7 @@ def create_user_data_with_member(user_id, guild_id, member=None):
join_date join_date
) )
logger.info(f"Created new user data for {nickname} (ID: {user_id}) with join_date: {join_date}") logger.info(f"Created new user data for {nickname} (ID: {user_id}) with join_date: {join_date} and profile_picture: {profile_picture}")
return user_data return user_data
def load_user_data_from_mysql(user_id, guild_id): def load_user_data_from_mysql(user_id, guild_id):
@@ -344,7 +353,7 @@ def remove_user_data_from_cache(user_id, guild_id):
pending_deletion[(user_id, guild_id)].cancel() pending_deletion[(user_id, guild_id)].cancel()
del pending_deletion[(user_id, guild_id)] del pending_deletion[(user_id, guild_id)]
def load_user_data(user_id, guild_id, member=None): async def load_user_data(user_id, guild_id, member=None):
if (user_id, guild_id) in cached_user_data: if (user_id, guild_id) in cached_user_data:
return cached_user_data[(user_id, guild_id)] return cached_user_data[(user_id, guild_id)]
@@ -353,7 +362,50 @@ def load_user_data(user_id, guild_id, member=None):
# Wenn keine User-Daten existieren, erstelle neue mit Member-Informationen # Wenn keine User-Daten existieren, erstelle neue mit Member-Informationen
if not user_data or user_data.get("user_id") is None: if not user_data or user_data.get("user_id") is None:
user_data = create_user_data_with_member(user_id, guild_id, member) user_data = await create_user_data_with_member(user_id, guild_id, member)
asyncio.ensure_future(cache_user_data(user_id, guild_id, user_data))
return user_data
def load_user_data_sync(user_id, guild_id):
"""Synchrone Version von load_user_data für bestehende Commands"""
if (user_id, guild_id) in cached_user_data:
return cached_user_data[(user_id, guild_id)]
# Daten aus der Datenbank laden oder neu anlegen
user_data = load_user_data_from_mysql(user_id, guild_id)
# Wenn keine User-Daten existieren, erstelle neue mit Default-Werten
if not user_data or user_data.get("user_id") is None:
user_data = {
"user_id": user_id,
"guild_id": guild_id,
"permission": 0,
"points": 0,
"ban": 0,
"askmultus": 0,
"filter_value": 0,
"rank": 0,
"chat_history": [],
"asknotes_history": [],
"xp": 0,
"level": 1,
"nickname": ""
}
insert_user_data(
user_data["user_id"],
user_data["guild_id"],
user_data["permission"],
user_data["points"],
user_data["ban"],
user_data["askmultus"],
user_data["filter_value"],
user_data["chat_history"],
user_data["xp"],
user_data["level"],
user_data["nickname"],
"/static/default_profile.png"
)
asyncio.ensure_future(cache_user_data(user_id, guild_id, user_data)) asyncio.ensure_future(cache_user_data(user_id, guild_id, user_data))
return user_data return user_data
@@ -619,7 +671,7 @@ class Giveaway:
async def startgiveaway(ctx, platform: str, prize: str, num_winners: int, title: str, subtitle: str, duration: str): async def startgiveaway(ctx, platform: str, prize: str, num_winners: int, title: str, subtitle: str, duration: str):
"""Creates a new giveaway, only available for admins.""" """Creates a new giveaway, only available for admins."""
guild_id = ctx.guild.id guild_id = ctx.guild.id
user_data = load_user_data(ctx.author.id, guild_id) user_data = load_user_data_sync(ctx.author.id, guild_id)
if user_data["permission"] < 5: if user_data["permission"] < 5:
await ctx.send("You don't have permission to create a giveaway.") await ctx.send("You don't have permission to create a giveaway.")
return return
@@ -879,7 +931,7 @@ def calculate_xp_needed_for_level(level):
async def add_xp_to_user(user_id, guild_id, xp_gained, member=None): async def add_xp_to_user(user_id, guild_id, xp_gained, member=None):
"""Fügt einem Benutzer XP hinzu und überprüft, ob er ein Level aufsteigt. Aktualisiert auch Benutzerdaten.""" """Fügt einem Benutzer XP hinzu und überprüft, ob er ein Level aufsteigt. Aktualisiert auch Benutzerdaten."""
# Lade Benutzerdaten (XP, Level, etc.) - mit member-Objekt für neue User # Lade Benutzerdaten (XP, Level, etc.) - mit member-Objekt für neue User
user_data = load_user_data(user_id, guild_id, member) user_data = await load_user_data(user_id, guild_id, member)
# Initialisiere XP, falls es None ist # Initialisiere XP, falls es None ist
user_data["xp"] = user_data.get("xp", 0) user_data["xp"] = user_data.get("xp", 0)
@@ -911,11 +963,22 @@ async def add_xp_to_user(user_id, guild_id, xp_gained, member=None):
update_user_data(user_id, guild_id, "nickname", new_nickname) update_user_data(user_id, guild_id, "nickname", new_nickname)
user_data["nickname"] = new_nickname user_data["nickname"] = new_nickname
# Aktualisiere Profilbild # Aktualisiere Profilbild - mit lokalem Download und Hash-Vergleich
new_profile_picture = str(member.display_avatar.url) if member.display_avatar else None if member.display_avatar:
if user_data.get("profile_picture") != new_profile_picture: discord_avatar_url = str(member.display_avatar.url)
update_user_data(user_id, guild_id, "profile_picture", new_profile_picture) # Download und speichere das Profilbild lokal
user_data["profile_picture"] = new_profile_picture local_profile_path = await download_and_save_profile_image(user_id, discord_avatar_url)
# Speichere den lokalen Pfad in der Datenbank statt der Discord URL
if user_data.get("profile_picture") != local_profile_path:
update_user_data(user_id, guild_id, "profile_picture", local_profile_path)
user_data["profile_picture"] = local_profile_path
else:
# Kein Profilbild vorhanden, nutze Default
default_path = "/static/default_profile.png"
if user_data.get("profile_picture") != default_path:
update_user_data(user_id, guild_id, "profile_picture", default_path)
user_data["profile_picture"] = default_path
# Aktualisiere Join-Datum - IMMER wenn member.joined_at verfügbar ist # Aktualisiere Join-Datum - IMMER wenn member.joined_at verfügbar ist
if member.joined_at: if member.joined_at:
@@ -1047,7 +1110,7 @@ async def on_message(message):
# Optional: Level-Up Benachrichtigung senden # Optional: Level-Up Benachrichtigung senden
if level_up: if level_up:
user_data = load_user_data(user_id, guild_id) user_data = await load_user_data(user_id, guild_id, member)
new_level = user_data["level"] new_level = user_data["level"]
try: try:
await message.channel.send(f"🎉 {member.mention} has reached **Level {new_level}**! Congratulations! 🎉") await message.channel.send(f"🎉 {member.mention} has reached **Level {new_level}**! Congratulations! 🎉")
@@ -1479,6 +1542,71 @@ CACHE_DIR = "cache"
if not os.path.exists(CACHE_DIR): if not os.path.exists(CACHE_DIR):
os.makedirs(CACHE_DIR) os.makedirs(CACHE_DIR)
# Profilbild-Ordner erstellen
PROFILE_IMAGES_DIR = "static/profile_images"
if not os.path.exists(PROFILE_IMAGES_DIR):
os.makedirs(PROFILE_IMAGES_DIR)
def get_url_hash(url):
"""Erstellt einen Hash aus der URL für Vergleichszwecke"""
if not url:
return None
return hashlib.md5(url.encode('utf-8')).hexdigest()
def get_local_profile_path(user_id):
"""Gibt den lokalen Pfad für das Profilbild eines Users zurück"""
return os.path.join(PROFILE_IMAGES_DIR, f"user_{user_id}.png")
def get_web_profile_path(user_id):
"""Gibt den Web-Pfad für das Profilbild eines Users zurück"""
return f"/static/profile_images/user_{user_id}.png"
async def download_and_save_profile_image(user_id, discord_url):
"""Lädt ein Profilbild herunter und speichert es lokal"""
if not discord_url:
return "/static/default_profile.png"
try:
local_path = get_local_profile_path(user_id)
web_path = get_web_profile_path(user_id)
# Überprüfe, ob das Bild bereits existiert und der Hash gleich ist
hash_file = local_path + ".hash"
current_hash = get_url_hash(discord_url)
if os.path.exists(local_path) and os.path.exists(hash_file):
with open(hash_file, 'r') as f:
stored_hash = f.read().strip()
if stored_hash == current_hash:
logger.info(f"Profile image for user {user_id} is up to date, skipping download")
return web_path
# Download das Bild
logger.info(f"Downloading profile image for user {user_id} from {discord_url}")
response = requests.get(discord_url, timeout=10)
if response.status_code == 200:
# Speichere das Bild
with open(local_path, 'wb') as f:
f.write(response.content)
# Speichere den Hash
with open(hash_file, 'w') as f:
f.write(current_hash)
logger.info(f"Successfully downloaded and saved profile image for user {user_id}")
return web_path
else:
logger.warning(f"Failed to download profile image for user {user_id}: HTTP {response.status_code}")
return "/static/default_profile.png"
except Exception as e:
logger.error(f"Error downloading profile image for user {user_id}: {e}")
return "/static/default_profile.png"
# Cache-Ordner für Notizen
@client.hybrid_command() @client.hybrid_command()
async def addnotes(ctx, type: str, *, source: str): async def addnotes(ctx, type: str, *, source: str):
"""Adds a note that can be consulted later.""" """Adds a note that can be consulted later."""