Spaces:
Running
Running
# Discord Bot for our community : https://github.com/TSOWatch/HF-Discord-Bot/ | |
# based on discord boilerplate : https://github.com/VolkanSah/HF-Discord-Bot | |
from flask import Flask, request, jsonify | |
import os | |
import logging | |
from waitress import serve | |
from nacl.signing import VerifyKey | |
from nacl.exceptions import BadSignatureError | |
import threading | |
import requests | |
import time | |
from commands import ping_command, settings_command | |
from bank import BankSystem, BankAccount, TransactionError | |
# Logging konfigurieren: Nur in die Konsole ausgeben | |
logging.basicConfig( | |
level=logging.INFO, | |
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', | |
handlers=[ | |
logging.StreamHandler() | |
] | |
) | |
logger = logging.getLogger(__name__) | |
# Umgebungsvariablen und Konfiguration | |
PUBLIC_KEY = os.getenv('Public_Key', '').strip() | |
APPLICATION_ID = os.getenv('Application_ID', '').strip() | |
PORT = int(os.getenv('PORT', 7860)) # Hugging Face Standard-Port | |
# Flask App und Bank-System initialisieren | |
app = Flask(__name__) | |
bank_system = BankSystem() | |
# Discord Verifizierungsschlüssel initialisieren | |
try: | |
verify_key = VerifyKey(bytes.fromhex(PUBLIC_KEY)) if PUBLIC_KEY else None | |
logger.info("Discord Verifizierungsschlüssel erfolgreich initialisiert") | |
except Exception as e: | |
logger.error(f"Fehler beim Initialisieren des Verifizierungsschlüssels: {str(e)}") | |
verify_key = None | |
def verify_discord_request(): | |
"""Überprüft die Authentizität der Discord-Anfrage""" | |
try: | |
signature = request.headers.get('X-Signature-Ed25519') | |
timestamp = request.headers.get('X-Signature-Timestamp') | |
if not signature or not timestamp: | |
return False | |
body = request.data.decode('utf-8') | |
verify_key.verify(f"{timestamp}{body}".encode(), bytes.fromhex(signature)) | |
return True | |
except Exception as e: | |
logger.error(f"Verifikationsfehler: {str(e)}") | |
return False | |
# Bank-bezogene Command-Handler | |
def handle_balance_command(data): | |
"""Verarbeitet den Balance-Check Befehl""" | |
user_id = data.get("member", {}).get("user", {}).get("id") | |
try: | |
account = bank_system.get_account(user_id) | |
if not account: | |
account = bank_system.create_account(user_id) | |
return jsonify({ | |
"type": 4, | |
"data": { | |
"content": f"💰 Dein Kontostand: {account.format_balance()}" | |
} | |
}) | |
except Exception as e: | |
logger.error(f"Fehler beim Balance-Check: {str(e)}") | |
return jsonify({ | |
"type": 4, | |
"data": { | |
"content": "❌ Fehler beim Abrufen des Kontostands." | |
} | |
}) | |
def handle_transfer_command(data): | |
"""Verarbeitet Überweisungsbefehle""" | |
options = data.get("data", {}).get("options", [{}])[0].get("options", []) | |
user_id = data.get("member", {}).get("user", {}).get("id") | |
# Parameter aus den Optionen extrahieren | |
target_id = next((opt.get("value") for opt in options if opt.get("name") == "user"), None) | |
amount = next((float(opt.get("value")) for opt in options if opt.get("name") == "amount"), None) | |
try: | |
bank_system.transfer_between_accounts(user_id, target_id, amount) # Richtige Einrückung | |
return jsonify({ | |
"type": 4, | |
"data": { | |
"content": f"✅ Erfolgreich {amount:,.2f} Credits an <@{target_id}> überwiesen!" | |
} | |
}) | |
except TransactionError as e: | |
return jsonify({ | |
"type": 4, | |
"data": { | |
"content": f"❌ Überweisungsfehler: {str(e)}" | |
} | |
}) | |
except Exception as e: | |
logger.error(f"Unerwarteter Fehler bei Überweisung: {str(e)}") | |
return jsonify({ | |
"type": 4, | |
"data": { | |
"content": "❌ Ein unerwarteter Fehler ist aufgetreten." | |
} | |
}) | |
def handle_daily_command(data): | |
"""Verarbeitet den täglichen Bonus-Befehl""" | |
user_id = data.get("member", {}).get("user", {}).get("id") | |
try: | |
result = bank_system.daily_reward(user_id) | |
return jsonify({ | |
"type": 4, | |
"data": { | |
"content": f"🎁 {result}" | |
} | |
}) | |
except Exception as e: | |
logger.error(f"Fehler beim Daily Reward: {str(e)}") | |
return jsonify({ | |
"type": 4, | |
"data": { | |
"content": "❌ Fehler beim Abholen der täglichen Belohnung." | |
} | |
}) | |
# Haupt-Route für Discord-Interaktionen | |
def interactions(): | |
"""Hauptendpunkt für alle Discord-Interaktionen""" | |
if not verify_key: | |
logger.error("Verifizierungsschlüssel nicht initialisiert") | |
return "Konfigurationsfehler", 500 | |
if not verify_discord_request(): | |
return "Ungültige Anfrage-Signatur", 401 | |
try: | |
data = request.json | |
# Discord Ping Verification | |
if data.get("type") == 1: | |
return jsonify({"type": 1}) | |
# Slash Commands | |
if data.get("type") == 2: | |
command = data.get("data", {}).get("name") | |
# Bank-bezogene Befehle | |
if command == "bank": | |
subcommand = data.get("data", {}).get("options", [{}])[0].get("name") | |
if subcommand == "balance": | |
return handle_balance_command(data) | |
elif subcommand == "transfer": | |
return handle_transfer_command(data) | |
elif subcommand == "daily": | |
return handle_daily_command(data) | |
# Andere Befehle | |
elif command == "settings": | |
return settings_command(data) | |
elif command == "ping": | |
return ping_command(data) | |
return jsonify({"type": 1}) | |
except Exception as e: | |
logger.error(f"Fehler bei der Verarbeitung der Anfrage: {str(e)}") | |
return "Interner Serverfehler", 500 | |
# Health Check Endpoint | |
def health_check(): | |
"""Endpoint für Hugging Face Health Checks""" | |
return jsonify({ | |
"status": "running", | |
"message": "Discord Bot läuft!", | |
"public_key_present": bool(PUBLIC_KEY), | |
"verify_key_initialized": verify_key is not None, | |
"bank_system_status": "active" | |
}) | |
def health_check_worker(): | |
"""Background Worker für regelmäßige Health Checks""" | |
while True: | |
try: | |
response = requests.get(f"http://localhost:{PORT}/") | |
logger.info(f"Health Check Status: {response.status_code}") | |
except Exception as e: | |
logger.error(f"Health Check fehlgeschlagen: {str(e)}") | |
time.sleep(30) # 30 Sekunden Pause zwischen den Checks | |
# Hauptausführung | |
if __name__ == "__main__": | |
try: | |
logger.info(f"Starte Discord Bot auf Port {PORT}...") | |
logger.info("Initialisiere Bank-System...") | |
# Starte Health-Check Worker in separatem Thread | |
health_thread = threading.Thread(target=health_check_worker, daemon=True) | |
health_thread.start() | |
# Registriere Slash-Commands (einmalig bei Bot-Start) | |
BANK_COMMANDS = { | |
"name": "bank", | |
"description": "Bank-bezogene Befehle", | |
"options": [ | |
{ | |
"name": "balance", | |
"description": "Zeigt deinen aktuellen Kontostand", | |
"type": 1 | |
}, | |
{ | |
"name": "transfer", | |
"description": "Überweise Credits an einen anderen Benutzer", | |
"type": 1, | |
"options": [ | |
{ | |
"name": "user", | |
"description": "Der Empfänger der Überweisung", | |
"type": 6, | |
"required": True | |
}, | |
{ | |
"name": "amount", | |
"description": "Die Menge an Credits", | |
"type": 10, | |
"required": True | |
} | |
] | |
}, | |
{ | |
"name": "daily", | |
"description": "Hole dir deine tägliche Belohnung ab", | |
"type": 1 | |
} | |
] | |
} | |
# Starte Server | |
logger.info("Server wird gestartet...") | |
serve(app, host="0.0.0.0", port=PORT) | |
except Exception as e: | |
logger.critical(f"Kritischer Fehler beim Starten des Bots: {str(e)}") | |
raise |