fix: Telegram Bot + Morgenbericht zeigen alle aktiven Scanner
- Queries nutzen jetzt alle Scanner statt nur deaktivierte - Morgenbericht auf 07:30 verschoben (nach 06:30-Scan) - /preis zeigt Preise pro Quelle (Kayak, Momondo, Trip.com) - Preis-Alerts fuer alle Scanner aktiv - preis_korrigiert in Abfragen beruecksichtigt
This commit is contained in:
parent
a90a73b7cb
commit
35252246dc
1 changed files with 79 additions and 66 deletions
|
|
@ -405,25 +405,23 @@ def speichere_screenshot(screenshot_b64, node_name, job):
|
|||
ALERT_SCHWELLE_EUR = 900 # Telegram-Alert wenn CX unter diesen Preis fällt
|
||||
|
||||
def pruefe_preis_alert(results, job):
|
||||
"""Sendet Telegram-Alert wenn CX (via HKG oder direkt) unter Schwelle fällt."""
|
||||
if job.get("scanner") not in ("kayak_multicity", "cathay_pacific"):
|
||||
return
|
||||
label = "CX direkt" if job.get("scanner") == "cathay_pacific" else "CX via HKG"
|
||||
"""Sendet Telegram-Alert wenn CX Economy unter Schwelle fällt."""
|
||||
scanner = job.get("scanner", "")
|
||||
for r in results:
|
||||
if r.get("preis", 9999) < ALERT_SCHWELLE_EUR:
|
||||
preis = r["preis"]
|
||||
abflug = r.get("abflug", "?")
|
||||
url = r.get("booking_url", "")
|
||||
telegram_send(
|
||||
f"✈️ <b>{label} unter {ALERT_SCHWELLE_EUR}€!</b>\n\n"
|
||||
f"✈️ <b>CX unter {ALERT_SCHWELLE_EUR}€!</b>\n\n"
|
||||
f"💰 Preis: <b>{preis:.0f} EUR</b> Roundtrip\n"
|
||||
f"📊 Quelle: {scanner}\n"
|
||||
f"📅 Abflug: {abflug}\n"
|
||||
f"🔗 <a href='{url}'>Preis prüfen</a>\n\n"
|
||||
f"⚠️ Sofort auf Buchungsseite prüfen — Preise ändern sich schnell, "
|
||||
f"Aggregatoren zeigen teils Economy Light zuerst."
|
||||
f"⚠️ Sofort auf Buchungsseite prüfen — Preise ändern sich schnell."
|
||||
)
|
||||
log(f"💰 PREIS-ALERT: {preis:.0f}EUR {label} — Telegram gesendet")
|
||||
break # Nur einmal pro Job-Lauf
|
||||
log(f"💰 PREIS-ALERT: {preis:.0f}EUR {scanner} — Telegram gesendet")
|
||||
break
|
||||
|
||||
|
||||
def speichere_preise(results, node_name, job, screenshot_id=None, kabine_erkannt=None):
|
||||
|
|
@ -647,34 +645,36 @@ def cleanup_lauf():
|
|||
# ── Telegram Bot Befehle ──────────────────────────────────────────────────────
|
||||
|
||||
def _cx_preise_jetzt() -> dict:
|
||||
"""Holt aktuellen CX-Preis (via HKG + direkt) und Vergleichswerte aus DB."""
|
||||
"""Holt aktuellen CX-Preis aus allen aktiven Scannern."""
|
||||
conn = get_conn()
|
||||
cx = conn.execute("""
|
||||
SELECT MIN(preis) as min_p, MAX(scraped_at) as zuletzt
|
||||
FROM prices WHERE scanner IN ('kayak_multicity', 'cathay_pacific')
|
||||
AND kabine_erkannt = 'Economy'
|
||||
AND (plausibel = 1 OR plausibel IS NULL)
|
||||
AND scraped_at >= datetime('now','-3 hours')
|
||||
best = conn.execute("""
|
||||
SELECT MIN(COALESCE(preis_korrigiert, preis)) as min_p,
|
||||
MAX(scraped_at) as zuletzt
|
||||
FROM prices WHERE (plausibel = 1 OR plausibel IS NULL)
|
||||
AND date(scraped_at) = date('now')
|
||||
""").fetchone()
|
||||
cx_direkt = conn.execute("""
|
||||
SELECT MIN(preis) as min_p
|
||||
FROM prices WHERE scanner='cathay_pacific'
|
||||
AND kabine_erkannt = 'Economy'
|
||||
AND (plausibel = 1 OR plausibel IS NULL)
|
||||
AND scraped_at >= datetime('now','-3 hours')
|
||||
""").fetchone()
|
||||
direkt = conn.execute("""
|
||||
SELECT MIN(preis) as min_p
|
||||
best_kayak = conn.execute("""
|
||||
SELECT MIN(COALESCE(preis_korrigiert, preis)) as min_p
|
||||
FROM prices WHERE scanner='kayak'
|
||||
AND kabine_erkannt = 'Economy'
|
||||
AND (plausibel = 1 OR plausibel IS NULL)
|
||||
AND scraped_at >= datetime('now','-3 hours')
|
||||
AND date(scraped_at) = date('now')
|
||||
""").fetchone()
|
||||
gestern_cx = conn.execute("""
|
||||
SELECT MIN(preis) as min_p
|
||||
FROM prices WHERE scanner IN ('kayak_multicity', 'cathay_pacific')
|
||||
AND date(scraped_at) = date('now','-1 day')
|
||||
AND kabine_erkannt = 'Economy'
|
||||
best_momondo = conn.execute("""
|
||||
SELECT MIN(COALESCE(preis_korrigiert, preis)) as min_p
|
||||
FROM prices WHERE scanner='momondo'
|
||||
AND (plausibel = 1 OR plausibel IS NULL)
|
||||
AND date(scraped_at) = date('now')
|
||||
""").fetchone()
|
||||
best_trip = conn.execute("""
|
||||
SELECT MIN(COALESCE(preis_korrigiert, preis)) as min_p
|
||||
FROM prices WHERE scanner='trip'
|
||||
AND (plausibel = 1 OR plausibel IS NULL)
|
||||
AND date(scraped_at) = date('now')
|
||||
""").fetchone()
|
||||
gestern = conn.execute("""
|
||||
SELECT MIN(COALESCE(preis_korrigiert, preis)) as min_p
|
||||
FROM prices WHERE date(scraped_at) = date('now','-1 day')
|
||||
AND (plausibel = 1 OR plausibel IS NULL)
|
||||
""").fetchone()
|
||||
ki = conn.execute("""
|
||||
SELECT ki_empfehlung, ki_analyse FROM analyses
|
||||
|
|
@ -682,26 +682,28 @@ def _cx_preise_jetzt() -> dict:
|
|||
""").fetchone()
|
||||
conn.close()
|
||||
return {
|
||||
"cx_min": cx["min_p"] if cx else None,
|
||||
"cx_direkt": cx_direkt["min_p"] if cx_direkt else None,
|
||||
"cx_zuletzt": cx["zuletzt"][:16] if cx and cx["zuletzt"] else "?",
|
||||
"direkt_min": direkt["min_p"] if direkt else None,
|
||||
"gestern_cx": gestern_cx["min_p"] if gestern_cx else None,
|
||||
"cx_min": best["min_p"] if best else None,
|
||||
"cx_zuletzt": best["zuletzt"][:16] if best and best["zuletzt"] else "?",
|
||||
"kayak_min": best_kayak["min_p"] if best_kayak else None,
|
||||
"momondo_min": best_momondo["min_p"] if best_momondo else None,
|
||||
"trip_min": best_trip["min_p"] if best_trip else None,
|
||||
"gestern_cx": gestern["min_p"] if gestern else None,
|
||||
"ki_empf": ki["ki_empfehlung"] if ki else "—",
|
||||
"ki_text": ki["ki_analyse"][:200] if ki else "—",
|
||||
}
|
||||
|
||||
|
||||
def _top5_heute() -> list:
|
||||
"""Top 5 günstigste Economy-Treffer (kayak_multicity + cathay_pacific)."""
|
||||
"""Top 5 günstigste Economy-Treffer heute (alle aktiven Scanner)."""
|
||||
conn = get_conn()
|
||||
rows = conn.execute("""
|
||||
SELECT preis, abflug, ankunft, booking_url, scanner, node, scraped_at
|
||||
FROM prices WHERE scanner IN ('kayak_multicity', 'cathay_pacific')
|
||||
AND kabine_erkannt = 'Economy'
|
||||
AND (plausibel = 1 OR plausibel IS NULL)
|
||||
AND scraped_at >= datetime('now','-3 hours')
|
||||
ORDER BY preis ASC LIMIT 5
|
||||
SELECT COALESCE(preis_korrigiert, preis) as preis,
|
||||
abflug, ankunft, booking_url, scanner, node, scraped_at,
|
||||
kabine_erkannt
|
||||
FROM prices
|
||||
WHERE (plausibel = 1 OR plausibel IS NULL)
|
||||
AND date(scraped_at) = date('now')
|
||||
ORDER BY COALESCE(preis_korrigiert, preis) ASC LIMIT 5
|
||||
""").fetchall()
|
||||
conn.close()
|
||||
return [dict(r) for r in rows]
|
||||
|
|
@ -714,25 +716,26 @@ def handle_bot_command(text: str, chat_id: str):
|
|||
|
||||
if cmd == "/preis":
|
||||
cx = d["cx_min"]
|
||||
cx_direkt = d["cx_direkt"]
|
||||
direkt = d["direkt_min"]
|
||||
gestern = d["gestern_cx"]
|
||||
trend = ""
|
||||
if cx and gestern:
|
||||
diff = cx - gestern
|
||||
trend = f"↗️ +{diff:.0f}€ vs. gestern" if diff > 0 else f"↘️ {diff:.0f}€ vs. gestern"
|
||||
cx_zeile = f"💰 <b>{cx:.0f} EUR</b> Roundtrip {trend}\n"
|
||||
if cx_direkt is not None:
|
||||
cx_zeile += f"🔵 CX direkt: {cx_direkt:.0f} EUR\n"
|
||||
if direkt is not None:
|
||||
cx_zeile += f"📊 Kayak (Aggregator): {direkt:.0f} EUR\n"
|
||||
quellen = []
|
||||
if d.get("kayak_min"):
|
||||
quellen.append(f" Kayak: {d['kayak_min']:.0f}€")
|
||||
if d.get("momondo_min"):
|
||||
quellen.append(f" Momondo: {d['momondo_min']:.0f}€")
|
||||
if d.get("trip_min"):
|
||||
quellen.append(f" Trip.com: {d['trip_min']:.0f}€")
|
||||
quellen_str = "\n".join(quellen) if quellen else " Keine Daten"
|
||||
msg = (
|
||||
f"✈️ <b>CX Economy (via HKG + direkt)</b>\n\n"
|
||||
f"{cx_zeile}"
|
||||
f"🕐 Gültig bei Scan: {d['cx_zuletzt']}\n\n"
|
||||
f"⚠️ Preise auf Buchungsseiten können abweichen.\n"
|
||||
f"✈️ <b>CX Economy FRA→KTI</b>\n\n"
|
||||
f"💰 Günstigster: <b>{cx:.0f} EUR</b> Roundtrip {trend}\n\n"
|
||||
f"📊 <b>Pro Quelle:</b>\n{quellen_str}\n\n"
|
||||
f"🕐 Scan: {d['cx_zuletzt']}\n"
|
||||
f"KI: <b>{d['ki_empf']}</b>"
|
||||
) if cx else "⏳ Keine Economy-Daten (nur Economy, kein Light) im Fenster."
|
||||
) if cx else "⏳ Heute noch keine Scan-Daten."
|
||||
|
||||
elif cmd == "/best":
|
||||
top5 = _top5_heute()
|
||||
|
|
@ -830,7 +833,7 @@ def telegram_polling():
|
|||
# ── Morgenbericht ─────────────────────────────────────────────────────────────
|
||||
|
||||
def morgenbericht():
|
||||
"""Täglich 07:00: Tagesüberblick per Telegram."""
|
||||
"""Täglich 07:30: Tagesüberblick per Telegram (nach 06:30-Scan)."""
|
||||
d = _cx_preise_jetzt()
|
||||
top5 = _top5_heute()
|
||||
|
||||
|
|
@ -838,7 +841,7 @@ def morgenbericht():
|
|||
gestern = d["gestern_cx"]
|
||||
|
||||
if not cx:
|
||||
telegram_send("☀️ Guten Morgen — noch keine Scan-Daten für heute.")
|
||||
telegram_send("☀️ Guten Morgen — noch keine Scan-Daten für heute. Nächster Scan läuft bald.")
|
||||
return
|
||||
|
||||
trend_emoji = "📈" if (cx and gestern and cx > gestern) else "📉" if (cx and gestern and cx < gestern) else "➡️"
|
||||
|
|
@ -846,17 +849,27 @@ def morgenbericht():
|
|||
|
||||
empf_farbe = {"JETZT BUCHEN": "🟢", "WARTEN": "🔴", "NEUTRAL": "🟡"}.get(d["ki_empf"], "⚪")
|
||||
|
||||
quellen = []
|
||||
if d.get("kayak_min"):
|
||||
quellen.append(f" Kayak: {d['kayak_min']:.0f}€")
|
||||
if d.get("momondo_min"):
|
||||
quellen.append(f" Momondo: {d['momondo_min']:.0f}€")
|
||||
if d.get("trip_min"):
|
||||
quellen.append(f" Trip.com: {d['trip_min']:.0f}€")
|
||||
quellen_str = "\n".join(quellen) if quellen else ""
|
||||
|
||||
top_str = ""
|
||||
if top5:
|
||||
top_str = "\n🏆 <b>Beste Angebote:</b>\n" + "\n".join([
|
||||
f" {i+1}. {r['preis']:.0f}€ — {r['abflug']} <a href='{r['booking_url']}'>buchen</a>"
|
||||
f" {i+1}. {r['preis']:.0f}€ ({r.get('scanner','?')}) — {r['abflug']} <a href='{r['booking_url']}'>buchen</a>"
|
||||
for i, r in enumerate(top5)
|
||||
])
|
||||
|
||||
msg = (
|
||||
f"☀️ <b>Guten Morgen — CX Economy</b>\n\n"
|
||||
f"☀️ <b>Guten Morgen — CX Economy FRA→KTI</b>\n\n"
|
||||
f"💰 Heute ab <b>{cx:.0f} EUR</b> {trend_str}\n"
|
||||
f"{empf_farbe} KI-Empfehlung: <b>{d['ki_empf']}</b>\n"
|
||||
f"{empf_farbe} KI: <b>{d['ki_empf']}</b>\n\n"
|
||||
f"📊 <b>Pro Quelle:</b>\n{quellen_str}"
|
||||
f"{top_str}"
|
||||
)
|
||||
telegram_send(msg)
|
||||
|
|
@ -868,9 +881,9 @@ def morgenbericht():
|
|||
_letzter_cx_preis: float = 0.0
|
||||
|
||||
def pruefe_preisanstieg(results, job):
|
||||
"""Alert wenn CX (via HKG oder direkt) um mehr als 50€ gestiegen ist."""
|
||||
"""Alert wenn CX Economy um mehr als 50€ gestiegen ist."""
|
||||
global _letzter_cx_preis
|
||||
if job.get("scanner") not in ("kayak_multicity", "cathay_pacific") or not results:
|
||||
if not results:
|
||||
return
|
||||
aktuell = min(r["preis"] for r in results)
|
||||
if _letzter_cx_preis > 0 and aktuell > _letzter_cx_preis + 50:
|
||||
|
|
@ -982,9 +995,9 @@ def run():
|
|||
schedule.every().day.at("08:00").do(vorlauf_lauf)
|
||||
log("Vorlauf-Scan: täglich 08:00 für 45/60/84 Tage vorab")
|
||||
|
||||
# Täglich 07:00: Morgenbericht
|
||||
schedule.every().day.at("07:00").do(morgenbericht)
|
||||
log("Morgenbericht: täglich 07:00 Uhr")
|
||||
# Täglich 07:30: Morgenbericht (nach 06:30-Scan)
|
||||
schedule.every().day.at("07:30").do(morgenbericht)
|
||||
log("Morgenbericht: täglich 07:30 Uhr")
|
||||
|
||||
# Täglich 20:00: Tagesbilanz
|
||||
schedule.every().day.at("20:00").do(tagesbilanz)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue