Wiki-Quellcode von Matrix Bot Terry
Zuletzt geändert von René Schmidt am 2025/10/11 19:30
Zeige letzte Bearbeiter
| author | version | line-number | content |
|---|---|---|---|
| 1 | import asyncio | ||
| 2 | import logging | ||
| 3 | import json | ||
| 4 | import random | ||
| 5 | import time | ||
| 6 | from datetime import datetime | ||
| 7 | from aiohttp import web, ClientSession | ||
| 8 | from nio import AsyncClient, MatrixRoom, RoomMessageText | ||
| 9 | |||
| 10 | # ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-- | ||
| 11 | # Logging-Setup | ||
| 12 | # ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-- | ||
| 13 | logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s") | ||
| 14 | logger = logging.getLogger(~_~_name~_~_) | ||
| 15 | |||
| 16 | # Reduziere Noise von anderen Loggern | ||
| 17 | logging.getLogger("nio").setLevel(logging.WARNING) | ||
| 18 | logging.getLogger("aiohttp").setLevel(logging.WARNING) | ||
| 19 | |||
| 20 | # ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-- | ||
| 21 | # Konfiguration - HIER ANPASSEN! | ||
| 22 | # ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-- | ||
| 23 | HOMESERVER = "https:~/~/matrix.rs-servertech.com" | ||
| 24 | USER_ID = "@terry:matrix.rs-servertech.com" | ||
| 25 | ACCESS_TOKEN = "HIER_DEIN_NEUER_TOKEN_EINTRAGEN" # ← Hole Token mit curl! | ||
| 26 | DEVICE_ID = "MATRIXBOT" | ||
| 27 | |||
| 28 | # n8n Konfiguration | ||
| 29 | N8N_WEBHOOK_URL = "https:~/~/n8n.rs-servertech.com/webhook/matrix-bot" | ||
| 30 | WEBHOOK_PORT = 8080 | ||
| 31 | WEBHOOK_SECRET = "mein-sicherer-key-2024-xyz" # ← Ändere zu eigenem Secret! | ||
| 32 | DEFAULT_ROOM_ID = "!dYNoYyZHeJfkqfsSFH:matrix.rs-servertech.com" | ||
| 33 | |||
| 34 | # KI Konfiguration - Ollama (Selfhosted) | ||
| 35 | OLLAMA_URL = "http:~/~/localhost:11434" # Deine Ollama URL | ||
| 36 | OLLAMA_MODEL = "llama3.2" # Dein Modell (llama3.2, mistral, phi3, etc.) | ||
| 37 | AI_ENABLED = True # Auf True für KI-Chat | ||
| 38 | |||
| 39 | # Bot-Verhalten | ||
| 40 | BOT_PREFIX = "!" | ||
| 41 | RESPOND_TO_ALL = False # False = nur auf Erwähnungen/Direktnachrichten | ||
| 42 | RESPOND_TO_MENTIONS = True # True = antwortet auf @terry | ||
| 43 | RESPOND_TO_DM = True # True = antwortet in Privatnachrichten | ||
| 44 | |||
| 45 | # ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-- | ||
| 46 | # Globale Variablen | ||
| 47 | # ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-- | ||
| 48 | client: AsyncClient = None | ||
| 49 | app = web.Application() | ||
| 50 | conversation_history = {} | ||
| 51 | |||
| 52 | # ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-- | ||
| 53 | # KI-Integration (Ollama) | ||
| 54 | # ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-- | ||
| 55 | async def ask_ai(message: str, user_id: str) -> str: | ||
| 56 | """Sendet Nachricht an Ollama und erhält Antwort.""" | ||
| 57 | if not AI_ENABLED: | ||
| 58 | return None | ||
| 59 | |||
| 60 | if user_id not in conversation_history: | ||
| 61 | conversation_history[user_id] = [] | ||
| 62 | |||
| 63 | conversation_history[user_id].append({"role": "user", "content": message}) | ||
| 64 | |||
| 65 | # Limitiere History | ||
| 66 | if len(conversation_history[user_id]) > 20: | ||
| 67 | conversation_history[user_id] = conversation_history[user_id][-20:] | ||
| 68 | |||
| 69 | try: | ||
| 70 | # Ollama API Format | ||
| 71 | messages = [ | ||
| 72 | {"role": "system", "content": "Du bist Terry, ein hilfreicher Matrix-Bot. Antworte kurz, freundlich und auf Deutsch. Sei persönlich und natürlich im Gespräch."}, | ||
| 73 | *conversation_history[user_id] | ||
| 74 | ] | ||
| 75 | |||
| 76 | payload = { | ||
| 77 | "model": OLLAMA_MODEL, | ||
| 78 | "messages": messages, | ||
| 79 | "stream": False, | ||
| 80 | "options": { | ||
| 81 | "temperature": 0.7, | ||
| 82 | "num_predict": 300 # Max Tokens | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | async with ClientSession() as session: | ||
| 87 | async with session.post( | ||
| 88 | f"{OLLAMA_URL}/api/chat", | ||
| 89 | json=payload, | ||
| 90 | timeout=60 # Ollama kann länger brauchen | ||
| 91 | ) as resp: | ||
| 92 | if resp.status == 200: | ||
| 93 | data = await resp.json() | ||
| 94 | answer = data["message"]["content"] | ||
| 95 | |||
| 96 | conversation_history[user_id].append({"role": "assistant", "content": answer}) | ||
| 97 | |||
| 98 | logger.info(f"🧠 Ollama Antwort: {answer[:50]}...") | ||
| 99 | return answer | ||
| 100 | else: | ||
| 101 | text = await resp.text() | ||
| 102 | logger.error(f"Ollama Fehler {resp.status}: {text}") | ||
| 103 | return None | ||
| 104 | |||
| 105 | except asyncio.TimeoutError: | ||
| 106 | logger.error("Ollama Timeout - Modell lädt oder ist zu langsam") | ||
| 107 | return "⏱️ Entschuldige, ich brauche gerade etwas länger zum Nachdenken..." | ||
| 108 | except Exception as e: | ||
| 109 | logger.error(f"KI-Fehler: {e}") | ||
| 110 | return None | ||
| 111 | |||
| 112 | # ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-- | ||
| 113 | # Webhook-Handler (n8n → Bot) | ||
| 114 | # ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-- | ||
| 115 | async def webhook_handler(request): | ||
| 116 | """Empfängt Messages von n8n.""" | ||
| 117 | try: | ||
| 118 | data = await request.json() | ||
| 119 | |||
| 120 | if data.get("secret") != WEBHOOK_SECRET: | ||
| 121 | logger.warning(f"❌ Unauthorized webhook: {request.remote}") | ||
| 122 | return web.json_response({"error": "Unauthorized"}, status=401) | ||
| 123 | |||
| 124 | message = data.get("message") | ||
| 125 | room_id = data.get("room_id", DEFAULT_ROOM_ID) | ||
| 126 | |||
| 127 | if not message: | ||
| 128 | return web.json_response({"error": "No message"}, status=400) | ||
| 129 | |||
| 130 | logger.info(f"📥 n8n → Bot: {message[:50]}...") | ||
| 131 | |||
| 132 | await client.room_send( | ||
| 133 | room_id, | ||
| 134 | "m.room.message", | ||
| 135 | {"msgtype": "m.text", "body": message} | ||
| 136 | ) | ||
| 137 | |||
| 138 | logger.info(f"✓ Message an {room_id} gesendet") | ||
| 139 | return web.json_response({"status": "success", "room": room_id}) | ||
| 140 | |||
| 141 | except json.JSONDecodeError: | ||
| 142 | return web.json_response({"error": "Invalid JSON"}, status=400) | ||
| 143 | except Exception as e: | ||
| 144 | logger.error(f"❌ Webhook Fehler: {e}") | ||
| 145 | return web.json_response({"error": str(e)}, status=500) | ||
| 146 | |||
| 147 | async def health_check(request): | ||
| 148 | """Health-Check Endpoint.""" | ||
| 149 | return web.json_response({ | ||
| 150 | "status": "online", | ||
| 151 | "bot": USER_ID, | ||
| 152 | "rooms": len(client.rooms) if client else 0, | ||
| 153 | "ai_enabled": AI_ENABLED, | ||
| 154 | "webhook_port": WEBHOOK_PORT, | ||
| 155 | "n8n_configured": N8N_WEBHOOK_URL != "https:~/~/n8n.rs-servertech.com/webhook/matrix-bot" | ||
| 156 | }) | ||
| 157 | |||
| 158 | # ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-- | ||
| 159 | # n8n Trigger (Bot → n8n) | ||
| 160 | # ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-- | ||
| 161 | async def trigger_n8n(trigger_type: str, data: dict): | ||
| 162 | """Sendet Event an n8n.""" | ||
| 163 | try: | ||
| 164 | payload = { | ||
| 165 | "trigger": trigger_type, | ||
| 166 | "data": data, | ||
| 167 | "timestamp": time.time() | ||
| 168 | } | ||
| 169 | |||
| 170 | async with ClientSession() as session: | ||
| 171 | async with session.post(N8N_WEBHOOK_URL, json=payload, timeout=10) as resp: | ||
| 172 | if resp.status == 200: | ||
| 173 | result = await resp.json() | ||
| 174 | logger.info(f"✓ n8n Trigger: {trigger_type}") | ||
| 175 | return result | ||
| 176 | else: | ||
| 177 | logger.warning(f"n8n returned {resp.status}") | ||
| 178 | return None | ||
| 179 | except Exception as e: | ||
| 180 | logger.error(f"n8n Fehler: {e}") | ||
| 181 | return None | ||
| 182 | |||
| 183 | # ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-- | ||
| 184 | # Command Handler | ||
| 185 | # ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-- | ||
| 186 | async def handle_command(room: MatrixRoom, event: RoomMessageText, message: str): | ||
| 187 | """Verarbeitet Befehle und Messages.""" | ||
| 188 | |||
| 189 | response = None | ||
| 190 | parts = message.split() | ||
| 191 | command = parts[0].lower() if parts else "" | ||
| 192 | args = parts[1:] if len(parts) > 1 else [] | ||
| 193 | |||
| 194 | # Befehle mit ! oder / | ||
| 195 | if message.startswith(BOT_PREFIX) or message.startswith("/"): | ||
| 196 | |||
| 197 | if command in ["!start", "!hilfe", "/start", "/hilfe"]: | ||
| 198 | response = f"""🤖 ~*~*Matrix Bot - Telegram Style~*~* | ||
| 199 | |||
| 200 | ~*~*Befehle:~*~* | ||
| 201 | • `/start` `/hilfe` - Diese Hilfe | ||
| 202 | • `/status` - Bot Status | ||
| 203 | • `/reset` - Chat löschen | ||
| 204 | • `/würfel` - Würfel 🎲 | ||
| 205 | • `/münze` - Münze werfen | ||
| 206 | • `/rechne [...]` - Rechner | ||
| 207 | • `/zeit` - Uhrzeit | ||
| 208 | • `/witz` - Witz | ||
| 209 | • `/ping` - Test | ||
| 210 | |||
| 211 | ~*~*n8n:~*~* | ||
| 212 | • `/webhook` - Webhook Info | ||
| 213 | • `/n8n [cmd]` - n8n Befehl | ||
| 214 | |||
| 215 | ~*~*Raum:~*~* `{room.room_id}` | ||
| 216 | |||
| 217 | KI-Chat: {'✅ Aktiv' if AI_ENABLED else '❌ Inaktiv'} | ||
| 218 | Schreib mir einfach! 💬""" | ||
| 219 | |||
| 220 | elif command in ["!status", "/status"]: | ||
| 221 | ai = "✅ Aktiv" if AI_ENABLED else "❌ Inaktiv" | ||
| 222 | response = f"""📊 ~*~*Status~*~* | ||
| 223 | |||
| 224 | ~*~*Matrix:~*~* ✅ Online | ||
| 225 | • User: {USER_ID} | ||
| 226 | • Räume: {len(client.rooms)} | ||
| 227 | |||
| 228 | ~*~*Features:~*~* | ||
| 229 | • KI: {ai} (Ollama: {OLLAMA_MODEL}) | ||
| 230 | • n8n: ✅ Konfiguriert | ||
| 231 | • Webhook: Port {WEBHOOK_PORT} | ||
| 232 | |||
| 233 | ~*~*Modus:~*~* | ||
| 234 | {'Antwortet auf alle' if RESPOND_TO_ALL else 'Nur Erwähnungen'}""" | ||
| 235 | |||
| 236 | elif command in ["!reset", "/reset"]: | ||
| 237 | if event.sender in conversation_history: | ||
| 238 | del conversation_history[event.sender] | ||
| 239 | response = "🔄 Chat-Verlauf gelöscht!" | ||
| 240 | |||
| 241 | elif command in ["!würfel", "/würfel"]: | ||
| 242 | result = random.randint(1, 6) | ||
| 243 | response = f"🎲 ~*~*{result}~*~*" | ||
| 244 | |||
| 245 | elif command in ["!münze", "/münze"]: | ||
| 246 | result = random.choice(["Kopf 🪙", "Zahl 🔢"]) | ||
| 247 | response = f"{result}" | ||
| 248 | |||
| 249 | elif command in ["!zeit", "/zeit"]: | ||
| 250 | response = f"🕐 ~*~*{datetime.now().strftime('%H:%M:%S')}~*~* Uhr" | ||
| 251 | |||
| 252 | elif command in ["!ping", "/ping"]: | ||
| 253 | response = "🏓 Pong!" | ||
| 254 | |||
| 255 | elif command in ["!witz", "/witz"]: | ||
| 256 | witze = [ | ||
| 257 | "Warum können Geister so schlecht lügen? Weil sie so leicht zu durchschauen sind! 👻", | ||
| 258 | "Was ist grün und steht vor der Tür? Ein Klopfsalat! 🥬", | ||
| 259 | "Warum summen Bienen? Weil sie den Text nicht kennen! 🐝", | ||
| 260 | "Was macht ein Clown im Büro? Faxen! 🤡", | ||
| 261 | "Welche Handwerker essen am meisten? Die Maurer! 🧱" | ||
| 262 | ] | ||
| 263 | response = random.choice(witze) | ||
| 264 | |||
| 265 | elif command in ["!rechne", "/rechne"]: | ||
| 266 | if not args: | ||
| 267 | response = "❌ Beispiel: `/rechne 5 + 3`" | ||
| 268 | else: | ||
| 269 | try: | ||
| 270 | expr = " ".join(args) | ||
| 271 | if all(c in "0123456789+-*/(). " for c in expr): | ||
| 272 | result = eval(expr) | ||
| 273 | response = f"🧮 `{expr}` = ~*~*{result}~*~*" | ||
| 274 | else: | ||
| 275 | response = "❌ Nur Zahlen und +-*/() erlaubt" | ||
| 276 | except: | ||
| 277 | response = "❌ Rechenfehler" | ||
| 278 | |||
| 279 | elif command in ["!webhook", "/webhook"]: | ||
| 280 | response = f"""📡 ~*~*Webhook Info~*~* | ||
| 281 | |||
| 282 | ~*~*n8n → Bot:~*~* | ||
| 283 | ``` | ||
| 284 | POST http:~/~/YOUR_IP:{WEBHOOK_PORT}/webhook | ||
| 285 | ~{~{ | ||
| 286 | "secret": "{WEBHOOK_SECRET}", | ||
| 287 | "message": "Text", | ||
| 288 | "room_id": "{room.room_id}" | ||
| 289 | }} | ||
| 290 | ``` | ||
| 291 | |||
| 292 | ~*~*Health:~*~* http:~/~/YOUR_IP:{WEBHOOK_PORT}/health | ||
| 293 | ~*~*Raum-ID:~*~* `{room.room_id}`""" | ||
| 294 | |||
| 295 | elif command in ["!n8n", "/n8n"]: | ||
| 296 | if not args: | ||
| 297 | response = "❌ Beispiel: `/n8n status`" | ||
| 298 | else: | ||
| 299 | cmd = " ".join(args) | ||
| 300 | result = await trigger_n8n("command", { | ||
| 301 | "command": cmd, | ||
| 302 | "room_id": room.room_id, | ||
| 303 | "sender": event.sender | ||
| 304 | }) | ||
| 305 | |||
| 306 | if result and "response" in result: | ||
| 307 | response = f"✅ n8n:\n{result['response']}" | ||
| 308 | elif result: | ||
| 309 | response = f"✅ Befehl gesendet: `{cmd}`" | ||
| 310 | else: | ||
| 311 | response = "❌ n8n nicht erreichbar" | ||
| 312 | |||
| 313 | # Normale Nachricht (kein Befehl) | ||
| 314 | else: | ||
| 315 | # Prüfe ob Bot erwähnt wurde | ||
| 316 | bot_mentioned = ( | ||
| 317 | f"@{USER_ID.split(':')[0][1:]}" in message.lower() or | ||
| 318 | USER_ID in message or | ||
| 319 | "@terry" in message.lower() | ||
| 320 | ) | ||
| 321 | |||
| 322 | # Ist es ein Direktchat (1-zu-1)? | ||
| 323 | is_direct_chat = False | ||
| 324 | if room.room_id in client.rooms: | ||
| 325 | room_obj = client.rooms[room.room_id] | ||
| 326 | is_direct_chat = len(room_obj.users) <= 2 | ||
| 327 | |||
| 328 | # Bot antwortet nur wenn: | ||
| 329 | # - Erwähnt (@terry) | ||
| 330 | # - In Direktnachrichten | ||
| 331 | # - Oder RESPOND_TO_ALL aktiv | ||
| 332 | should_respond = ( | ||
| 333 | RESPOND_TO_ALL or | ||
| 334 | (RESPOND_TO_MENTIONS and bot_mentioned) or | ||
| 335 | (RESPOND_TO_DM and is_direct_chat) | ||
| 336 | ) | ||
| 337 | |||
| 338 | if should_respond: | ||
| 339 | logger.info(f"💬 Verarbeite Nachricht (Mentioned: {bot_mentioned}, DM: {is_direct_chat})") | ||
| 340 | |||
| 341 | # Versuche KI | ||
| 342 | if AI_ENABLED: | ||
| 343 | logger.info("🧠 Sende an Ollama...") | ||
| 344 | ai_response = await ask_ai(message, event.sender) | ||
| 345 | if ai_response: | ||
| 346 | response = ai_response | ||
| 347 | else: | ||
| 348 | response = "🤖 Ollama antwortet nicht. Prüfe `/status`" | ||
| 349 | else: | ||
| 350 | # Einfache Antworten (OHNE Grüße die sich selbst triggern) | ||
| 351 | msg_lower = message.lower() | ||
| 352 | |||
| 353 | # NUR wenn Message MIT Gruß BEGINNT (nicht enthält) | ||
| 354 | if any(message.lower().startswith(w) for w in ["hallo", "hi", "hey", "moin", "guten tag", "servus"]): | ||
| 355 | username = event.sender.split(':')[0][1:] | ||
| 356 | response = f"Hey {username}! 👋 Tippe `/hilfe` für alle Befehle." | ||
| 357 | |||
| 358 | elif any(w in msg_lower for w in ["danke", "thx", "thanks"]): | ||
| 359 | response = "Gerne! 😊" | ||
| 360 | |||
| 361 | elif any(w in msg_lower for w in ["wie geht", "wie gehts"]): | ||
| 362 | response = "Mir geht's super! 🤖 Wie kann ich helfen?" | ||
| 363 | |||
| 364 | elif "?" in message: | ||
| 365 | response = "🤔 Gute Frage! Tippe `/hilfe` für verfügbare Befehle." | ||
| 366 | |||
| 367 | # Antwort senden | ||
| 368 | if response: | ||
| 369 | try: | ||
| 370 | await client.room_send( | ||
| 371 | room.room_id, | ||
| 372 | "m.room.message", | ||
| 373 | {"msgtype": "m.text", "body": response} | ||
| 374 | ) | ||
| 375 | logger.info(f"✓ Antwort → {event.sender.split(':')[0]}") | ||
| 376 | except Exception as e: | ||
| 377 | logger.error(f"❌ Senden fehlgeschlagen: {e}") | ||
| 378 | |||
| 379 | # ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-- | ||
| 380 | # Message Callback | ||
| 381 | # ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-- | ||
| 382 | async def message_callback(room: MatrixRoom, event: RoomMessageText) -> None: | ||
| 383 | """Reagiert auf Messages.""" | ||
| 384 | |||
| 385 | # WICHTIG: Ignoriere eigene Messages | ||
| 386 | if event.sender == client.user_id: | ||
| 387 | logger.debug(f"Ignoriere eigene Nachricht von {event.sender}") | ||
| 388 | return | ||
| 389 | |||
| 390 | # Ignoriere alte Messages beim Start | ||
| 391 | if hasattr(event, 'server_timestamp'): | ||
| 392 | current_time = time.time() * 1000 | ||
| 393 | age = current_time - event.server_timestamp | ||
| 394 | if age > 5000: # Älter als 5 Sekunden | ||
| 395 | return | ||
| 396 | |||
| 397 | message = event.body.strip() | ||
| 398 | logger.info(f"📨 {event.sender.split(':')[0]}: {message[:50]}...") | ||
| 399 | |||
| 400 | await handle_command(room, event, message) | ||
| 401 | |||
| 402 | # ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-- | ||
| 403 | # Webserver | ||
| 404 | # ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-- | ||
| 405 | async def start_webserver(): | ||
| 406 | """Startet Webhook-Server.""" | ||
| 407 | app.router.add_post('/webhook', webhook_handler) | ||
| 408 | app.router.add_get('/health', health_check) | ||
| 409 | |||
| 410 | runner = web.AppRunner(app) | ||
| 411 | await runner.setup() | ||
| 412 | site = web.TCPSite(runner, '0.0.0.0', WEBHOOK_PORT) | ||
| 413 | await site.start() | ||
| 414 | |||
| 415 | logger.info(f"🌐 Webhook: Port {WEBHOOK_PORT}") | ||
| 416 | |||
| 417 | # ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-- | ||
| 418 | # Matrix Client | ||
| 419 | # ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-- | ||
| 420 | async def start_matrix_client(): | ||
| 421 | """Startet Matrix Client.""" | ||
| 422 | global client | ||
| 423 | |||
| 424 | client = AsyncClient(HOMESERVER, USER_ID, device_id=DEVICE_ID) | ||
| 425 | client.access_token = ACCESS_TOKEN | ||
| 426 | client.add_event_callback(message_callback, RoomMessageText) | ||
| 427 | |||
| 428 | await client.sync(timeout=30000, full_state=True) | ||
| 429 | |||
| 430 | logger.info(f"✓ In {len(client.rooms)} Räumen:") | ||
| 431 | for room_id, room in client.rooms.items(): | ||
| 432 | logger.info(f" • {room.display_name or room.room_id}") | ||
| 433 | |||
| 434 | await client.sync_forever(timeout=30000) | ||
| 435 | |||
| 436 | # ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-- | ||
| 437 | # Main | ||
| 438 | # ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-- | ||
| 439 | async def main(): | ||
| 440 | """Hauptfunktion.""" | ||
| 441 | logger.info("=" * 70) | ||
| 442 | logger.info("🤖 Matrix Bot - Telegram Style v3.0") | ||
| 443 | logger.info("=" * 70) | ||
| 444 | logger.info(f"🔗 {HOMESERVER}") | ||
| 445 | logger.info(f"👤 {USER_ID}") | ||
| 446 | logger.info(f"🧠 KI: {'✅' if AI_ENABLED else '❌'}") | ||
| 447 | logger.info(f"🌐 Webhook: Port {WEBHOOK_PORT}") | ||
| 448 | logger.info(f"🎯 n8n: https:~/~/n8n.rs-servertech.com") | ||
| 449 | logger.info(f"📍 Raum: {DEFAULT_ROOM_ID}") | ||
| 450 | logger.info("=" * 70) | ||
| 451 | |||
| 452 | try: | ||
| 453 | await asyncio.gather( | ||
| 454 | start_webserver(), | ||
| 455 | start_matrix_client() | ||
| 456 | ) | ||
| 457 | except KeyboardInterrupt: | ||
| 458 | logger.info("\n⏹️ Bot beendet") | ||
| 459 | finally: | ||
| 460 | if client: | ||
| 461 | await client.close() | ||
| 462 | |||
| 463 | if ~_~_name~_~_ == "~_~_main~_~_": | ||
| 464 | try: | ||
| 465 | asyncio.run(main()) | ||
| 466 | except KeyboardInterrupt: | ||
| 467 | pass |