#!/usr/bin/env python3 """ Agent Turf Autonome - Prédictions quotidiennes Usage: python3 agent_turf.py [--dry-run] """ import sqlite3 import subprocess import sys import json import os from datetime import datetime, timedelta from pathlib import Path DB_PATH = "/home/h3r7/turf_scraper/turf.db" LOG_FILE = "/home/h3r7/turf_scraper/logs/agent_turf.log" VPS_HOST = "10.0.1.1" def log(msg: str): """Log avec timestamp""" timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") line = f"[{timestamp}] {msg}" print(line) os.makedirs(os.path.dirname(LOG_FILE), exist_ok=True) with open(LOG_FILE, "a") as f: f.write(line + "\n") def sync_database(): """Sync base de données depuis VPS""" log("=== SYNC DATABASE ===") # Check remote size cmd = f"sshpass -p 'Cronstadt35*' ssh -o StrictHostKeyChecking=no h3r7@{VPS_HOST} stat -c %s {DB_PATH}" result = subprocess.run(cmd, shell=True, capture_output=True, text=True) if result.returncode != 0: log(f"ERROR: Cannot connect to VPS") return False remote_size = int(result.stdout.strip()) local_size = os.path.getsize(DB_PATH) if os.path.exists(DB_PATH) else 0 if remote_size == local_size: log(f"Database up to date ({remote_size} bytes)") return True log(f"Syncing database (remote: {remote_size}, local: {local_size})...") cmd = f"sshpass -p 'Cronstadt35*' scp -o StrictHostKeyChecking=no h3r7@{VPS_HOST}:{DB_PATH} {DB_PATH}.tmp" result = subprocess.run(cmd, shell=True, capture_output=True) if result.returncode == 0: os.rename(DB_PATH + ".tmp", DB_PATH) log(f"Database synced successfully") return True else: log(f"ERROR: Sync failed") return False def generate_predictions(): """Génère les prédictions du jour""" log("=== GENERATE PREDICTIONS ===") conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row cursor = conn.cursor() today = datetime.now().strftime("%Y-%m-%d") # Get today's races cursor.execute( """ SELECT DISTINCT c.date_programme, c.num_reunion, c.num_course, c.libelle, c.heure_depart_str, r.hippodrome_court FROM pmu_courses c JOIN pmu_reunions r ON r.date_programme = c.date_programme AND r.num_reunion = c.num_reunion WHERE c.date_programme = ? AND r.pays_code = 'FRA' ORDER BY c.heure_depart_str """, (today,), ) races = cursor.fetchall() log(f"Found {len(races)} races today") predictions = [] for race in races: date, num_reunion, num_course, libelle, heure, hippodrome = race # Get scoring cursor.execute( """ SELECT horse_name, horse_number, score, cote, rang_scoring FROM scoring WHERE date = ? AND race_name LIKE ? ORDER BY rang_scoring LIMIT 4 """, (date, f"%{libelle}%"), ) scores = cursor.fetchall() if scores: pred = { "race": f"{heure} - {hippodrome} - {libelle}", "top4": [dict(s) for s in scores], } predictions.append(pred) log( f" {pred['race'][:50]} -> {', '.join([s['horse_name'] for s in scores[:3]])}" ) conn.close() return predictions def save_recommendations(predictions): """Sauvegarde les recommandations en base""" log("=== SAVE RECOMMENDATIONS ===") conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() today = datetime.now().strftime("%Y-%m-%d") # Clear today's recommendations cursor.execute("DELETE FROM recommendations WHERE date = ?", (today,)) for pred in predictions: race_name = pred["race"] # Add top 4 for i, horse in enumerate(pred["top4"]): if i < 4: # TOP 4 cursor.execute( """ INSERT INTO recommendations (date, race_name, type_pari, cheval1, numero1, cote, mise, confiance, justification) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( today, race_name, f"top{i + 1}", horse["horse_name"], horse["horse_number"], horse["cote"], 2.0, f"TOP {i + 1}", f"Score: {horse['score']}, Rang scoring: {horse['rang_scoring']}", ), ) # Add simple winner (top 1) top1 = pred["top4"][0] cursor.execute( """ INSERT INTO recommendations (date, race_name, type_pari, cheval1, numero1, cote, mise, confiance, justification) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( today, race_name, "simple_gagnant", top1["horse_name"], top1["horse_number"], top1["cote"], 2.0, "🔥 FORTE" if top1["score"] > 60 else "✅ BONNE", f"Score: {top1['score']}, Favorite prediction", ), ) conn.commit() conn.close() log(f"Saved {len(predictions)} race recommendations") def notify_telegram(predictions): """Envoie les prédictions du jour via Telegram""" import urllib.request config_path = "/home/h3r7/turf_scraper/config_telegram.json" if not os.path.exists(config_path): log("No Telegram config found, skipping notification") return try: with open(config_path, "r") as f: cfg = json.load(f) token = cfg.get("token", "") chat_id = cfg.get("chat_id", "") if not token or not chat_id: log("Telegram config missing token or chat_id") return except Exception as e: log(f"Error reading Telegram config: {e}") return today = datetime.now().strftime("%d/%m/%Y") lines = [f"🏇 *Prédictions Turf — {today}*", ""] if not predictions: lines.append("Aucune prédiction disponible aujourd'hui.") else: for pred in predictions: race_label = pred.get("race", "Course inconnue") top4 = pred.get("top4", []) lines.append(f"📍 *{race_label}*") for i, horse in enumerate(top4, 1): name = horse.get("horse_name", "?") score = horse.get("score", 0) cote = horse.get("cote", "?") icon = "🥇" if i == 1 else ("🥈" if i == 2 else ("🥉" if i == 3 else "4️⃣")) lines.append(f" {icon} {name} — score: {score} — cote: {cote}") lines.append("") text = "\n".join(lines) url = f"https://api.telegram.org/bot{token}/sendMessage" payload = json.dumps({ "chat_id": chat_id, "text": text, "parse_mode": "Markdown" }).encode("utf-8") try: req = urllib.request.Request( url, data=payload, headers={"Content-Type": "application/json"}, method="POST" ) with urllib.request.urlopen(req, timeout=10) as resp: result = json.loads(resp.read().decode("utf-8")) if result.get("ok"): log(f"Telegram notification sent ({len(predictions)} races)") else: log(f"Telegram API error: {result}") except Exception as e: log(f"Error sending Telegram notification: {e}") def run_backtest_yesterday(): """Lance le backtest sur les résultats d'hier""" log("=== RUN BACKTEST ===") yesterday = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d") # Check if we have results for yesterday conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() cursor.execute( """ SELECT COUNT(*) FROM pmu_partants WHERE date_programme = ? AND ordre_arrivee > 0 """, (yesterday,), ) count = cursor.fetchone()[0] conn.close() if count > 0: log(f"Running backtest for {yesterday}...") # Run backtest script cmd = f"cd /home/h3r7/turf_scraper && python3 backtest_analyzer.py --date {yesterday}" result = subprocess.run(cmd, shell=True, capture_output=True, text=True) if result.returncode == 0: log("Backtest completed") else: log(f"Backtest failed: {result.stderr}") else: log(f"No results for {yesterday}, skipping backtest") def update_paris_results(): """Met à jour les paris avec les résultats PMU""" log("=== UPDATE PARIS RESULTS ===") today = datetime.now().strftime("%Y-%m-%d") cmd = ( f"cd /home/h3r7/turf_scraper && python3 update_paris_results.py --date {today}" ) result = subprocess.run(cmd, shell=True, capture_output=True, text=True) if result.returncode == 0: log(f"Paris updated: {result.stdout.strip()}") else: log(f"Paris update failed: {result.stderr}") def main(): """Point d'entrée principal""" dry_run = "--dry-run" in sys.argv log("=" * 50) log("🤖 AGENT TURF AUTONOME - DEMARRAGE") log("=" * 50) try: # 1. Sync database if not dry_run: sync_database() # 2. Generate predictions predictions = generate_predictions() # 3. Save recommendations if not dry_run and predictions: save_recommendations(predictions) # 3b. Send Telegram notification if not dry_run: notify_telegram(predictions) # 4. Create paris from recommendations if not dry_run and predictions: update_paris_results() # 5. Run backtest on yesterday if not dry_run: run_backtest_yesterday() log("=" * 50) log("✅ AGENT TURF - TERMINÉ") log("=" * 50) except Exception as e: log(f"❌ ERREUR: {e}") import traceback traceback.print_exc() sys.exit(1) if __name__ == "__main__": main()