- Enhanced Telegram message format to show TOP 3 by Confidence AND Quality - Added average statistics (Confidence & Quality) to notifications - Fixed hardcoded iCloud path in generator (now uses --data-dir parameter) - Updated AlleEurojackpotzahlen.csv with latest draw (Jan 9) - Added run_update_and_learn.sh wrapper script - Created com.eurojackpot.update.plist for automated updates Benefits: - Better tip selection with dual metrics visibility - Generator now works with correct project paths - More robust automation setup Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
344 lines
11 KiB
Python
344 lines
11 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Eurojackpot Notification System
|
|
|
|
Sendet Benachrichtigungen via:
|
|
- Telegram Bot
|
|
- E-Mail (SMTP)
|
|
|
|
Konfiguration via config/notifications.json
|
|
"""
|
|
|
|
import os
|
|
import json
|
|
import requests
|
|
from typing import Optional, Dict, List
|
|
from email.mime.text import MIMEText
|
|
from email.mime.multipart import MIMEMultipart
|
|
import smtplib
|
|
|
|
|
|
class EurojackpotNotifier:
|
|
"""Benachrichtigungssystem für Eurojackpot-Events."""
|
|
|
|
def __init__(self, config_file: Optional[str] = None):
|
|
if config_file is None:
|
|
# Default config path
|
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
project_dir = os.path.dirname(os.path.dirname(script_dir))
|
|
config_file = os.path.join(project_dir, "config", "notifications.json")
|
|
|
|
self.config_file = config_file
|
|
self.config = self._load_config()
|
|
|
|
def _load_config(self) -> dict:
|
|
"""Lädt Konfiguration."""
|
|
if not os.path.exists(self.config_file):
|
|
return {
|
|
"telegram": {"enabled": False},
|
|
"email": {"enabled": False}
|
|
}
|
|
|
|
try:
|
|
with open(self.config_file, 'r') as f:
|
|
return json.load(f)
|
|
except Exception as e:
|
|
print(f"⚠️ Fehler beim Laden der Notification-Config: {e}")
|
|
return {
|
|
"telegram": {"enabled": False},
|
|
"email": {"enabled": False}
|
|
}
|
|
|
|
def send_tips_generated(self, tips: List[Dict], timestamp: str, best_tip: Dict):
|
|
"""
|
|
Benachrichtigung: Neue Tipps generiert.
|
|
|
|
Args:
|
|
tips: Liste der generierten Tipps
|
|
timestamp: Zeitstempel der Generierung
|
|
best_tip: Bester Tipp mit höchster Confidence
|
|
"""
|
|
subject = f"🎲 {len(tips)} neue Eurojackpot-Tipps generiert!"
|
|
|
|
# Statistiken
|
|
avg_conf = sum(t.get('confidence', 0) for t in tips) / len(tips) if tips else 0
|
|
avg_qual = sum(t.get('quality', 0) for t in tips) / len(tips) if tips else 0
|
|
|
|
# Top 3 nach Confidence
|
|
sorted_by_conf = sorted(tips, key=lambda x: x.get('confidence', 0), reverse=True)[:3]
|
|
top3_conf_text = ""
|
|
for i, tip in enumerate(sorted_by_conf, 1):
|
|
emoji = "🥇" if i == 1 else "🥈" if i == 2 else "🥉"
|
|
main = tip.get('main_numbers', '?')
|
|
euro = tip.get('euro_numbers', '?')
|
|
conf = tip.get('confidence', 0)
|
|
qual = tip.get('quality', 0)
|
|
strat = tip.get('strategy', 'UNKNOWN')
|
|
|
|
top3_conf_text += f"""{emoji} #{i} - Conf: {conf:.2%} | Q: {qual:.3f}
|
|
🔢 {main} + ⭐ {euro}
|
|
📈 {strat}
|
|
|
|
"""
|
|
|
|
# Top 3 nach Quality
|
|
sorted_by_qual = sorted(tips, key=lambda x: x.get('quality', 0), reverse=True)[:3]
|
|
top3_qual_text = ""
|
|
for i, tip in enumerate(sorted_by_qual, 1):
|
|
emoji = "🥇" if i == 1 else "🥈" if i == 2 else "🥉"
|
|
main = tip.get('main_numbers', '?')
|
|
euro = tip.get('euro_numbers', '?')
|
|
conf = tip.get('confidence', 0)
|
|
qual = tip.get('quality', 0)
|
|
strat = tip.get('strategy', 'UNKNOWN')
|
|
|
|
top3_qual_text += f"""{emoji} #{i} - Q: {qual:.3f} | Conf: {conf:.2%}
|
|
🔢 {main} + ⭐ {euro}
|
|
📈 {strat}
|
|
|
|
"""
|
|
|
|
message = f"""🎲 NEUE EUROJACKPOT-TIPPS GENERIERT
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
|
|
📊 Anzahl Tipps: {len(tips)}
|
|
⏰ Zeitpunkt: {timestamp}
|
|
|
|
📊 STATISTIKEN:
|
|
🎯 Ø Confidence: {avg_conf:.4f}
|
|
💎 Ø Quality: {avg_qual:.4f}
|
|
|
|
🎯 TOP 3 NACH CONFIDENCE:
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
{top3_conf_text}
|
|
💎 TOP 3 NACH QUALITY:
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
{top3_qual_text}
|
|
💡 Alle 10 Tipps findest du in der CSV-Datei!
|
|
|
|
Viel Glück! 🍀
|
|
"""
|
|
|
|
# Sende via Telegram
|
|
if self.config.get("telegram", {}).get("enabled"):
|
|
self._send_telegram(message)
|
|
|
|
# Sende via Email
|
|
if self.config.get("email", {}).get("enabled"):
|
|
self._send_email(subject, message)
|
|
|
|
def send_draw_results(self, draw: Dict, evaluation: Dict, best_match: Dict):
|
|
"""
|
|
Benachrichtigung: Ziehung evaluiert.
|
|
|
|
Args:
|
|
draw: Gezogene Zahlen
|
|
evaluation: Evaluierungsergebnisse
|
|
best_match: Bester Tipp
|
|
"""
|
|
# Formatiere gezogene Zahlen
|
|
main = [draw[f'Z{i}'] for i in range(1, 6)]
|
|
euro = [draw['SZ1'], draw['SZ2']]
|
|
date = draw.get('date', 'unknown')
|
|
|
|
# Formatiere besten Tipp
|
|
best_main_matches = best_match.get('main_matches', 0)
|
|
best_euro_matches = best_match.get('euro_matches', 0)
|
|
total_matches = best_main_matches + best_euro_matches
|
|
|
|
# Bewertungs-Emoji
|
|
if total_matches >= 5:
|
|
emoji = "🎉🎉🎉"
|
|
rating = "FANTASTISCH!"
|
|
elif total_matches >= 4:
|
|
emoji = "🎊"
|
|
rating = "Sehr gut!"
|
|
elif total_matches >= 3:
|
|
emoji = "✅"
|
|
rating = "Gut!"
|
|
elif total_matches >= 2:
|
|
emoji = "👍"
|
|
rating = "OK"
|
|
else:
|
|
emoji = "⚪"
|
|
rating = "Nächstes Mal besser"
|
|
|
|
subject = f"🎰 Ziehung vom {date}: {total_matches} Treffer!"
|
|
|
|
message = f"""🎰 EUROJACKPOT-ZIEHUNG AUSGEWERTET
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
|
|
📅 Datum: {date}
|
|
|
|
🎲 GEZOGENE ZAHLEN:
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
🔢 Hauptzahlen: {'-'.join(map(str, main))}
|
|
⭐ Eurozahlen: {'-'.join(map(str, euro))}
|
|
|
|
{emoji} DEINE BESTEN TREFFER:
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
🔢 Hauptzahlen: {best_main_matches} Treffer
|
|
⭐ Eurozahlen: {best_euro_matches} Treffer
|
|
🎯 Gesamt: {total_matches} Treffer
|
|
📊 Bewertung: {rating}
|
|
|
|
📈 DURCHSCHNITT ALLER TIPPS:
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
🔢 Main: {evaluation.get('avg_main', 0):.1f} Treffer
|
|
⭐ Euro: {evaluation.get('avg_euro', 0):.1f} Treffer
|
|
|
|
💡 Details im Performance-Report!
|
|
"""
|
|
|
|
# Sende via Telegram
|
|
if self.config.get("telegram", {}).get("enabled"):
|
|
self._send_telegram(message)
|
|
|
|
# Sende via Email
|
|
if self.config.get("email", {}).get("enabled"):
|
|
self._send_email(subject, message)
|
|
|
|
def send_error(self, error_msg: str, context: str = ""):
|
|
"""
|
|
Benachrichtigung: Fehler aufgetreten.
|
|
|
|
Args:
|
|
error_msg: Fehlermeldung
|
|
context: Kontext (z.B. "Tipp-Generierung")
|
|
"""
|
|
subject = f"❌ Eurojackpot Fehler: {context}"
|
|
|
|
message = f"""❌ FEHLER IM EUROJACKPOT-SYSTEM
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
|
|
📍 Kontext: {context}
|
|
🔴 Fehler: {error_msg}
|
|
|
|
💡 Bitte System-Logs prüfen!
|
|
"""
|
|
|
|
# Sende nur via Telegram (Fehler sind dringender)
|
|
if self.config.get("telegram", {}).get("enabled"):
|
|
self._send_telegram(message)
|
|
|
|
def _send_telegram(self, message: str):
|
|
"""Sendet Nachricht via Telegram Bot."""
|
|
try:
|
|
telegram_config = self.config.get("telegram", {})
|
|
bot_token = telegram_config.get("bot_token")
|
|
chat_id = telegram_config.get("chat_id")
|
|
|
|
if not bot_token or not chat_id:
|
|
return
|
|
|
|
url = f"https://api.telegram.org/bot{bot_token}/sendMessage"
|
|
|
|
payload = {
|
|
"chat_id": chat_id,
|
|
"text": message,
|
|
"disable_web_page_preview": True
|
|
}
|
|
|
|
response = requests.post(url, json=payload, timeout=10)
|
|
|
|
if response.status_code != 200:
|
|
error_detail = response.json() if response.text else {}
|
|
print(f"⚠️ Telegram API Error: {error_detail}")
|
|
|
|
response.raise_for_status()
|
|
|
|
except Exception as e:
|
|
print(f"⚠️ Telegram-Benachrichtigung fehlgeschlagen: {e}")
|
|
|
|
def _send_email(self, subject: str, message: str):
|
|
"""Sendet E-Mail via SMTP."""
|
|
try:
|
|
email_config = self.config.get("email", {})
|
|
|
|
smtp_server = email_config.get("smtp_server")
|
|
smtp_port = email_config.get("smtp_port", 587)
|
|
smtp_user = email_config.get("smtp_user")
|
|
smtp_password = email_config.get("smtp_password")
|
|
from_email = email_config.get("from_email", smtp_user)
|
|
to_email = email_config.get("to_email")
|
|
|
|
if not all([smtp_server, smtp_user, smtp_password, to_email]):
|
|
return
|
|
|
|
# Erstelle E-Mail
|
|
msg = MIMEMultipart("alternative")
|
|
msg["Subject"] = subject
|
|
msg["From"] = from_email
|
|
msg["To"] = to_email
|
|
|
|
# Plain text
|
|
text_part = MIMEText(message, "plain", "utf-8")
|
|
msg.attach(text_part)
|
|
|
|
# Sende via SMTP
|
|
with smtplib.SMTP(smtp_server, smtp_port) as server:
|
|
server.starttls()
|
|
server.login(smtp_user, smtp_password)
|
|
server.send_message(msg)
|
|
|
|
except Exception as e:
|
|
print(f"⚠️ E-Mail-Benachrichtigung fehlgeschlagen: {e}")
|
|
|
|
def test_notifications(self):
|
|
"""Testet alle konfigurierten Benachrichtigungen."""
|
|
print("\n🧪 TESTE BENACHRICHTIGUNGEN")
|
|
print("=" * 70)
|
|
|
|
test_message = """🧪 TEST-BENACHRICHTIGUNG
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
|
|
Das Eurojackpot-Benachrichtigungssystem funktioniert! ✅
|
|
|
|
Wenn du diese Nachricht erhältst, ist alles korrekt konfiguriert.
|
|
"""
|
|
|
|
# Test Telegram
|
|
if self.config.get("telegram", {}).get("enabled"):
|
|
print("\n📱 Teste Telegram...")
|
|
self._send_telegram(test_message)
|
|
print(" ✅ Telegram-Nachricht gesendet")
|
|
|
|
# Test Email
|
|
if self.config.get("email", {}).get("enabled"):
|
|
print("\n📧 Teste E-Mail...")
|
|
self._send_email("🧪 Eurojackpot Test", test_message)
|
|
print(" ✅ E-Mail gesendet")
|
|
|
|
print("\n" + "=" * 70)
|
|
print("✅ Test abgeschlossen - prüfe deine Nachrichten!")
|
|
|
|
|
|
def main():
|
|
"""Test-Funktion."""
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(description="Eurojackpot Notifier Test")
|
|
parser.add_argument(
|
|
'--test',
|
|
action='store_true',
|
|
help='Sendet Test-Benachrichtigungen'
|
|
)
|
|
parser.add_argument(
|
|
'--config',
|
|
type=str,
|
|
help='Pfad zur Config-Datei'
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
notifier = EurojackpotNotifier(config_file=args.config)
|
|
|
|
if args.test:
|
|
notifier.test_notifications()
|
|
else:
|
|
print("Nutze --test um Test-Benachrichtigungen zu senden")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|