# -*- coding: utf-8 -*- """Telegram Bot webhook handler — integrated into app.py""" def _telegram_download_file(file_id): """Download a file from Telegram servers. Returns local path or None.""" import requests as _r if not TELEGRAM_TOKEN: return None try: gr = _r.get("{}/getFile?file_id={}".format(TELEGRAM_API, file_id), timeout=10) gr.raise_for_status() gdata = gr.json() if not gdata.get("ok"): print("[telegram] getFile failed: {}".format(gdata), file=sys.stderr) return None file_path = gdata["result"]["file_path"] ext = os.path.splitext(file_path)[1].lower() or ".bin" safe_name = "tg_{}{}".format(file_id, ext) dest = str(MEDIA_DIR / safe_name) dl_url = "https://api.telegram.org/file/bot{}/{}".format(TELEGRAM_TOKEN, file_path) fr = _r.get(dl_url, timeout=60) fr.raise_for_status() with open(dest, "wb") as f: f.write(fr.content) if ext == ".mov": mp4_path = dest + ".mp4" import subprocess result = subprocess.run( ["/home/hermes/.local/bin/ffmpeg", "-y", "-i", dest, "-c:v", "libx264", "-preset", "fast", "-crf", "23", "-c:a", "aac", "-b:a", "128k", "-movflags", "+faststart", mp4_path], capture_output=True, text=True, timeout=120 ) if result.returncode == 0 and os.path.exists(mp4_path): os.remove(dest) return mp4_path return dest except Exception as e: print("[telegram download error] {}".format(e), file=sys.stderr) return None def _telegram_autoreply(chat_id, sender_name, is_new_user): """Send welcome or thank-you message via Telegram Send API.""" if not TELEGRAM_TOKEN: return import requests as _r try: if is_new_user: text = ( "🎲 Hey {}! Cool, dass du da bist — willkommen in der ".format(sender_name) + "brettspiel-news.de Community!\n\n" "Schick uns einfach, was dich bewegt: 💬 Text, 🎤 Sprachnachrichten, " "🖼️ Fotos oder 🎬 Videos — alles rund um Brettspiele. Wir schauen uns alles an!\n\n" "✍️ Nenn uns deinen Vornamen, wenn du magst — das ist deine Einwilligung, " "dass wir dich als Quelle nennen, falls wir deinen Beitrag verwenden. " "Ohne Namen schreiben wir „anonyme Quelle\".\n\n" "🔒 „Meine Daten löschen\" genügt — und alles ist sofort und vollständig weg." ) else: text = "📨 Danke {}! Ist angekommen — wir kümmern uns drum.".format(sender_name) _r.post( "{}/sendMessage".format(TELEGRAM_API), json={"chat_id": chat_id, "text": text}, timeout=10, ) except Exception as e: print("[telegram reply error] {}".format(e), file=sys.stderr) @app.route("/telegram/webhook", methods=["GET", "POST"]) def telegram_webhook(): """Telegram Bot Webhook. GET=health check, POST=incoming messages.""" if request.method == "GET": if TELEGRAM_TOKEN: import requests as _r try: r = _r.get("{}/getMe".format(TELEGRAM_API), timeout=5) data = r.json() if data.get("ok"): return jsonify({"status": "ok", "bot": data["result"]["username"]}) except Exception: pass return jsonify({"status": "ok", "token_configured": True}) return jsonify({"status": "off", "reason": "TELEGRAM_TOKEN not set"}), 500 # POST: Incoming message body = request.get_json(force=True, silent=True) if not body or not TELEGRAM_TOKEN: return "OK", 200 try: msg = body.get("message", {}) if not msg: return "OK", 200 chat = msg.get("chat", {}) sender_id = str(chat.get("id", "unknown")) sender_name = msg.get("from", {}).get("first_name", "") or "" last_name = msg.get("from", {}).get("last_name", "") if last_name: sender_name = "{} {}".format(sender_name, last_name).strip() if not sender_name: sender_name = sender_id msg_type = "text" content = "" media_path = None file_id = None if "text" in msg: msg_type = "text" content = msg["text"] elif "photo" in msg: msg_type = "image" content = msg.get("caption", "[Bild ohne Text]") file_id = msg["photo"][-1]["file_id"] elif "video" in msg: msg_type = "video" content = msg.get("caption", "[🎬 Video — Transkription läuft...]") file_id = msg["video"]["file_id"] elif "voice" in msg: msg_type = "audio" content = "[🎤 Sprachnachricht — Transkription läuft...]" file_id = msg["voice"]["file_id"] elif "audio" in msg: msg_type = "audio" content = msg.get("caption", "[🎤 Audio]") file_id = msg["audio"]["file_id"] elif "document" in msg: msg_type = "document" content = msg.get("caption", "[📎 Dokument]") file_id = msg["document"]["file_id"] elif "sticker" in msg: content = "[Sticker: {}]".format(msg['sticker'].get('emoji', '')) msg_type = "document" elif "location" in msg: lat = msg["location"]["latitude"] lon = msg["location"]["longitude"] content = "[📍 Standort: {}, {}]".format(lat, lon) msg_type = "text" else: content = "[Unbekannter Nachrichtentyp]" msg_type = "text" if file_id: media_path = _telegram_download_file(file_id) # GDPR delete trigger if msg_type == "text" and content.strip().lower() == "meine daten löschen": _gdpr_delete_sender(get_db(), sender_id) if TELEGRAM_TOKEN: import requests as _r try: _r.post( "{}/sendMessage".format(TELEGRAM_API), json={"chat_id": int(sender_id), "text": "✅ Alle deine Daten wurden vollständig und endgültig vom Server gelöscht. " "Es befinden sich keinerlei personenbezogene Daten mehr von dir bei uns."}, timeout=10, ) except Exception: pass return "OK", 200 db = get_db() spiel_titel = None category_override = None if "#out" in content.lower(): category_override = "ausverkauft" idx = content.lower().find("#out") after = content[idx + 4:].strip() if after: spiel_titel = re.split(r'[.!?\n]| - | – | — | ist | war | in ', after, maxsplit=1)[0].strip()[:200] halle = extract_halle(content) THREAD_WINDOW_MINUTES = 5 recent = db.execute( """SELECT id, content, media_path, halle FROM submissions WHERE channel='telegram' AND sender_id=? AND status != 'gdpr_deleted' ORDER BY created_at DESC LIMIT 1""", (sender_id,), ).fetchone() is_new_user = db.execute( "SELECT COUNT(*) FROM submissions WHERE channel='telegram' AND sender_id=? AND status != 'gdpr_deleted'", (sender_id,), ).fetchone()[0] == 0 thread_found = False if recent: age_check = db.execute( "SELECT (strftime('%s','now') - strftime('%s',created_at)) < ? FROM submissions WHERE id=?", (THREAD_WINDOW_MINUTES * 60, recent["id"]), ).fetchone() if age_check and age_check[0]: thread_found = True if thread_found: prefix = { "text": "", "image": "\n[🖼️ Bild] ", "audio": "\n[🎤 Sprachnachricht]", "video": "\n[🎬 Video] ", "document": "\n[📎 Dokument] ", }.get(msg_type, "\n[{}] ".format(msg_type)) new_content = (recent["content"] or "") + prefix + content new_halle = extract_halle(new_content) or recent["halle"] db.execute( "UPDATE submissions SET content=?, halle=?, created_at=CURRENT_TIMESTAMP WHERE id=?", (new_content, new_halle, recent["id"]), ) if "#out" in content.lower(): out_title = None idx2 = content.lower().find("#out") after2 = content[idx2 + 4:].strip() if after2: out_title = re.split(r'[.!?\n]| - | – | — | ist | war | in ', after2, maxsplit=1)[0].strip()[:200] db.execute("UPDATE submissions SET category=?, spiel_titel=? WHERE id=?", ("ausverkauft", out_title, recent["id"])) if out_title: mark_ausverkauft_in_neuheiten(db, out_title) if media_path: merged = media_path if recent["media_path"]: merged = recent["media_path"] + "||" + media_path db.execute("UPDATE submissions SET media_path=? WHERE id=?", (merged, recent["id"])) else: cat = category_override or None db.execute( """INSERT INTO submissions (channel, sender_id, sender_name, type, content, media_path, status, halle, category, spiel_titel) VALUES (?, ?, ?, ?, ?, ?, 'new', ?, ?, ?)""", ("telegram", sender_id, sender_name, msg_type, content, media_path, halle, cat, spiel_titel), ) if cat == "ausverkauft" and spiel_titel: mark_ausverkauft_in_neuheiten(db, spiel_titel) if not thread_found: _telegram_autoreply(int(sender_id), sender_name, is_new_user) if msg_type == "audio" and media_path: import threading sub_id = recent["id"] if thread_found else db.execute("SELECT last_insert_rowid()").fetchone()[0] threading.Thread(target=_transcribe_bg, args=(DB_PATH, sub_id, media_path), daemon=True).start() if msg_type == "video" and media_path: import threading sub_id = recent["id"] if thread_found else db.execute("SELECT last_insert_rowid()").fetchone()[0] threading.Thread(target=_transcribe_video_bg, args=(DB_PATH, sub_id, media_path), daemon=True).start() if msg_type in ("text", "image") and not thread_found and content and len(content.strip()) > 20: import threading sub_id = db.execute("SELECT last_insert_rowid()").fetchone()[0] if not halle: threading.Thread(target=auto_process_bg, args=(DB_PATH, sub_id), daemon=True).start() db.commit() except Exception as e: print("[telegram webhook error] {}".format(e), file=sys.stderr) return "OK", 200 return "OK", 200