Spaces:
Running
Running
Update app/app.py
Browse files- app/app.py +184 -34
app/app.py
CHANGED
@@ -8,26 +8,38 @@ import threading
|
|
8 |
import requests
|
9 |
import time
|
10 |
from commands import ping_command, settings_command
|
|
|
11 |
|
12 |
# Logging konfigurieren
|
13 |
-
logging.basicConfig(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
logger = logging.getLogger(__name__)
|
15 |
|
16 |
-
# Konfiguration
|
17 |
PUBLIC_KEY = os.getenv('Public_Key', '').strip()
|
18 |
APPLICATION_ID = os.getenv('Application_ID', '').strip()
|
19 |
-
PORT = int(os.getenv('PORT', 7860)) # Hugging Face
|
20 |
|
|
|
21 |
app = Flask(__name__)
|
|
|
22 |
|
|
|
23 |
try:
|
24 |
verify_key = VerifyKey(bytes.fromhex(PUBLIC_KEY)) if PUBLIC_KEY else None
|
25 |
-
logger.info("
|
26 |
except Exception as e:
|
27 |
-
logger.error(f"
|
28 |
verify_key = None
|
29 |
|
30 |
def verify_discord_request():
|
|
|
31 |
try:
|
32 |
signature = request.headers.get('X-Signature-Ed25519')
|
33 |
timestamp = request.headers.get('X-Signature-Timestamp')
|
@@ -37,63 +49,201 @@ def verify_discord_request():
|
|
37 |
verify_key.verify(f"{timestamp}{body}".encode(), bytes.fromhex(signature))
|
38 |
return True
|
39 |
except Exception as e:
|
40 |
-
logger.error(f"
|
41 |
return False
|
42 |
|
43 |
-
|
44 |
-
def
|
45 |
-
"""
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
|
|
|
53 |
@app.route("/interactions", methods=["POST"])
|
54 |
def interactions():
|
|
|
55 |
if not verify_key:
|
56 |
-
logger.error("
|
57 |
-
return "
|
58 |
|
59 |
if not verify_discord_request():
|
60 |
-
return "
|
61 |
|
62 |
try:
|
63 |
data = request.json
|
64 |
|
65 |
# Discord Ping Verification
|
66 |
if data.get("type") == 1:
|
67 |
-
return
|
68 |
|
69 |
# Slash Commands
|
70 |
if data.get("type") == 2:
|
71 |
command = data.get("data", {}).get("name")
|
72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
return settings_command(data)
|
|
|
|
|
74 |
|
75 |
return jsonify({"type": 1})
|
76 |
|
77 |
except Exception as e:
|
78 |
-
logger.error(f"
|
79 |
-
return "
|
80 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
def health_check_worker():
|
82 |
-
"""Background
|
83 |
while True:
|
84 |
try:
|
85 |
response = requests.get(f"http://localhost:{PORT}/")
|
86 |
-
logger.info(f"Health
|
87 |
except Exception as e:
|
88 |
-
logger.error(f"Health
|
89 |
-
time.sleep(30)
|
90 |
|
|
|
91 |
if __name__ == "__main__":
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
import requests
|
9 |
import time
|
10 |
from commands import ping_command, settings_command
|
11 |
+
from bank import BankSystem, BankAccount, TransactionError
|
12 |
|
13 |
# Logging konfigurieren
|
14 |
+
logging.basicConfig(
|
15 |
+
level=logging.INFO,
|
16 |
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
17 |
+
handlers=[
|
18 |
+
logging.FileHandler('discord_bot.log'),
|
19 |
+
logging.StreamHandler()
|
20 |
+
]
|
21 |
+
)
|
22 |
logger = logging.getLogger(__name__)
|
23 |
|
24 |
+
# Umgebungsvariablen und Konfiguration
|
25 |
PUBLIC_KEY = os.getenv('Public_Key', '').strip()
|
26 |
APPLICATION_ID = os.getenv('Application_ID', '').strip()
|
27 |
+
PORT = int(os.getenv('PORT', 7860)) # Hugging Face Standard-Port
|
28 |
|
29 |
+
# Flask App und Bank-System initialisieren
|
30 |
app = Flask(__name__)
|
31 |
+
bank_system = BankSystem()
|
32 |
|
33 |
+
# Discord Verifizierungsschlüssel initialisieren
|
34 |
try:
|
35 |
verify_key = VerifyKey(bytes.fromhex(PUBLIC_KEY)) if PUBLIC_KEY else None
|
36 |
+
logger.info("Discord Verifizierungsschlüssel erfolgreich initialisiert")
|
37 |
except Exception as e:
|
38 |
+
logger.error(f"Fehler beim Initialisieren des Verifizierungsschlüssels: {str(e)}")
|
39 |
verify_key = None
|
40 |
|
41 |
def verify_discord_request():
|
42 |
+
"""Überprüft die Authentizität der Discord-Anfrage"""
|
43 |
try:
|
44 |
signature = request.headers.get('X-Signature-Ed25519')
|
45 |
timestamp = request.headers.get('X-Signature-Timestamp')
|
|
|
49 |
verify_key.verify(f"{timestamp}{body}".encode(), bytes.fromhex(signature))
|
50 |
return True
|
51 |
except Exception as e:
|
52 |
+
logger.error(f"Verifikationsfehler: {str(e)}")
|
53 |
return False
|
54 |
|
55 |
+
# Bank-bezogene Command-Handler
|
56 |
+
def handle_balance_command(data):
|
57 |
+
"""Verarbeitet den Balance-Check Befehl"""
|
58 |
+
user_id = data.get("member", {}).get("user", {}).get("id")
|
59 |
+
try:
|
60 |
+
account = bank_system.get_account(user_id)
|
61 |
+
if not account:
|
62 |
+
account = bank_system.create_account(user_id)
|
63 |
+
return jsonify({
|
64 |
+
"type": 4,
|
65 |
+
"data": {
|
66 |
+
"content": f"💰 Dein Kontostand: {account.format_balance()}"
|
67 |
+
}
|
68 |
+
})
|
69 |
+
except Exception as e:
|
70 |
+
logger.error(f"Fehler beim Balance-Check: {str(e)}")
|
71 |
+
return jsonify({
|
72 |
+
"type": 4,
|
73 |
+
"data": {
|
74 |
+
"content": "❌ Fehler beim Abrufen des Kontostands."
|
75 |
+
}
|
76 |
+
})
|
77 |
+
|
78 |
+
def handle_transfer_command(data):
|
79 |
+
"""Verarbeitet Überweisungsbefehle"""
|
80 |
+
options = data.get("data", {}).get("options", [{}])[0].get("options", [])
|
81 |
+
user_id = data.get("member", {}).get("user", {}).get("id")
|
82 |
+
|
83 |
+
# Parameter aus den Optionen extrahieren
|
84 |
+
target_id = next((opt.get("value") for opt in options if opt.get("name") == "user"), None)
|
85 |
+
amount = next((float(opt.get("value")) for opt in options if opt.get("name") == "amount"), None)
|
86 |
+
|
87 |
+
try:
|
88 |
+
bank_system.transfer_between_accounts(user_id, target_id, amount)
|
89 |
+
return jsonify({
|
90 |
+
"type": 4,
|
91 |
+
"data": {
|
92 |
+
"content": f"✅ Erfolgreich {amount:,.2f} Credits an <@{target_id}> überwiesen!"
|
93 |
+
}
|
94 |
+
})
|
95 |
+
except TransactionError as e:
|
96 |
+
return jsonify({
|
97 |
+
"type": 4,
|
98 |
+
"data": {
|
99 |
+
"content": f"❌ Überweisungsfehler: {str(e)}"
|
100 |
+
}
|
101 |
+
})
|
102 |
+
except Exception as e:
|
103 |
+
logger.error(f"Unerwarteter Fehler bei Überweisung: {str(e)}")
|
104 |
+
return jsonify({
|
105 |
+
"type": 4,
|
106 |
+
"data": {
|
107 |
+
"content": "❌ Ein unerwarteter Fehler ist aufgetreten."
|
108 |
+
}
|
109 |
+
})
|
110 |
+
|
111 |
+
def handle_daily_command(data):
|
112 |
+
"""Verarbeitet den täglichen Bonus-Befehl"""
|
113 |
+
user_id = data.get("member", {}).get("user", {}).get("id")
|
114 |
+
try:
|
115 |
+
result = bank_system.daily_reward(user_id)
|
116 |
+
return jsonify({
|
117 |
+
"type": 4,
|
118 |
+
"data": {
|
119 |
+
"content": f"🎁 {result}"
|
120 |
+
}
|
121 |
+
})
|
122 |
+
except Exception as e:
|
123 |
+
logger.error(f"Fehler beim Daily Reward: {str(e)}")
|
124 |
+
return jsonify({
|
125 |
+
"type": 4,
|
126 |
+
"data": {
|
127 |
+
"content": "❌ Fehler beim Abholen der täglichen Belohnung."
|
128 |
+
}
|
129 |
+
})
|
130 |
|
131 |
+
# Haupt-Route für Discord-Interaktionen
|
132 |
@app.route("/interactions", methods=["POST"])
|
133 |
def interactions():
|
134 |
+
"""Hauptendpunkt für alle Discord-Interaktionen"""
|
135 |
if not verify_key:
|
136 |
+
logger.error("Verifizierungsschlüssel nicht initialisiert")
|
137 |
+
return "Konfigurationsfehler", 500
|
138 |
|
139 |
if not verify_discord_request():
|
140 |
+
return "Ungültige Anfrage-Signatur", 401
|
141 |
|
142 |
try:
|
143 |
data = request.json
|
144 |
|
145 |
# Discord Ping Verification
|
146 |
if data.get("type") == 1:
|
147 |
+
return jsonify({"type": 1})
|
148 |
|
149 |
# Slash Commands
|
150 |
if data.get("type") == 2:
|
151 |
command = data.get("data", {}).get("name")
|
152 |
+
|
153 |
+
# Bank-bezogene Befehle
|
154 |
+
if command == "bank":
|
155 |
+
subcommand = data.get("data", {}).get("options", [{}])[0].get("name")
|
156 |
+
if subcommand == "balance":
|
157 |
+
return handle_balance_command(data)
|
158 |
+
elif subcommand == "transfer":
|
159 |
+
return handle_transfer_command(data)
|
160 |
+
elif subcommand == "daily":
|
161 |
+
return handle_daily_command(data)
|
162 |
+
|
163 |
+
# Andere Befehle
|
164 |
+
elif command == "settings":
|
165 |
return settings_command(data)
|
166 |
+
elif command == "ping":
|
167 |
+
return ping_command(data)
|
168 |
|
169 |
return jsonify({"type": 1})
|
170 |
|
171 |
except Exception as e:
|
172 |
+
logger.error(f"Fehler bei der Verarbeitung der Anfrage: {str(e)}")
|
173 |
+
return "Interner Serverfehler", 500
|
174 |
|
175 |
+
# Health Check Endpoint
|
176 |
+
@app.route("/", methods=["GET"])
|
177 |
+
def health_check():
|
178 |
+
"""Endpoint für Hugging Face Health Checks"""
|
179 |
+
return jsonify({
|
180 |
+
"status": "running",
|
181 |
+
"message": "Discord Bot läuft!",
|
182 |
+
"public_key_present": bool(PUBLIC_KEY),
|
183 |
+
"verify_key_initialized": verify_key is not None,
|
184 |
+
"bank_system_status": "active"
|
185 |
+
})
|
186 |
def health_check_worker():
|
187 |
+
"""Background Worker für regelmäßige Health Checks"""
|
188 |
while True:
|
189 |
try:
|
190 |
response = requests.get(f"http://localhost:{PORT}/")
|
191 |
+
logger.info(f"Health Check Status: {response.status_code}")
|
192 |
except Exception as e:
|
193 |
+
logger.error(f"Health Check fehlgeschlagen: {str(e)}")
|
194 |
+
time.sleep(30) # 30 Sekunden Pause zwischen den Checks
|
195 |
|
196 |
+
# Hauptausführung
|
197 |
if __name__ == "__main__":
|
198 |
+
try:
|
199 |
+
logger.info(f"Starte Discord Bot auf Port {PORT}...")
|
200 |
+
logger.info("Initialisiere Bank-System...")
|
201 |
+
|
202 |
+
# Starte Health-Check Worker in separatem Thread
|
203 |
+
health_thread = threading.Thread(target=health_check_worker, daemon=True)
|
204 |
+
health_thread.start()
|
205 |
+
|
206 |
+
# Registriere Slash-Commands (einmalig bei Bot-Start)
|
207 |
+
BANK_COMMANDS = {
|
208 |
+
"name": "bank",
|
209 |
+
"description": "Bank-bezogene Befehle",
|
210 |
+
"options": [
|
211 |
+
{
|
212 |
+
"name": "balance",
|
213 |
+
"description": "Zeigt deinen aktuellen Kontostand",
|
214 |
+
"type": 1
|
215 |
+
},
|
216 |
+
{
|
217 |
+
"name": "transfer",
|
218 |
+
"description": "Überweise Credits an einen anderen Benutzer",
|
219 |
+
"type": 1,
|
220 |
+
"options": [
|
221 |
+
{
|
222 |
+
"name": "user",
|
223 |
+
"description": "Der Empfänger der Überweisung",
|
224 |
+
"type": 6,
|
225 |
+
"required": True
|
226 |
+
},
|
227 |
+
{
|
228 |
+
"name": "amount",
|
229 |
+
"description": "Die Menge an Credits",
|
230 |
+
"type": 10,
|
231 |
+
"required": True
|
232 |
+
}
|
233 |
+
]
|
234 |
+
},
|
235 |
+
{
|
236 |
+
"name": "daily",
|
237 |
+
"description": "Hole dir deine tägliche Belohnung ab",
|
238 |
+
"type": 1
|
239 |
+
}
|
240 |
+
]
|
241 |
+
}
|
242 |
+
|
243 |
+
# Starte Server
|
244 |
+
logger.info("Server wird gestartet...")
|
245 |
+
serve(app, host="0.0.0.0", port=PORT)
|
246 |
+
|
247 |
+
except Exception as e:
|
248 |
+
logger.critical(f"Kritischer Fehler beim Starten des Bots: {str(e)}")
|
249 |
+
raise
|