modified: bot.py
This commit is contained in:
154
bot.py
154
bot.py
@@ -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."""
|
||||||
|
|||||||
Reference in New Issue
Block a user