fix: add use tor option
This commit is contained in:
@@ -11,3 +11,11 @@ HEADLESS=false
|
|||||||
# Use mock data generation instead of denuncias.yml file (true/false)
|
# Use mock data generation instead of denuncias.yml file (true/false)
|
||||||
# Set to true to generate realistic test data automatically
|
# Set to true to generate realistic test data automatically
|
||||||
USE_MOCK_DATA=false
|
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
|
||||||
|
18
README.md
18
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.
|
**¿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
|
### 🔐 Anonimato con Tor (Opcional)
|
||||||
Si vas a hacer denuncias reales, NO uses VPN. LaLiga necesita verificar que eres un denunciante local y serio.
|
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
|
# SIEMPRE false para denuncias reales
|
||||||
USE_MOCK_DATA=false
|
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
|
## 🔧 Como Ejecutar
|
||||||
|
18
docker-compose.local.yml
Normal file
18
docker-compose.local.yml
Normal file
@@ -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
|
18
docker-compose.yml
Normal file
18
docker-compose.yml
Normal file
@@ -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
|
||||||
|
|
||||||
|
|
116
main.py
116
main.py
@@ -3,6 +3,8 @@ import yaml
|
|||||||
import random
|
import random
|
||||||
import glob
|
import glob
|
||||||
import logging
|
import logging
|
||||||
|
import requests
|
||||||
|
import time
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from playwright.sync_api import sync_playwright
|
from playwright.sync_api import sync_playwright
|
||||||
from faker import Faker
|
from faker import Faker
|
||||||
@@ -22,13 +24,63 @@ def setup_logging():
|
|||||||
logger = 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):
|
def close_cookie_consent(page):
|
||||||
try:
|
try:
|
||||||
logger.info("Waiting for cookie consent dialog to appear...")
|
logger.info("Waiting for cookie consent dialog to appear...")
|
||||||
reject_button = page.locator("#onetrust-reject-all-handler")
|
reject_button = page.locator("#onetrust-reject-all-handler")
|
||||||
|
|
||||||
# Wait longer for the cookie banner to appear after page load
|
# 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()
|
reject_button.click()
|
||||||
logger.info("Cookie consent dialog closed")
|
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 = page.locator("h2:has-text('FORMULARIO ENVIADO CORRECTAMENTE')")
|
||||||
success_heading.wait_for(state="visible", timeout=10000)
|
success_heading.wait_for(state="visible", timeout=10000)
|
||||||
logger.info("Form submitted successfully")
|
logger.info("Form submitted successfully")
|
||||||
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error submitting form: {e}")
|
logger.error(f"Error submitting form: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def return_to_form(page):
|
def return_to_form(page):
|
||||||
@@ -409,13 +463,27 @@ def main():
|
|||||||
|
|
||||||
headless = os.getenv("HEADLESS", "true").lower() == "true"
|
headless = os.getenv("HEADLESS", "true").lower() == "true"
|
||||||
use_mock_data = os.getenv("USE_MOCK_DATA", "false").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"Opening browser to: {url}")
|
||||||
logger.info(f"Headless mode: {headless}")
|
logger.info(f"Headless mode: {headless}")
|
||||||
logger.info(f"Use mock data: {use_mock_data}")
|
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:
|
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 = browser.new_page()
|
||||||
page.goto(url)
|
page.goto(url)
|
||||||
|
|
||||||
@@ -425,6 +493,8 @@ def main():
|
|||||||
if use_mock_data:
|
if use_mock_data:
|
||||||
# Continuous mock data mode - generate and submit until stopped
|
# Continuous mock data mode - generate and submit until stopped
|
||||||
submission_count = 0
|
submission_count = 0
|
||||||
|
failure_count = 0
|
||||||
|
max_failures = 3
|
||||||
logger.info("Starting continuous mock data mode - will run until stopped")
|
logger.info("Starting continuous mock data mode - will run until stopped")
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
@@ -436,31 +506,65 @@ def main():
|
|||||||
logger.info(f"Submitting for: {form_data['nombre_local']}")
|
logger.info(f"Submitting for: {form_data['nombre_local']}")
|
||||||
|
|
||||||
fill_form(page, form_data)
|
fill_form(page, form_data)
|
||||||
submit_form(page)
|
success = submit_form(page)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
failure_count = 0 # Reset failure counter on success
|
||||||
return_to_form(page)
|
return_to_form(page)
|
||||||
|
|
||||||
# Wait between submissions
|
# Wait between submissions
|
||||||
wait_time = random.randint(3, 8) # Random delay 3-8 seconds
|
wait_time = random.randint(3, 5) # Random delay 3-5 seconds
|
||||||
logger.info(f"Waiting {wait_time} seconds before next submission...")
|
logger.info(f"Waiting {wait_time} seconds before next submission...")
|
||||||
page.wait_for_timeout(wait_time * 1000)
|
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:
|
else:
|
||||||
# YAML file mode - process all submissions once
|
# YAML file mode - process all submissions once
|
||||||
form_data_list = load_form_data()
|
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):
|
for i, form_data in enumerate(form_data_list, 1):
|
||||||
logger.info(f"--- Processing submission {i}/{len(form_data_list)} ---")
|
logger.info(f"--- Processing submission {i}/{len(form_data_list)} ---")
|
||||||
logger.info(f"Submitting for: {form_data['nombre_local']}")
|
logger.info(f"Submitting for: {form_data['nombre_local']}")
|
||||||
|
|
||||||
fill_form(page, form_data)
|
fill_form(page, form_data)
|
||||||
submit_form(page)
|
success = submit_form(page)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
failure_count = 0 # Reset failure counter on success
|
||||||
|
successful_submissions += 1
|
||||||
|
|
||||||
# Return to form for next submission (except on last one)
|
# Return to form for next submission (except on last one)
|
||||||
if i < len(form_data_list):
|
if i < len(form_data_list):
|
||||||
return_to_form(page)
|
return_to_form(page)
|
||||||
logger.info("Waiting 2 seconds before next submission...")
|
logger.info("Waiting 2 seconds before next submission...")
|
||||||
page.wait_for_timeout(2000)
|
page.wait_for_timeout(2000)
|
||||||
|
else:
|
||||||
|
failure_count += 1
|
||||||
|
logger.warning(f"Submission failed. Failure count: {failure_count}/{max_failures}")
|
||||||
|
|
||||||
logger.info(f"Completed {len(form_data_list)} form submissions successfully")
|
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 {successful_submissions}/{len(form_data_list)} form submissions successfully")
|
||||||
|
|
||||||
if not headless:
|
if not headless:
|
||||||
input("Press Enter to close the browser...")
|
input("Press Enter to close the browser...")
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "l4l1g4-d3nunc145"
|
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."
|
description = "Automated LaLiga piracy reporting tool for legitimate complaints. Spanish form automation with Docker support."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.13"
|
requires-python = ">=3.13"
|
||||||
@@ -9,4 +9,5 @@ dependencies = [
|
|||||||
"playwright>=1.55.0",
|
"playwright>=1.55.0",
|
||||||
"python-dotenv>=1.1.1",
|
"python-dotenv>=1.1.1",
|
||||||
"pyyaml>=6.0.2",
|
"pyyaml>=6.0.2",
|
||||||
|
"requests>=2.31.0",
|
||||||
]
|
]
|
||||||
|
Reference in New Issue
Block a user