From c1c3e5ca53915be99add585f2208cc0fd6f96326 Mon Sep 17 00:00:00 2001 From: Jorge Teixeira Date: Sat, 13 Sep 2025 13:26:39 +0200 Subject: [PATCH] fix: add use tor option --- .env.example | 8 +++ README.md | 18 +++++- docker-compose.local.yml | 18 ++++++ docker-compose.yml | 18 ++++++ main.py | 134 ++++++++++++++++++++++++++++++++++----- pyproject.toml | 3 +- 6 files changed, 181 insertions(+), 18 deletions(-) create mode 100644 docker-compose.local.yml create mode 100644 docker-compose.yml diff --git a/.env.example b/.env.example index 0b4180e..5801b1d 100644 --- a/.env.example +++ b/.env.example @@ -11,3 +11,11 @@ HEADLESS=false # Use mock data generation instead of denuncias.yml file (true/false) # Set to true to generate realistic test data automatically USE_MOCK_DATA=false + +# Use Tor proxy for anonymity (true/false) +# When enabled, routes all browser traffic through Tor +USE_TOR=false + +# Tor proxy address (SOCKS5) +# Default Tor proxy address, change if using custom Tor configuration +TOR_PROXY=socks5://127.0.0.1:9050 diff --git a/README.md b/README.md index 5005a6b..29efe74 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,16 @@ docker run -e USE_MOCK_DATA=true jorgeteixe/laliga-denuncias:latest **¿Por qué?** Este comando genera y envía denuncias falsas automáticamente al sitio oficial de LaLiga. Solo debe usarse en entornos de desarrollo con URLs de prueba. -### 🛑 NO uses VPN -Si vas a hacer denuncias reales, NO uses VPN. LaLiga necesita verificar que eres un denunciante local y serio. +### 🔐 Anonimato con Tor (Opcional) +El sistema incluye soporte para proxy Tor para mayor privacidad. Para habilitarlo: + +```bash +# En tu archivo .env +USE_TOR=true +TOR_PROXY=socks5://127.0.0.1:9050 +``` + +**Nota**: Asegurate de tener Tor ejecutandose en tu sistema antes de habilitar esta opcion. --- @@ -84,6 +92,12 @@ HEADLESS=false # SIEMPRE false para denuncias reales USE_MOCK_DATA=false + +# Usar proxy Tor para anonimato (opcional) +USE_TOR=false + +# Direccion del proxy Tor (SOCKS5) +TOR_PROXY=socks5://127.0.0.1:9050 ``` ## 🔧 Como Ejecutar diff --git a/docker-compose.local.yml b/docker-compose.local.yml new file mode 100644 index 0000000..4f3bf0d --- /dev/null +++ b/docker-compose.local.yml @@ -0,0 +1,18 @@ +services: + tor-proxy: + image: dperson/torproxy + container_name: tor-sidecar + restart: unless-stopped + + laliga-denuncias: + build: + context: . + dockerfile: Dockerfile + container_name: laliga-denuncias + network_mode: "service:tor-proxy" + volumes: + - ./images:/app/images + environment: + - USE_MOCK_DATA=true + - USE_TOR=true + restart: always diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..b25dbf4 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,18 @@ +services: + tor-proxy: + image: dperson/torproxy + container_name: tor-sidecar + restart: unless-stopped + + laliga-denuncias: + image: jorgeteixe/laliga-denuncias:latest + container_name: laliga-denuncias + network_mode: "service:tor-proxy" + volumes: + - ./images:/app/images + environment: + - USE_MOCK_DATA=true + - USE_TOR=true + restart: always + + diff --git a/main.py b/main.py index 8f60eb5..0a9cd74 100644 --- a/main.py +++ b/main.py @@ -3,6 +3,8 @@ import yaml import random import glob import logging +import requests +import time from dotenv import load_dotenv from playwright.sync_api import sync_playwright from faker import Faker @@ -22,13 +24,63 @@ def setup_logging(): logger = setup_logging() +def wait_for_tor_connection(proxy_server): + """Wait for Tor connection to be established using Playwright""" + logger.info("Waiting for Tor connection to be established...") + + while True: + try: + # Use Playwright to check Tor status through the proxy + with sync_playwright() as p: + browser = p.chromium.launch( + headless=True, + proxy={ + "server": proxy_server + } + ) + context = browser.new_context() + page = context.new_page() + + try: + response = page.goto('https://check.torproject.org/api/ip', timeout=10000) + if response and response.status == 200: + content = page.content() + # Extract JSON from the page content + import re + json_match = re.search(r'\{.*\}', content) + if json_match: + import json + data = json.loads(json_match.group()) + if data.get("IsTor") is True: + logger.info(f"Tor connection established! IP: {data.get('IP', 'Unknown')}") + browser.close() + return + else: + logger.info(f"Not using Tor yet. Current IP: {data.get('IP', 'Unknown')}") + else: + logger.warning("Could not parse Tor check response") + else: + logger.warning(f"Tor check failed with status: {response.status if response else 'No response'}") + + except Exception as e: + logger.warning(f"Error during Tor check: {e}") + finally: + browser.close() + + except Exception as e: + logger.warning(f"Error checking Tor status: {e}") + + logger.info("Waiting 10 seconds before next Tor check...") + time.sleep(10) + + def close_cookie_consent(page): try: logger.info("Waiting for cookie consent dialog to appear...") reject_button = page.locator("#onetrust-reject-all-handler") # Wait longer for the cookie banner to appear after page load - reject_button.wait_for(state="visible", timeout=15000) + reject_button.wait_for(state="visible", timeout=30000) reject_button.click() logger.info("Cookie consent dialog closed") @@ -373,9 +425,11 @@ def submit_form(page): success_heading = page.locator("h2:has-text('FORMULARIO ENVIADO CORRECTAMENTE')") success_heading.wait_for(state="visible", timeout=10000) logger.info("Form submitted successfully") + return True except Exception as e: logger.error(f"Error submitting form: {e}") + return False def return_to_form(page): @@ -409,13 +463,27 @@ def main(): headless = os.getenv("HEADLESS", "true").lower() == "true" use_mock_data = os.getenv("USE_MOCK_DATA", "false").lower() == "true" + use_tor = os.getenv("USE_TOR", "false").lower() == "true" + proxy_address = os.getenv("TOR_PROXY", "socks5://127.0.0.1:9050") logger.info(f"Opening browser to: {url}") logger.info(f"Headless mode: {headless}") logger.info(f"Use mock data: {use_mock_data}") + logger.info(f"Use Tor: {use_tor}") + if use_tor: + logger.info(f"Tor proxy: {proxy_address}") + + # Wait for Tor connection if enabled + if use_tor: + wait_for_tor_connection(proxy_address) with sync_playwright() as p: - browser = p.chromium.launch(headless=headless) + # Configure browser with or without proxy + browser_options = {"headless": headless} + if use_tor: + browser_options["proxy"] = {"server": proxy_address} + + browser = p.chromium.launch(**browser_options) page = browser.new_page() page.goto(url) @@ -425,6 +493,8 @@ def main(): if use_mock_data: # Continuous mock data mode - generate and submit until stopped submission_count = 0 + failure_count = 0 + max_failures = 3 logger.info("Starting continuous mock data mode - will run until stopped") while True: @@ -436,31 +506,65 @@ def main(): logger.info(f"Submitting for: {form_data['nombre_local']}") fill_form(page, form_data) - submit_form(page) - return_to_form(page) + success = submit_form(page) - # Wait between submissions - wait_time = random.randint(3, 8) # Random delay 3-8 seconds - logger.info(f"Waiting {wait_time} seconds before next submission...") - page.wait_for_timeout(wait_time * 1000) + if success: + failure_count = 0 # Reset failure counter on success + return_to_form(page) + + # Wait between submissions + wait_time = random.randint(3, 5) # Random delay 3-5 seconds + logger.info(f"Waiting {wait_time} seconds before next submission...") + page.wait_for_timeout(wait_time * 1000) + else: + failure_count += 1 + logger.warning(f"Submission failed. Failure count: {failure_count}/{max_failures}") + + if failure_count >= max_failures: + logger.error(f"Reached maximum failures ({max_failures}). Exiting application.") + break + + # Wait a bit before retrying + logger.info("Waiting 5 seconds before next attempt...") + page.wait_for_timeout(5000) else: # YAML file mode - process all submissions once form_data_list = load_form_data() + failure_count = 0 + max_failures = 3 + successful_submissions = 0 for i, form_data in enumerate(form_data_list, 1): logger.info(f"--- Processing submission {i}/{len(form_data_list)} ---") logger.info(f"Submitting for: {form_data['nombre_local']}") fill_form(page, form_data) - submit_form(page) + success = submit_form(page) - # Return to form for next submission (except on last one) - if i < len(form_data_list): - return_to_form(page) - logger.info("Waiting 2 seconds before next submission...") - page.wait_for_timeout(2000) + if success: + failure_count = 0 # Reset failure counter on success + successful_submissions += 1 + + # Return to form for next submission (except on last one) + if i < len(form_data_list): + return_to_form(page) + logger.info("Waiting 2 seconds before next submission...") + page.wait_for_timeout(2000) + else: + failure_count += 1 + logger.warning(f"Submission failed. Failure count: {failure_count}/{max_failures}") + + if failure_count >= max_failures: + logger.error(f"Reached maximum failures ({max_failures}). Exiting application.") + break + + # Return to form to retry or continue + if i < len(form_data_list): + return_to_form(page) + logger.info("Waiting 5 seconds before next attempt...") + page.wait_for_timeout(5000) - logger.info(f"Completed {len(form_data_list)} form submissions successfully") + logger.info(f"Completed {successful_submissions}/{len(form_data_list)} form submissions successfully") if not headless: input("Press Enter to close the browser...") diff --git a/pyproject.toml b/pyproject.toml index 6f407d7..f016df1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "l4l1g4-d3nunc145" -version = "0.1.1" +version = "0.1.2" description = "Automated LaLiga piracy reporting tool for legitimate complaints. Spanish form automation with Docker support." readme = "README.md" requires-python = ">=3.13" @@ -9,4 +9,5 @@ dependencies = [ "playwright>=1.55.0", "python-dotenv>=1.1.1", "pyyaml>=6.0.2", + "requests>=2.31.0", ]