Spaces:
Runtime error
Runtime error
| #!/usr/bin/env python3 | |
| """Script para capturar apostas abertas da Superbet via clipboard. | |
| Uso: | |
| 1. Abra a página "Minhas Apostas" da Superbet no navegador | |
| 2. Copie o texto do card da aposta (Ctrl+C ou Cmd+C) | |
| 3. Rode: ./scripts/capture_superbet.py | |
| 4. O script lê a área de transferência, parseia e envia para a API | |
| 5. A aposta aparece no painel ao vivo automaticamente | |
| Dependências: | |
| pip install pyperclip requests | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import sys | |
| try: | |
| import pyperclip | |
| import requests | |
| except ImportError: | |
| print("Instale as dependências: pip install pyperclip requests") | |
| sys.exit(1) | |
| API_BASE = "http://127.0.0.1:8000" | |
| def parse_clipboard(text: str) -> dict | None: | |
| """Extrai dados da aposta a partir do texto copiado da Superbet.""" | |
| lines = [line.strip() for line in text.strip().splitlines() if line.strip()] | |
| if not lines: | |
| return None | |
| # Primeira linha com nomes do evento | |
| event_line = lines[0] | |
| # Tentar separar por ·, —, -, vs, x | |
| teams = None | |
| for sep in ("·", "—", " - ", " vs ", " x "): | |
| if sep in event_line: | |
| parts = event_line.split(sep) | |
| if len(parts) >= 2: | |
| teams = (parts[0].strip(), parts[1].strip()) | |
| break | |
| if not teams: | |
| return None | |
| # Linhas com "CRIAR APOSTA" → combo | |
| # Linhas com "Total de Gols" → over/under | |
| # Linhas com "Ambas as Equipes Marcam" → btts | |
| # Linhas com "Resultado Final" → h2h | |
| picks = [] | |
| import re | |
| for ln in lines: | |
| ln_lower = ln.lower() | |
| if "resultado final" in ln_lower: | |
| m = re.search(r"resultado final.*[-—]\s*([1X2])", ln, re.I) | |
| outcome = m.group(1) if m else None | |
| picks.append({"market": "h2h", "outcome": outcome or "1"}) | |
| elif "total de gols" in ln_lower: | |
| m = re.search(r"(?:mais|menos) de ([\d.]+)", ln_lower) | |
| val = m.group(1) if m else "2.5" | |
| over = "mais" in ln_lower | |
| picks.append({"market": f"over_{val}", "outcome": "yes" if over else "no", "target_value": val}) | |
| elif "ambas as equipes marcam" in ln_lower: | |
| yes = "sim" in ln_lower | |
| picks.append({"market": "btts_any_half" if "algum dos tempos" in ln_lower else "btts", "outcome": "yes" if yes else "no"}) | |
| elif re.search(r"\w+\s*-\s*vence|vencer", ln_lower): | |
| picks.append({"market": "h2h", "outcome": "home" if teams[0].lower() in ln_lower else "away"}) | |
| elif "próximo gol" in ln_lower or "proximo gol" in ln_lower or "2º gol" in ln_lower: | |
| m = re.search(r"-\s*(\w+)", ln) | |
| outcome = None | |
| if m: | |
| name = m.group(1).strip().lower() | |
| if name in ("1", teams[0].lower().split()[0]): | |
| outcome = "home" | |
| elif name in ("2", teams[1].lower().split()[0]): | |
| outcome = "away" | |
| picks.append({"market": "next_goal", "outcome": outcome or "home"}) | |
| elif "criar aposta" in ln_lower: | |
| picks.append({"market": "combo", "outcome": "combo"}) | |
| # Extrair valor apostado | |
| stake = 0.0 | |
| for ln in lines: | |
| m = re.search(r"APOSTA\s*([\d.,]+)", ln) | |
| if m: | |
| stake = float(m.group(1).replace(".", "").replace(",", ".")) | |
| break | |
| # Extrair odd total | |
| total_odd = 0.0 | |
| for ln in lines: | |
| m = re.search(r"ODDS\s*TOTAIS\s*([\d.,]+)", ln) | |
| if m: | |
| total_odd = float(m.group(1).replace(",", ".")) | |
| break | |
| m = re.search(r"@\s*([\d.,]+)", ln) | |
| if m: | |
| total_odd = float(m.group(1).replace(",", ".")) | |
| # Extrair ticket code (ex: 892P-1YINSZ) | |
| ticket_code = None | |
| for ln in lines: | |
| m = re.search(r"\b([A-Z0-9]{3,}-?[A-Z0-9]{5,})\b", ln) | |
| if m and not re.search(r"ODDS|APOSTA|Ganho|R\\$", ln): | |
| ticket_code = m.group(1) | |
| break | |
| # Extrair ganho potencial | |
| potential_return = 0.0 | |
| for ln in lines: | |
| m = re.search(r"GANHO\s+POTENCIAL\s*R?\$?\s*([\d.,]+)", ln, re.I) | |
| if m: | |
| potential_return = float(m.group(1).replace(".", "").replace(",", ".")) | |
| break | |
| m = re.search(r"Retorno\s*R?\$?\s*([\d.,]+)", ln, re.I) | |
| if m: | |
| potential_return = float(m.group(1).replace(".", "").replace(",", ".")) | |
| break | |
| if not picks or stake <= 0 or total_odd <= 1: | |
| return None | |
| return { | |
| "event_name": event_line, | |
| "home_team": teams[0], | |
| "away_team": teams[1], | |
| "picks": picks, | |
| "stake": stake, | |
| "odds_placed": total_odd, | |
| "potential_return": potential_return if potential_return > 0 else round(stake * total_odd, 2), | |
| "ticket_code": ticket_code, | |
| "source": "clipboard_capture", | |
| } | |
| def send_to_api(payload: dict, api_key: str | None = None) -> dict: | |
| headers = {"Content-Type": "application/json"} | |
| if api_key: | |
| headers["X-API-Key"] = api_key | |
| resp = requests.post( | |
| f"{API_BASE}/user/open-bets", | |
| json=payload, | |
| headers=headers, | |
| timeout=15, | |
| ) | |
| resp.raise_for_status() | |
| return resp.json() | |
| def main() -> None: | |
| parser = argparse.ArgumentParser(description="Captura aposta da Superbet via clipboard") | |
| parser.add_argument("--api-key", default=None, help="API key para autenticação") | |
| parser.add_argument("--text", default=None, help="Texto da aposta (em vez de clipboard)") | |
| parser.add_argument("--paste", action="store_true", help="Lê JSON do clipboard e envia direto (modo DevTools)") | |
| args = parser.parse_args() | |
| if args.paste: | |
| raw = pyperclip.paste() | |
| try: | |
| payload_list = json.loads(raw) | |
| if not isinstance(payload_list, list): | |
| payload_list = [payload_list] | |
| except json.JSONDecodeError: | |
| print("❌ JSON inválido no clipboard.") | |
| sys.exit(1) | |
| for payload in payload_list: | |
| payload.setdefault("source", "clipboard_paste") | |
| try: | |
| result = send_to_api(payload, api_key=args.api_key) | |
| print(f"🎉 Enviado: {payload.get('event_name')} — ID {result.get('id')}") | |
| except requests.HTTPError as e: | |
| print(f"❌ Erro: {e}") | |
| return | |
| text = args.text or pyperclip.paste() | |
| if not text or len(text) < 20: | |
| print("❌ Área de transferência vazia ou texto muito curto.") | |
| print(" Copie o card da aposta da Superbet e tente novamente.") | |
| sys.exit(1) | |
| print("🔍 Analisando texto copiado...") | |
| payload = parse_clipboard(text) | |
| if not payload: | |
| print("❌ Não consegui extrair dados válidos.") | |
| print(" Certifique-se de copiar o card completo: evento, mercado, valor e odd.") | |
| sys.exit(1) | |
| print(f"✅ Aposta identificada: {payload['home_team']} × {payload['away_team']}") | |
| print(f" Mercados: {', '.join(p['market'] for p in payload['picks'])}") | |
| print(f" Stake: R$ {payload['stake']:.2f} · Odd: {payload['odds_placed']:.2f}") | |
| print(f" Ganho potencial: R$ {payload['potential_return']:.2f}") | |
| print(f" Ticket: {payload.get('ticket_code') or 'não identificado'}") | |
| print("📡 Enviando para a API...") | |
| try: | |
| result = send_to_api(payload, api_key=args.api_key) | |
| print(f"🎉 Aposta cadastrada! ID: {result.get('id')}") | |
| print(" Aposta aparece no painel: /ao-vivo") | |
| except requests.HTTPError as e: | |
| print(f"❌ Erro da API: {e}") | |
| try: | |
| print(e.response.json().get("detail", "")) | |
| except Exception: | |
| pass | |
| sys.exit(1) | |
| if __name__ == "__main__": | |
| main() | |