- booking_log.txt: midnight run results (cita booked at 15:15, 3347ms RTT) - fire_at_midnight.py: original midnight launch script - book_cita.sh / book_at_midnight.py: booking shell scripts - fetch_final.py / fetch_cita.sh: API fetch utilities - services_full.js, controllers_full.js, app_real2.js: frontend source - services_real.js, url.js: API endpoint references - tipos_doc.json: document type data from API - benchmark_results.json: server load profiling results
194 lines
6.3 KiB
Python
194 lines
6.3 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Precision midnight booking for CitaPrevia Portugal Tardes (cola I).
|
|
Runs in background, pre-establishes TLS at 23:59:55, fires at 00:00:00.
|
|
|
|
User: Humberto Normey Gomez, NIE X2491454W
|
|
Service: S (SAIC), Cola: I (Portugal Tardes, Padron), Fecha: 19-10-2026
|
|
"""
|
|
import http.client
|
|
import ssl
|
|
import time
|
|
import json
|
|
import sys
|
|
from datetime import datetime
|
|
|
|
# ── User Data ──────────────────────────────────────────────
|
|
SERVICIO = "S"
|
|
COLA = "I"
|
|
FECHA = "19-10-2026"
|
|
DOC = "X2491454W"
|
|
NOMBRE = "Humberto"
|
|
APE1 = "Normey"
|
|
APE2 = "Gomez"
|
|
TIPO = "2" # NIE
|
|
TEL = "687915599"
|
|
EMAIL = "hj@normey.net"
|
|
IMPUESTOS = "N"
|
|
COMENTARIO = "Solicitud de Alta en el Padrón"
|
|
IP = "0.0.0.0"
|
|
|
|
# Slots in priority order: 16:00 first, then earliest first
|
|
HORAS = ["16:00", "14:30", "14:45", "15:00", "15:15", "15:30", "15:45", "16:15", "16:30"]
|
|
|
|
HOST = "webservices.alicante.es"
|
|
PORT = 443
|
|
PATH = "/WsCitaPreviaPublic-RESTWebService-context-root/rest/1/ScCitaVo1"
|
|
|
|
CT = "application/vnd.oracle.adf.action+json"
|
|
|
|
# ── Target: Oct 4, 2026 00:00:00 CEST ─────────────────────
|
|
target_ts = datetime(2026, 10, 4, 0, 0, 0).timestamp()
|
|
preconnect_ts = target_ts - 5 # pre-establish TLS 5s before midnight
|
|
|
|
def log(msg):
|
|
ts = datetime.now().strftime('%H:%M:%S.%f')[:-3]
|
|
print(f"[{ts}] {msg}", flush=True)
|
|
|
|
def build_body(hora, motivo_ts):
|
|
data = {
|
|
"name": "insertarCitaPrevia",
|
|
"parameters": [
|
|
{"servicio": SERVICIO},
|
|
{"cola": COLA},
|
|
{"fecha": FECHA},
|
|
{"hora": hora},
|
|
{"documento": DOC},
|
|
{"ip": IP},
|
|
{"nombre": NOMBRE},
|
|
{"apellido1": APE1},
|
|
{"apellido2": APE2},
|
|
{"tipo": TIPO},
|
|
{"telefono": TEL},
|
|
{"email": EMAIL},
|
|
{"impuestos": IMPUESTOS},
|
|
{"motivo": f"{motivo_ts} {COMENTARIO}"},
|
|
{"procedencia":"NULO"}
|
|
]
|
|
}
|
|
return json.dumps(data, separators=(',', ':'))
|
|
|
|
def build_check_body(hora):
|
|
"""getHorasCitas to check availability"""
|
|
data = {
|
|
"name": "getHorasCitas",
|
|
"parameters": [
|
|
{"servicio": SERVICIO},
|
|
{"cola": COLA},
|
|
{"fecha": FECHA}
|
|
]
|
|
}
|
|
return json.dumps(data, separators=(',', ':'))
|
|
|
|
# ── Main ───────────────────────────────────────────────────
|
|
log("Starting midnight booking bot")
|
|
log(f"Target midnight: {datetime.fromtimestamp(target_ts)}")
|
|
log(f"Pre-connect time: {datetime.fromtimestamp(preconnect_ts)}")
|
|
|
|
# Phase 1: Sleep until 5 seconds before midnight
|
|
now = time.time()
|
|
if now < preconnect_ts:
|
|
sleep_dur = preconnect_ts - now
|
|
log(f"Sleeping {sleep_dur:.0f}s until pre-connect phase...")
|
|
time.sleep(sleep_dur)
|
|
|
|
# Phase 2: Pre-establish TLS connection
|
|
log("Pre-establishing TLS connection...")
|
|
context = ssl.create_default_context()
|
|
conn = http.client.HTTPSConnection(HOST, PORT, context=context, timeout=12)
|
|
try:
|
|
conn.connect()
|
|
log("TLS connection established ✓")
|
|
except Exception as e:
|
|
log(f"Connection failed: {e}")
|
|
log("Will retry at midnight...")
|
|
conn = None
|
|
|
|
# Phase 3: Busy-wait until midnight
|
|
log("Entering busy-wait until midnight...")
|
|
while time.time() < target_ts:
|
|
pass
|
|
|
|
# Phase 4: At midnight — fire requests
|
|
log("=== MIDNIGHT! Firing booking requests ===")
|
|
success = False
|
|
|
|
for hora in HORAS:
|
|
motivo_ts = datetime.now().strftime('%H:%M:%S')
|
|
body = build_body(hora, motivo_ts)
|
|
|
|
if conn is None:
|
|
# Fallback: re-establish connection
|
|
log(f"Reconnecting for {hora}...")
|
|
context = ssl.create_default_context()
|
|
conn = http.client.HTTPSConnection(HOST, PORT, context=context, timeout=12)
|
|
conn.connect()
|
|
|
|
headers = {
|
|
"Content-Type": CT,
|
|
"Origin": "https://citaprevia.alicante.es",
|
|
"Referer": "https://citaprevia.alicante.es/CitaPrevia/",
|
|
}
|
|
|
|
log(f"→ Attempting: {FECHA} at {hora}")
|
|
try:
|
|
conn.request("POST", PATH, body=body, headers=headers)
|
|
response = conn.getresponse()
|
|
raw = response.read().decode('utf-8')
|
|
|
|
# Parse response
|
|
try:
|
|
resp_json = json.loads(raw)
|
|
result = resp_json.get('result', '')
|
|
except:
|
|
result = raw[:200] if raw else "(empty response)"
|
|
|
|
log(f" Response ({response.status}): {result}")
|
|
|
|
if result == "Cita insertada":
|
|
log(f"")
|
|
log(f"*** ✅ CITA BOOKED! ***")
|
|
log(f" Servicio: {SERVICIO} (SAC/SAIC)")
|
|
log(f" Cola: {COLA} (C/PORTUGAL 17 TARDE - PADRÓN)")
|
|
log(f" Fecha: {FECHA}")
|
|
log(f" Hora: {hora}")
|
|
log(f" Nombre: {NOMBRE} {APE1} {APE2}")
|
|
log(f" NIE: {DOC}")
|
|
log(f" Motivo: {motivo_ts} {COMENTARIO}")
|
|
log(f"")
|
|
log(f" Verify: getHorasCitas for {FECHA} — slot {hora} should be gone")
|
|
log(f" View cita: https://citaprevia.alicante.es/CitaPrevia/#/verCita/{DOC}/{SERVICIO}{COLA}{DOC}/OLD")
|
|
success = True
|
|
break
|
|
|
|
elif result == "Fecha posterior al limite permitido":
|
|
log(f" → Timing issue (server clock?). Retrying immediately...")
|
|
time.sleep(0.05)
|
|
|
|
elif result == "Hora no permitida":
|
|
log(f" → Slot taken or invalid. Trying next...")
|
|
|
|
else:
|
|
log(f" → Unexpected response. Stopping.")
|
|
break
|
|
|
|
except Exception as e:
|
|
log(f" → Error: {e}")
|
|
# Try re-establishing connection
|
|
try:
|
|
conn.close()
|
|
context = ssl.create_default_context()
|
|
conn = http.client.HTTPSConnection(HOST, PORT, context=context, timeout=12)
|
|
conn.connect()
|
|
log(" Connection re-established.")
|
|
except:
|
|
log(" Failed to re-establish connection.")
|
|
break
|
|
|
|
if not success:
|
|
log("")
|
|
log("❌ All slots failed. The 9 slots at Oct 19 may have been grabbed by bots.")
|
|
log(" Check getHorasCitas manually for remaining availability.")
|
|
|
|
log("Script finished.")
|