#!/usr/bin/env python3 """ Script de prueba con los REQUEST BODIES COMPLETOS descubiertos en el análisis de ingeniería reversa del código decompilado. Incluye el objeto DetailedInfoDTO completo para estaciones. """ import requests import json import time from datetime import datetime # Headers correctos del análisis HEADERS_CIRCULATION = { "Content-Type": "application/json;charset=utf-8", "User-key": "f4ce9fbfa9d721e39b8984805901b5df" } HEADERS_STATIONS = { "Content-Type": "application/json;charset=utf-8", "User-key": "0d021447a2fd2ac64553674d5a0c1a6f" } # URLs base BASE_CIRCULATION = "https://circulacion.api.adif.es" BASE_STATIONS = "https://estaciones.api.adif.es" def test_endpoint(name, method, url, headers, data=None, save_response=False): """Probar un endpoint y mostrar resultado detallado""" print(f"\n{'='*70}") print(f"TEST: {name}") print(f"{'='*70}") print(f"Method: {method}") print(f"URL: {url}") print(f"Headers: {json.dumps(headers, indent=2)}") if data: print(f"\nRequest Body:") print(json.dumps(data, indent=2, ensure_ascii=False)) try: start_time = time.time() if method == "GET": response = requests.get(url, headers=headers, timeout=15, verify=True) elif method == "POST": response = requests.post(url, headers=headers, json=data, timeout=15, verify=True) else: print(f"❌ Método {method} no soportado") return False elapsed = time.time() - start_time print(f"\n⏱️ Tiempo de respuesta: {elapsed:.2f}s") print(f"📊 Status Code: {response.status_code}") print(f"📦 Content-Length: {len(response.content)} bytes") print(f"📋 Response Headers:") for key, value in response.headers.items(): print(f" {key}: {value}") if response.status_code == 200: print("\n✅ SUCCESS - La petición funcionó!") try: result = response.json() resp_str = json.dumps(result, indent=2, ensure_ascii=False) print(f"\n📄 Response Body (primeros 1500 chars):") print(resp_str[:1500]) if len(resp_str) > 1500: print(f"\n... ({len(resp_str) - 1500} caracteres más)") if save_response: filename = f"response_{name.replace(' ', '_').replace('/', '_')}.json" with open(filename, 'w', encoding='utf-8') as f: json.dump(result, f, indent=2, ensure_ascii=False) print(f"\n💾 Respuesta guardada en: {filename}") return True except json.JSONDecodeError: print(f"\n⚠️ Respuesta no es JSON válido:") print(response.text[:500]) return False elif response.status_code == 401: print("\n🔒 ERROR 401 - UNAUTHORIZED") print("Problema de autenticación. Se necesitan headers adicionales.") print(f"Response: {response.text[:500]}") return False elif response.status_code == 403: print("\n🚫 ERROR 403 - FORBIDDEN") print("Acceso denegado. Posible problema con User-key o autenticación.") print(f"Response: {response.text[:500]}") return False elif response.status_code == 400: print("\n❌ ERROR 400 - BAD REQUEST") print("El formato del body es incorrecto.") print(f"Response: {response.text[:500]}") return False elif response.status_code == 404: print("\n❌ ERROR 404 - NOT FOUND") print("El endpoint no existe.") print(f"Response: {response.text[:500]}") return False else: print(f"\n❌ ERROR {response.status_code}") print(f"Response: {response.text[:500]}") return False except requests.exceptions.Timeout: print("\n⏱️ ERROR: Timeout - El servidor no respondió a tiempo") return False except requests.exceptions.SSLError as e: print(f"\n🔒 ERROR SSL: {str(e)}") print("Posible certificate pinning activo en el servidor") return False except requests.exceptions.ConnectionError as e: print(f"\n🌐 ERROR de Conexión: {str(e)}") return False except Exception as e: print(f"\n💥 EXCEPTION: {type(e).__name__}: {str(e)}") return False def main(): print("=" * 70) print("PRUEBAS CON REQUEST BODIES COMPLETOS") print("Análisis de ingeniería reversa - Código decompilado") print("=" * 70) print(f"Fecha: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") results = {} # ========================================================================= # TEST 1: Detalles de Estación con DetailedInfoDTO COMPLETO # ========================================================================= print("\n\n" + "🔍 " * 20) print("TEST 1: Detalles de Estación (DetailedInfoDTO completo)") print("🔍 " * 20) # Este es el body COMPLETO descubierto en el código results['station_details'] = test_endpoint( "Station Details - Madrid Atocha", "POST", f"{BASE_STATIONS}/portroyalmanager/secure/stations/onestation/", HEADERS_STATIONS, { "detailedInfo": { "extendedStationInfo": True, "stationActivities": True, "stationBanner": True, "stationCommercialServices": True, "stationInfo": True, "stationServices": True, "stationTransportServices": True }, "stationCode": "10200", # Madrid Atocha "token": "test_token_12345" # Token de prueba }, save_response=True ) # ========================================================================= # TEST 2: Observaciones de Estación # ========================================================================= print("\n\n" + "🔍 " * 20) print("TEST 2: Observaciones de Estación") print("🔍 " * 20) results['station_observations'] = test_endpoint( "Station Observations - Multiple Stations", "POST", f"{BASE_STATIONS}/portroyalmanager/secure/stationsobservations/", HEADERS_STATIONS, { "stationCodes": ["10200", "10302", "71801"] # Madrid, Madrid, Barcelona }, save_response=True ) # ========================================================================= # TEST 3: Salidas/Departures - TrafficCirculationPathRequest completo # ========================================================================= print("\n\n" + "🔍 " * 20) print("TEST 3: Salidas/Departures") print("🔍 " * 20) results['departures_all'] = test_endpoint( "Departures - Madrid Atocha (ALL traffic)", "POST", f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpaths/departures/traffictype/", HEADERS_CIRCULATION, { "commercialService": "BOTH", "commercialStopType": "BOTH", "destinationStationCode": None, "originStationCode": None, "page": { "pageNumber": 0 }, "stationCode": "10200", "trafficType": "ALL" }, save_response=True ) # ========================================================================= # TEST 4: Llegadas/Arrivals # ========================================================================= print("\n\n" + "🔍 " * 20) print("TEST 4: Llegadas/Arrivals") print("🔍 " * 20) results['arrivals_cercanias'] = test_endpoint( "Arrivals - Madrid Atocha (CERCANIAS)", "POST", f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpaths/arrivals/traffictype/", HEADERS_CIRCULATION, { "commercialService": "BOTH", "commercialStopType": "BOTH", "destinationStationCode": None, "originStationCode": None, "page": { "pageNumber": 0 }, "stationCode": "10200", "trafficType": "CERCANIAS" }, save_response=True ) # ========================================================================= # TEST 5: Entre Estaciones # ========================================================================= print("\n\n" + "🔍 " * 20) print("TEST 5: Entre Estaciones") print("🔍 " * 20) results['between_stations'] = test_endpoint( "Between Stations - Madrid to Barcelona", "POST", f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpaths/betweenstations/traffictype/", HEADERS_CIRCULATION, { "commercialService": "BOTH", "commercialStopType": "BOTH", "destinationStationCode": "71801", # Barcelona Sants "originStationCode": "10200", # Madrid Atocha "page": { "pageNumber": 0 }, "stationCode": None, "trafficType": "ALL" }, save_response=True ) # ========================================================================= # TEST 6: Detalles de Ruta - OneOrSeveralPathsRequest # ========================================================================= print("\n\n" + "🔍 " * 20) print("TEST 6: Detalles de Ruta Específica") print("🔍 " * 20) # Timestamp para hoy a las 00:00 today_timestamp = int(datetime.now().replace(hour=0, minute=0, second=0, microsecond=0).timestamp() * 1000) results['onepaths'] = test_endpoint( "OnePaths - Madrid to Barcelona", "POST", f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpathdetails/onepaths/", HEADERS_CIRCULATION, { "allControlPoints": True, "commercialNumber": None, "destinationStationCode": "71801", "launchingDate": today_timestamp, # Timestamp en milisegundos "originStationCode": "10200" }, save_response=True ) # ========================================================================= # TEST 7: Composiciones de Tren # ========================================================================= print("\n\n" + "🔍 " * 20) print("TEST 7: Composiciones de Tren") print("🔍 " * 20) results['compositions'] = test_endpoint( "Train Compositions", "POST", f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpaths/compositions/path/", HEADERS_CIRCULATION, { "allControlPoints": False, "commercialNumber": None, "destinationStationCode": "71801", "launchingDate": None, "originStationCode": "10200" }, save_response=True ) # ========================================================================= # TEST 8: Salidas con diferentes TrafficTypes # ========================================================================= print("\n\n" + "🔍 " * 20) print("TEST 8: Diferentes TrafficTypes") print("🔍 " * 20) for traffic_type in ["AVLDMD", "TRAVELERS", "GOODS", "OTHERS"]: results[f'departures_{traffic_type.lower()}'] = test_endpoint( f"Departures - TrafficType={traffic_type}", "POST", f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpaths/departures/traffictype/", HEADERS_CIRCULATION, { "commercialService": "BOTH", "commercialStopType": "BOTH", "page": {"pageNumber": 0}, "stationCode": "10200", "trafficType": traffic_type } ) # ========================================================================= # RESUMEN FINAL # ========================================================================= print("\n\n" + "="*70) print("📊 RESUMEN DE PRUEBAS") print("="*70) total = len(results) passed = sum(1 for v in results.values() if v) failed = total - passed print(f"\n📈 Estadísticas:") print(f" Total de pruebas: {total}") print(f" ✅ Exitosas: {passed}") print(f" ❌ Fallidas: {failed}") print(f" 📊 Tasa de éxito: {(passed/total*100):.1f}%") print(f"\n📋 Detalle por prueba:") for test_name, result in results.items(): status = "✅ PASS" if result else "❌ FAIL" print(f" {status} - {test_name}") print("\n" + "="*70) if passed == total: print("🎉 ¡ÉXITO TOTAL! Todas las pruebas pasaron.") print("Los request bodies son correctos y el servidor los acepta.") elif passed > 0: print(f"⚠️ ÉXITO PARCIAL: {passed}/{total} pruebas funcionaron.") print("\nLas pruebas fallidas probablemente requieren:") print(" - Headers adicionales de autenticación (X-CanalMovil-*)") print(" - Token válido generado por el sistema de autenticación HMAC") print("\nVer API_REQUEST_BODIES.md sección 5 para más detalles.") else: print("❌ TODAS LAS PRUEBAS FALLARON") print("\nPosibles causas:") print(" 1. Sistema de autenticación HMAC-SHA256 requerido") print(" 2. Headers X-CanalMovil-* faltantes") print(" 3. Certificate pinning activo") print(" 4. Servidor requiere User-Agent específico") print("\nConsultar README.md sección 'Sistema de Autenticación'") print("="*70 + "\n") if __name__ == "__main__": main()