Panoramica

Il linguaggio di scripting di JuliuS è un JavaScript esteso con direttive speciali. Ogni script è un file di testo (.js) composto da due tipi di istruzioni che coesistono:

  • Direttive JuliuS — righe che iniziano con //#*#, interpretate dal motore Delphi per controllare browser, timing, OS e flusso.
  • Blocchi JavaScript — codice JS puro racchiuso tra //#*#SNIPPET=BEGIN e //#*#SNIPPET=END, eseguito nel browser Chromium.
Nota: Le direttive //#*# sembrano commenti JS ma vengono intercettate dall'engine prima di qualsiasi valutazione JavaScript, garantendo piena compatibilità con editor JS standard.

Struttura di uno script

1

Intestazione / Commento

Titolo, descrizione, nome della compagnia/target.

2

GLOBALV — Dichiarazione variabili

Blocco GLOBALV=BEGIN / END con tutte le variabili di input.

3

Configurazione iniziale

SCREEN_SIZE, WAIT, TARGET_FRAME=MAIN.

4

Navigazione verso l'URL target

URL_GOTO seguito da WAIT per il caricamento.

5

Interazione con la pagina

Sequenza di SNIPPET, MOUSE_CLICK, WRITETO, IF/ELSE/ENDIF, WAIT.

6

Estrazione del risultato

Definizione della funzione JS di estrazione, EVALJS, CAPTURE_SCREENSHOT.

Ciclo di esecuzione

L'engine legge lo script riga per riga in un thread separato. Per ogni riga:

  • Se inizia con //#*# → viene interpretata come direttiva JuliuS.
  • Se è compresa tra SNIPPET=BEGIN e SNIPPET=END → viene accumulata e inviata al browser come JavaScript.
  • Se inizia con /* → l'engine salta tutte le righe fino al corrispondente */.
  • Altrimenti → la riga viene ignorata dall'engine.
⚠ Attenzione: Il codice JavaScript al di fuori dei blocchi SNIPPET non viene eseguito nel browser. Tutto il JS da eseguire va obbligatoriamente dentro un blocco SNIPPET.

GLOBALV — Variabili globali

dati
//#*#GLOBALV=BEGIN … //#*#GLOBALV=ENDDichiara le variabili dello script
Definisce il blocco delle variabili globali dello script. Tutto ciò che è compreso tra GLOBALV=BEGIN e GLOBALV=END viene caricato nella lista interna GlobalV di JuliuS e può essere iniettato nel browser con GLOBALV=INJECT. Ogni riga deve essere una dichiarazione JavaScript valida var nome="valore";. I valori vengono estratti dall'engine per l'interpolazione con la sintassi {{{NOME}}}.
//#*#GLOBALV=BEGIN var marca="HONDA"; var modello="XL 650 V TRANSALP"; var data_nasc="10/01/1986"; var cu="1"; //#*#GLOBALV=END

GLOBALV=INJECT

javascript
//#*#GLOBALV=INJECTInietta le variabili nel contesto JS del browser
Esegue il testo del blocco GlobalV nel contesto JavaScript del browser. Dopo l'inject, tutte le variabili del blocco GLOBALV sono disponibili come variabili JS nella pagina corrente e possono essere usate nei successivi blocchi SNIPPET. Poiché ogni navigazione azzera il contesto JS, è necessario re-iniettare dopo ogni cambio pagina.
//#*#GLOBALV=INJECT //#*#WAIT=1 //#*#SNIPPET=BEGIN document.getElementById('inputMarca').value = marca; //#*#SNIPPET=END
💡 Best practice: Ri-iniettare GLOBALV=INJECT dopo ogni navigazione verso una nuova pagina.

Interpolazione {{{VAR}}}

dati

Le variabili definite in GLOBALV possono essere interpolate nei parametri delle direttive JuliuS usando {{{NOME_VARIABILE}}}. L'engine sostituisce il placeholder con il valore prima di eseguire il comando. I nomi sono case-insensitive.

//#*#WRITETO="TEXT={{{MARCA}}}{{TAB}}"&"CHAR_INT_MS=200" // Se il placeholder racchiude una espressione JS, viene valutata a runtime: //#*#WRITETO="ELEMENTID={{{document.querySelector('.active').id}}}"&"TEXT=ciao"

URL_GOTO

navigazione
//#*#URL_GOTO=<url>Naviga il browser verso un URL
Forza il browser Chromium embedded a navigare verso l'URL specificato. L'operazione è asincrona: usare sempre WAIT subito dopo per attendere il caricamento completo della pagina prima di interagire con il DOM.
//#*#URL_GOTO=https://www.esempio.it/pagina-target //#*#WAIT=5

WAIT

controllo flusso
//#*#WAIT=<secondi>Pausa l'esecuzione per N secondi interi
Mette in pausa l'esecuzione dello script per il numero di secondi interi specificato. Usare attese brevi (1–3 s) per operazioni UI locali, attese più lunghe (5–15 s) dopo navigazioni o submit di form con round-trip server.
//#*#WAIT=3 //#*#WAIT=10

BACK

navigazione
//#*#BACKTorna alla pagina precedente
Equivale al pulsante "Indietro" del browser. Naviga alla pagina precedente nella history del browser. Seguire sempre con un WAIT e un GLOBALV=INJECT per ripristinare il contesto JS.
//#*#BACK //#*#WAIT=3 //#*#GLOBALV=INJECT

SCREEN_SIZE

navigazione
//#*#SCREEN_SIZE=<width>,<height>Imposta le dimensioni del pannello browser
Ridimensiona il pannello del browser embedded. Tutte le coordinate dei successivi MOUSE_CLICK e OS_BROWSER_CLICK devono essere calibrate sulla risoluzione impostata con questo comando.
//#*#SCREEN_SIZE=1024,768

MOUSE_CLICK

interazione CEF
//#*#MOUSE_CLICK=<X>,<Y>Click sintetico CEF a coordinate relative al browser
Simula un click del mouse nel browser CEF alle coordinate pixel specificate, relative al pannello. Agisce tramite l'API interna CEF. Per click su elementi che non rispondono agli eventi CEF (es. elementi con pointer-events:none, scrollbar native, dialog Windows) usare OS_BROWSER_CLICK.
//#*#MOUSE_CLICK=374,494 //#*#WAIT=1

WRITETO

interazione CEF
//#*#WRITETO="..."&"..."Simula digitazione da tastiera nel browser CEF
Simula la digitazione di testo in un campo HTML del browser CEF. I parametri sono separati da & e racchiusi tra virgolette doppie. Supporta interpolazione {{{VAR}}} e tasti speciali {{TASTO}}.

Parametri

ParametroTipoDescrizione
ELEMENTIDstringID HTML dell'elemento su cui mettere il focus. Usare none per saltare il focus.
FRAMENAMEstringNome del frame/iframe target. Se omesso usa il frame con focus corrente.
TEXTstringTesto da digitare. Supporta {{{VAR}}} e {{KEY}}.
CHAR_INT_MSintegerIntervallo ms tra un carattere e l'altro (default: 200).
CHAR_INT_RANDOMS / NSe S, introduce variazioni casuali per simulare digitazione umana.
//#*#WRITETO="ELEMENTID=ctl00_txtNome"&"TEXT={{{NOME}}}{{TAB}}"&"CHAR_INT_MS=150" //#*#WRITETO="TEXT={{SELECT_ALL}}{{CANC}}{{{MODELLO}}}"&"CHAR_INT_MS=200"&"CHAR_INT_RANDOM=N"

Tasti speciali

interazione CEF

All'interno del parametro TEXT del comando WRITETO:

{{ENTER}}Invio (↵)
{{TAB}}Tabulazione
{{CANC}}Canc (Delete)
{{BACK_SPACE}}Backspace (←)
{{ESC}}Escape
{{ESCAPE}}Escape (alias)
{{END}}Fine riga
{{ARROW_DOWN}}Freccia giù
{{ARROW_UP}}Freccia su
{{ARROW_LEFT}}Freccia sinistra
{{ARROW_RIGTH}}Freccia destra
{{SELECT_ALL}}Seleziona tutto

SNIPPET

javascript
//#*#SNIPPET=BEGIN[&NOFRAME] … //#*#SNIPPET=ENDEsegue un blocco JavaScript nel browser
Tutto il codice tra SNIPPET=BEGIN e SNIPPET=END viene eseguito nel contesto JavaScript del browser Chromium. È il meccanismo principale per interagire con il DOM. L'opzione NOFRAME forza l'esecuzione nel frame principale (window) invece del frame con focus corrente.
//#*#SNIPPET=BEGIN $('#ddlMarca').val(marca).keydown(); //#*#SNIPPET=END //#*#SNIPPET=BEGIN&NOFRAME window.scrollTo(0, 0); //#*#SNIPPET=END
⚠ Attenzione: I blocchi SNIPPET non possono essere annidati. Ogni SNIPPET=BEGIN deve avere il suo SNIPPET=END.

EVALJS

javascript
//#*#EVALJS=<espressione JS>Valuta un'espressione JS e ne memorizza il risultato
Esegue una singola espressione JavaScript nel browser (timeout 10 s) e memorizza il valore restituito come risultato dell'operazione corrente, recuperabile poi via API con ?operation=getresult. Pensato per chiamare funzioni di estrazione già definite in un SNIPPET precedente.
//#*#EVALJS=preventivo(); //#*#EVALJS=document.getElementById("risultato").innerText

TARGET_FRAME

navigazione
//#*#TARGET_FRAME=MAIN | POPUPSeleziona il frame/finestra target
Imposta il contesto di esecuzione per i successivi comandi. MAIN seleziona il frame principale; POPUP seleziona una finestra popup figlia identificata dall'URL nella riga successiva.
ValoreDescrizione
MAINFrame principale della finestra browser corrente.
POPUPPopup figlio. Deve essere seguito da //#*#TARGET_URL=<url>.
//#*#TARGET_FRAME=MAIN //#*#TARGET_FRAME=POPUP //#*#TARGET_URL=https://www.esempio.it/popup-login
⚠ Attenzione: Se il popup con l'URL specificato non è aperto, l'engine lancia un'eccezione e interrompe lo script.

IF / ELSE / ENDIF

controllo flusso
//#*#IF= … //#*#ELSE … //#*#ENDIFEsecuzione condizionale basata su espressione JS
Permette di eseguire blocchi di direttive in modo condizionale. L'espressione dopo IF= viene valutata nel browser tramite EvalJS: se il risultato è truthy il blocco IF viene eseguito e il blocco ELSE saltato; se è falsy il blocco IF viene saltato e il blocco ELSE (opzionale) eseguito. Il blocco ELSE è opzionale. Gli IF possono essere annidati: l'engine gestisce correttamente la profondità di annidamento.

Valori considerati falsy

Stringa vuota "", "false", "0", "null", "undefined". Qualsiasi altro valore è considerato truthy.

// Esempio base: IF senza ELSE //#*#IF=document.querySelector('#btnAccetta') !== null //#*#SNIPPET=BEGIN document.querySelector('#btnAccetta').click(); //#*#SNIPPET=END //#*#WAIT=2 //#*#ENDIF // Esempio con ELSE //#*#IF=document.querySelector('.errore-pagina') !== null //#*#PAUSE // c'è un errore: sospende per intervento manuale //#*#ELSE //#*#SNIPPET=BEGIN $('#btnAvanti').click(); //#*#SNIPPET=END //#*#WAIT=8 //#*#ENDIF // Esempio con variabili GlobalV //#*#IF=guida === "Libera" //#*#SNIPPET=BEGIN $('#rdoGuidaLibera').click(); //#*#SNIPPET=END //#*#ENDIF // Esempio con IF annidati //#*#IF=cu < 5 //#*#IF=guida === "Libera" //#*#MOUSE_CLICK=200,300 //#*#ELSE //#*#MOUSE_CLICK=250,300 //#*#ENDIF //#*#ENDIF
Note tecniche: L'espressione viene valutata via EvalJS nel thread principale. Le variabili iniettate con GLOBALV=INJECT sono disponibili nell'espressione. Il risultato della valutazione viene scritto nel log di JuliuS per facilitare il debug (IF [expr] = valore → true/false).

PAUSE

controllo flusso
//#*#PAUSESospende l'esecuzione fino a ripresa manuale
Sospende l'esecuzione dello script fino a quando l'utente non riprende manualmente (tramite UI o API ?operation=start). Utile per interventi manuali (es. risoluzione captcha, login manuale) in punti critici del flusso. Spesso usato in combinazione con IF per sospendere solo in caso di condizioni anomale.
//#*#IF=document.querySelector('.captcha-box') !== null //#*#PAUSE // sospende: l'operatore risolve il captcha //#*#ENDIF

RNDWAIT

controllo flusso
//#*#RNDWAIT=<N>Attesa casuale in un range
Attende un numero di secondi scelto casualmente nel range [N div 2 .. N]. Il valore minimo viene calcolato automaticamente come N div 2. Utile per simulare comportamento umano e ridurre il rischio di rilevamento anti-bot rispetto a WAIT con tempi fissi.

Parametri

ParametroTipoDescrizione
NintegerSecondi massimi. Il minimo effettivo è N div 2.
//#*#RNDWAIT=10 // attende tra 5s e 10s (random) //#*#RNDWAIT=6 // attende tra 3s e 6s (random)
💡 Quando usarlo: usa RNDWAIT sulle pause lunghe (submit form, caricamento pagina) e mantieni WAIT=1 solo per sincronizzazioni brevi dove la variabilità non serve.

Commenti

controllo flusso

JuliuS supporta entrambe le sintassi di commento JavaScript. I blocchi /* ... */ sono utili per disabilitare temporaneamente intere sezioni, incluse direttive JuliuS.

// Commento su riga singola — ignorato dall'engine /* Blocco multi-riga — tutto ciò che è compreso tra /* e */ viene saltato dall'engine JuliuS, incluse eventuali direttive //#*# al suo interno. */

RESTART_CHROMIUM

sessione
//#*#RESTART_CHROMIUMReset in-memory della sessione Chromium
Esegue un reset in-memory della sessione Chromium senza riavviare fisicamente il browser. Operazioni eseguite internamente via DevTools Protocol:
1

Naviga su about:blank

Rilascia tutte le risorse attive della pagina corrente.

2

Network.clearBrowserCache

Svuota la cache HTTP in memoria.

3

Network.clearBrowserCookies

Elimina tutti i cookie della sessione.

4

Storage.clearDataForOrigin

Pulisce localStorage, sessionStorage e IndexedDB.

5

Riapplica preferenze Chromium

User-agent, zoom e altre preferenze impostate.

6

Torna su about:blank

Sessione pulita pronta per URL_GOTO. Dopo il reset attende automaticamente 3 secondi di reinizializzazione.

//#*#RESTART_CHROMIUM //#*#WAIT=2 //#*#URL_GOTO=https://... //#*#WAIT=5 //#*#GLOBALV=INJECT
💡 Quando usarlo: reset rapido della sessione tra un'esecuzione e l'altra (es. tra preventivi consecutivi sullo stesso target).

DELETE_CHROMIUM_CACHE

sessione
//#*#DELETE_CHROMIUM_CACHEReset profondo: in-memory + cancellazione su disco
Esegue un reset profondo della sessione Chromium. Prima pulisce tutto in-memory tramite DevTools (esattamente come RESTART_CHROMIUM), poi cancella fisicamente la directory cache su disco (GlobalCEFApp.RootCache).
//#*#DELETE_CHROMIUM_CACHE //#*#WAIT=5 //#*#URL_GOTO=https://...

Differenza con RESTART_CHROMIUM

 RESTART_CHROMIUMDELETE_CHROMIUM_CACHE
Tipo resetIn-memory (DevTools)In-memory + filesystem
Velocità~3s~5s (I/O disco)
Cancella file su disco
Caso d'usoReset tra esecuzioniCache corrotta, reset pre-sessione
⚠ Attenzione: usalo solo quando serve veramente. Cancellare la cache su disco è un'operazione lenta e impedisce a Chromium di riutilizzare risorse statiche già scaricate (CSS, JS, immagini) nelle navigazioni successive.

Risoluzione immagini & captcha — Panoramica

captcha

JuliuS dispone di un sistema integrato di risoluzione automatica dei captcha basato su riconoscimento immagini. Il browser non esegue OCR localmente: cattura uno screenshot del pannello CEF e lo invia, tramite WebSocket sicuro (WSS), a un resolver remoto che elabora l'immagine e restituisce la risposta (testo letto, coordinate del click, token di sblocco, ecc.). La risposta viene poi consegnata allo script o direttamente al JavaScript della pagina.

Esistono due canali di risoluzione complementari, attivabili indipendentemente:

A

Captcha Interceptor (URL-driven)

Attivo quando il toggle Captcha Interceptor è abilitato (AI_ResolverEnabled = true). JuliuS osserva l'URL del frame focalizzato del browser: se rileva la stringa captcha (escludendo le varianti invisibili size=invisible&badge=bottomright) sospende lo script, centra il div captcha alla coordinata configurata con CAPTCHA_Y e invia lo screenshot al resolver. Quando il resolver risponde con "go" lo script riprende automaticamente, oppure il resolver può inviare click remoti (tntRemoteClick) o scroll (tntJuliusScrollY) che JuliuS applica al browser.

B

Solver Bridge (DOM-driven)

Attivo anche quando l'interceptor è spento. Il JavaScript della pagina può chiamare myextension.solverRequest(reqId, kind, payload, meta) per inviare al resolver una richiesta arbitraria (es. "leggi questa immagine", "risolvi questa sfida") e ricevere la risposta su window.__juliusSolver.responses[reqId] o tramite callback registrata. Vedi Solver Bridge.

Prerequisito comune: entrambi i canali richiedono una connessione WSS attiva e validata verso il server resolver. Vedi Panoramica WSS per configurazione e login. Se la connessione non è in stato csConnectedValidated la richiesta viene scartata e (per il solver bridge) viene consegnato un errore WSS_NOT_CONNECTED al JavaScript chiamante.

CAPTCHA_Y

captcha
//#*#CAPTCHA_Y=<Y>Coordinata Y di centratura del captcha
Imposta la coordinata Y (in pixel) a cui scrollare la pagina quando l'interceptor captcha rileva un captcha attivo nel frame focalizzato. Serve a portare il div del captcha nella zona inquadrata dallo screenshot prima dell'invio al resolver, così l'immagine inviata contiene effettivamente la sfida da risolvere.

Parametri

ParametroTipoDescrizione
YintegerCoordinata Y in pixel a cui scrollare la pagina per centrare il captcha.
//#*#CAPTCHA_Y=350 // scrolla a Y=350 per centrare il captcha
⚠ Posizionamento: va impostato prima della navigazione verso la pagina che contiene il captcha. Una volta che l'interceptor rileva il captcha è già troppo tardi per cambiare la coordinata. Se omesso, JuliuS usa la coordinata configurata nelle impostazioni globali (Settings.Data.CaptchaInterceptor / captchaScrollCoordComma).
// Esempio: captcha previsto subito dopo il submit del form //#*#CAPTCHA_Y=280 //#*#URL_GOTO=https://target.com/login //#*#WAIT=5 //#*#GLOBALV=INJECT //#*#WRITETO="ELEMENTID=user"&"TEXT={{{USERNAME}}}" //#*#SNIPPET=BEGIN document.getElementById('btnLogin').click(); //#*#SNIPPET=END // Da qui l'interceptor prende il controllo: se appare un captcha // scrolla a Y=280, manda lo screenshot al resolver e attende "go" //#*#WAIT=15

Captcha Interceptor — Flusso URL-driven

captcha

L'interceptor non è una direttiva di script ma un componente sempre-attivo (quando abilitato nelle impostazioni) che osserva il browser e interviene automaticamente. Lo script non deve chiamarlo: deve solo essere consapevole che può sospenderne l'esecuzione e riprenderla.

Ciclo di vita

1

Frame change

Il browser cambia il frame focalizzato (Setframe_focused_url). L'interceptor verifica se la nuova URL contiene captcha e non contiene size=invisible&badge=bottomright (varianti invisibili di reCAPTCHA, da ignorare).

2

Pausa script

Lo script in esecuzione viene messo in pausa per fino a 180 secondi (frmScript.pauseExecution(180)) per dare tempo al resolver di rispondere.

3

Centratura captcha

Viene chiamato centerCaptchaDiv che scrolla la pagina alla coordinata Y impostata con CAPTCHA_Y (o quella di default dalle impostazioni).

4

Invio screenshot

sendScreenToResolver(notificami=true) cattura lo screenshot JPG del pannello CEF (via getSnapShotAsJPGStream), lo codifica base64 e lo invia al resolver in un messaggio tntJuliusScreenShot con metadati (dimensioni browser, scale factor, offset Y, altezza documento).

5

Elaborazione resolver

Il resolver remoto analizza l'immagine (riconoscimento testo, click su tile reCAPTCHA, ecc.) e può rispondere in tre modi: con un click remoto (tntRemoteClick) ai pixel calcolati, con uno scroll (tntJuliusScrollY) per esplorare la pagina, o con un messaggio "go" quando ha completato la soluzione.

6

Ripresa script

Quando arriva il messaggio "go", JuliuS rimuove il flag sstPaused e chiama ExecuteScript('') per riprendere dall'istruzione successiva. Se invece il frame esce dallo stato captcha (wasCaptcha diventa true → false), viene comunque inviato uno screenshot finale con stopLive=true.

Anti-duplicazione

Per evitare di sovraccaricare il resolver con immagini identiche, JuliuS calcola lo SHA2 dello screenshot e lo confronta con l'ultimo inviato (LastSendSha). Se l'hash coincide e c'è già un resolver assegnato (lblResolverGUID non vuoto), lo screenshot viene scartato silenziosamente.

Payload TtntJuliusScreenShot

CampoTipoDescrizione
pictureString (base64)JPEG dello screenshot CEF, qualità 45.
BrowserWidthIntegerLarghezza pannello browser in pixel logici (post-DPI).
BrowserHeigthIntegerAltezza pannello browser in pixel logici.
ScaleFactorSingleFattore di scala DPI del monitor (es. 1.25).
yOffsetIntegerScroll Y corrente del frame principale.
documentHeightIntegerAltezza totale del documento HTML.
notificamiBooleantrue → richiede al resolver di rispondere con click/messaggi; false → fire-and-forget (es. snapshot finale di fine sessione).
Coordinate dei click remoti: il resolver invia coordinate in pixel logici del browser. Sono calibrate sulla risoluzione impostata con SCREEN_SIZE e divise dal ScaleFactor. JuliuS le applica direttamente con clickOnBrowser senza ulteriori conversioni.

Solver Bridge — API JavaScript DOM-driven

javascript

Il Solver Bridge è un canale alternativo all'interceptor, pensato per essere chiamato esplicitamente dal JavaScript della pagina tramite un'estensione iniettata. Funziona anche quando l'interceptor è spento ed è indipendente dal pattern URL del frame. Tipicamente usato dentro un blocco SNIPPET quando lo script sa con certezza che serve una risoluzione su un payload specifico (un'immagine in <img src="data:...">, una sfida JS, un token da generare).

API esposta in pagina

// Iniettata dall'estensione JuliuS in ogni frame window.myextension.solverRequest(reqId, kind, payload, meta); // Strutture di consegna risposta window.__juliusSolver = { responses: {}, // reqId → {success, answer, error} callbacks: {} // reqId → function(resp) chiamata all'arrivo };

Parametri della richiesta

ParametroTipoDescrizione
reqIdstringID univoco scelto dal chiamante (es. GUID o timestamp). Identifica la richiesta nelle risposte.
kindstringTipo di risoluzione richiesta. Convenzione libera concordata col resolver: "image-text", "recaptcha-v2", "hcaptcha", "image-classify", ecc.
payloadstringDato da risolvere. Tipicamente un'immagine base64 (data:image/png;base64,...), un URL, un JSON serializzato.
metastringMetadati opzionali (JSON serializzato): hint, configurazione, contesto. Può essere stringa vuota.

Flusso completo

1

JS in pagina

Chiama myextension.solverRequest(reqId, kind, payload, meta).

2

Renderer CEF

Invia SendProcessMessage('solver_request', [reqId, kind, payload, meta, frameID]) al processo Browser.

3

Browser Pascal

Chromium1ProcessMessageReceived intercetta SOLVER_REQUEST_MESSAGE_NAMETThread.Queue(handleSolverRequest). Verifica che WSS sia in csConnectedValidated, altrimenti consegna errore WSS_NOT_CONNECTED.

4

Mappa pending

JuliuS salva reqId → frameID in FPendingSolverRequests (protetto da FSolverCS) per poter consegnare la risposta nel frame originario.

5

Invio WSS

dmWSsc.sendSolverRequest(TtntSolverRequest{RequestId, Kind, Payload, Meta}) serializza e invia al resolver.

6

Risposta resolver

Il resolver elabora e risponde con TtntSolverResponse{RequestId, Success, Answer, Error} sullo stesso canale WSS, conservando lo stesso RequestId.

7

Consegna a JS

deliverSolverAnswerToJS recupera il frameID originale, costruisce un piccolo JS che popola window.__juliusSolver.responses[reqId] e invoca la callback registrata (se presente), poi lo esegue nel frame corretto (fallback su MainFrame se il frame non è più valido).

8

JS in pagina

Legge la risposta tramite polling, callback o Promise wrapper, e prosegue la logica della pagina.

Esempio — wrapper Promise lato pagina

//#*#SNIPPET=BEGIN async function solveImage(imgBase64) { const reqId = 'req-' + Date.now() + '-' + Math.random(); return new Promise((resolve, reject) => { window.__juliusSolver = window.__juliusSolver || {responses:{}, callbacks:{}}; window.__juliusSolver.callbacks[reqId] = (resp) => { if (resp.success) resolve(resp.answer); else reject(new Error(resp.error || 'solver failed')); }; window.myextension.solverRequest(reqId, 'image-text', imgBase64, ''); // Timeout di sicurezza setTimeout(() => { if (window.__juliusSolver.callbacks[reqId]) { delete window.__juliusSolver.callbacks[reqId]; reject(new Error('solver timeout')); } }, 60000); }); } // Uso: prende un'immagine dalla pagina e ne legge il testo (async () => { const img = document.querySelector('img.captcha-image'); const canvas = document.createElement('canvas'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; canvas.getContext('2d').drawImage(img, 0, 0); const b64 = canvas.toDataURL('image/png'); try { const testo = await solveImage(b64); document.querySelector('#captcha-input').value = testo; window.__solverOK = true; } catch (e) { window.__solverErr = e.message; } })(); //#*#SNIPPET=END //#*#WAIT=10 // dai tempo al solver di completare //#*#IF=window.__solverOK !== true //#*#PAUSE // fallback manuale se il solver fallisce //#*#ENDIF

Forma della risposta

CampoTipoDescrizione
successbooleantrue se la risoluzione è andata a buon fine.
answerstringRisposta del resolver (testo letto, token, JSON con coordinate, ecc.). Vuoto se success=false.
errorstringCodice/messaggio errore. Valori speciali generati da JuliuS: WSS_NOT_CONNECTED (connessione WSS non validata al momento della richiesta).
💡 Indipendenza dal Captcha Interceptor: il Solver Bridge è sempre attivo se il WSS è connesso, anche con swCaptchaInterceptor spento. Usa l'interceptor per captcha riconoscibili dall'URL (es. reCAPTCHA standard) e il Solver Bridge per casi custom in cui sai esattamente cosa risolvere.

WebSocket Client (WSS) — Panoramica

comunicazione

JuliuS include un client WebSocket sicuro (WSS) che funge da canale di comunicazione bidirezionale con un server remoto. Il client è implementato in uDmWSsc.pas (modulo TdmWSsc) e usa il componente scWS per la connessione TLS. Tutta la risoluzione immagini (Captcha Interceptor + Solver Bridge) passa da questo canale.

Capacità del client

  • Connessione TLS al server resolver con autenticazione applicativa (appID + appSecret derivato + credenziali utente).
  • Scambio di messaggi strutturati JSON tipizzati (TWSRawRequest / TWSRawResponse).
  • Modello broadcast e point-to-point: i messaggi possono essere in broadcast (a tutti i resolver in ascolto) o diretti a un partner specifico (toGUID).
  • Pairing automatico col primo resolver libero che risponde via tntSetResponder.
  • Stati busy/free per comunicare disponibilità (sendI_M_busy / sendI_M_free).
  • Eventi tipizzati per ricezione: onMessageRecive, onRemoteClick, onRemoteScroll, onProcessJuliusMessage, onSolverResponse.
Non è una direttiva di script: il WSS non si controlla con direttive //#*#. È un servizio sempre-attivo configurato dalla UI principale di JuliuS o dal file JuliuS.conf.json. Lo script ne beneficia automaticamente quando interagisce con captcha o usa il Solver Bridge.

Configurazione & login

comunicazione

Impostazioni persistite

I parametri di connessione sono salvati in Settings.Data.CaptchaInterceptor (struttura TCaptchaInterceptor) all'interno di JuliuS.conf.json:

CampoTipoDescrizione
WS_EnabledBooleanAbilita il client WSS. Mappato sul toggle swWSS nella UI.
WS_ServerAddressStringURI del server WSS (es. wss://resolver.example.com:443/julius). Mappato sul campo edWebSocketServerURL.
AI_ResolverEnabledBooleanAbilita il Captcha Interceptor URL-driven. Mappato sul toggle swCaptchaInterceptor.

Sequenza di login

Quando si chiama dmWSsc.doConnect(url, userName, password, appId):

1

Apertura TLS

scWS.RequestUri := url + scWS.Connect. Se al termine lo stato non è sOpen viene sollevata l'eccezione "Connection to server failed".

2

Generazione appSecret

genAppSecret(appId) calcola HMAC-SHA2(appID, 'staceppadiminkia'). Il secret non viene mai trasmesso in chiaro — viene usato dal server per derivare l'accessToken.

3

Invio tntLoginRequest

Viene inviato un TWSRawRequest con msg_type=tntLoginRequest e content = JSON di TtntLoginRequest{appID, appSecret, userName, userPWD}.

4

Stato csConnected

Alla risposta scWSAfterConnect lo stato passa a csConnected (connesso ma non ancora validato).

5

Ricezione tntLoginResponse

Se statusCode = 200: lo stato passa a csConnectedValidated, vengono memorizzati accessToken e connGUID (assegnato dal server). Da questo momento in poi tutti i messaggi includono l'accessToken nel campo omonimo.

Auto-connect

Se in JuliuS.conf.json è impostato XtumbleConnection.autoConnect = true, il login viene eseguito automaticamente all'avvio. I parametri usati sono ServerAddress, companyId (come appId), userName, password.

Protocollo messaggi

comunicazione

Inviluppo comune TWSRawRequest / TWSRawResponse

Tutti i messaggi scambiati hanno la stessa struttura. TWSRawRequest e TWSRawResponse sono alias dello stesso record (TWSRaw). Il discriminante è msg_type; il payload tipizzato è serializzato come JSON dentro content.

CampoTipoDescrizione
msg_typeTmsg_typeTipo del messaggio (vedi tabella sotto).
accessTokenStringToken rilasciato dal server al login. Obbligatorio dopo la validazione.
contentJSON stringRecord tipizzato serializzato in JSON, specifico del msg_type.
statusCodeIntegerCodice di stato (200 = OK). Convenzioni HTTP-like.
broadcastBooleanSe true il messaggio è destinato a tutti (es. ricerca primo resolver libero). Se false va al solo toGUID.
fromGUIDStringGUID del mittente (assegnato dal server, valorizzato dal server).
toGUIDStringGUID del destinatario per messaggi point-to-point.
userNameStringUsername del mittente (per log e routing applicativo).
appIdStringID applicazione (per multi-tenancy).

Tipi di messaggio (Tmsg_type)

TipoDirezioneDescrizione
tntLoginRequest→ serverLogin iniziale. content = TtntLoginRequest.
tntLoginResponse← serverEsito login. content = TtntLoginResponse{accessToken, connGUID, statusCode, welcomeMSG}.
tntMessageMessaggio testuale generico. content = TtntMessage{msg_body, msgFrom_userName, msgTo_userName}. Il body "go" è riservato: indica al client di riprendere lo script in pausa.
tntJuliusScreenShot→ resolverInvia uno screenshot del browser per analisi. Vedi payload completo.
tntRemoteClick← resolverIl resolver richiede a JuliuS di eseguire un click. content = TtntRemoteClick{X, Y} in pixel logici del browser.
tntJuliusScrollY← resolverIl resolver richiede uno scroll verticale. content = TtntJuliusScrollY{Y} (delta in pixel).
tntSetResponderPairing client–resolver. Il primo resolver libero che risponde diventa il partnerGUID dedicato. Successivi screenshot non in broadcast vanno solo a lui.
tntBusy / tntNotBusyNotifica al server che il client è occupato o libero. Usato dal resolver per la logica di routing.
tntDisconnectedNotifica disconnessione di un partner. Resetta partnerGUID.
tntBinaryStreamRiservato per trasferimenti binari (non attualmente usato dal flusso captcha).
tntVideoCallRiservato (non attualmente implementato).
tntErrorRisposta di errore generica.
Solver bridge: per il Solver Bridge DOM sono definiti due tipi aggiuntivi gestiti tramite dmWSsc.sendSolverRequest / onSolverResponse: TtntSolverRequest{RequestId, Kind, Payload, Meta} e TtntSolverResponse{RequestId, Success, Answer, Error}. La correlazione richiesta/risposta avviene tramite RequestId.

API dei client (metodi TdmWSsc)

MetodoScopo
doConnect(url, user, pwd, appId)Connette e autentica.
doDisconnectChiude la connessione, reset partnerGUID.
sendScreenShot(data)Invia un TtntJuliusScreenShot al partner (o broadcast se partnerGUID = '').
sendClick(LClick)Inoltra al partner una richiesta di click — usato lato resolver.
sendYScroll(LScroll)Inoltra al partner uno scroll Y.
sendCustomMessage(txt)Invia un tntMessage testuale al partner o in broadcast.
sendI_M_busy / sendI_M_freeAggiorna lo stato occupazionale del client.
joinMeAsResponder(senderGUID)Si propone come responder per un sender specifico (lato resolver).
sendDisconnectPartnerComunica al partner la fine della sessione e si rimette in stato free.

Stati di connessione

comunicazione

L'enum TConnectionStatus espone tre stati. Lo stato viene aggiornato in tempo reale dalla UI tramite la property dmWSsc.ConnectionStatus e il setter associato.

StatoSignificatoOperazioni permesse
csDisconnectedNessuna connessione TCP/TLS attiva.Solo doConnect.
csConnectedSocket TLS aperto ma login non ancora confermato.Solo invio tntLoginRequest (gestito internamente da doConnect).
csConnectedValidatedLogin OK, accessToken e connGUID ricevuti.Tutti i messaggi applicativi: screenshot, solver, click, scroll, busy/free.

Comportamento atteso nello script

  • Captcha Interceptor: se la connessione non è csConnectedValidated al momento dell'intercept, sendScreenToResolver ritorna false e registra "connessione al socketserver non attiva, non posso mandare gli screenshots" nel log. Lo script rimane in pausa fino a timeout (180s) — predisporre un blocco IF/PAUSE manuale come fallback.
  • Solver Bridge: se la connessione non è csConnectedValidated al momento della solverRequest, viene consegnata immediatamente al JS una risposta con success=false e error="WSS_NOT_CONNECTED". Lo script JS può intercettare l'errore e mostrare un fallback (PAUSE manuale o tentativo di riconnessione).
⚠ Recupero post-disconnessione: JuliuS non riconnette automaticamente in caso di drop. Se la sessione cade durante uno script in esecuzione, l'interceptor segnala l'errore ma non interviene. Per script lunghi è buona pratica iniziare con un IF/PAUSE che verifichi la connessione (es. una variabile globale popolata via API ?operation=connectionstatus se esposta, o un test di richiesta solver dummy).

CAPTURE_SCREENSHOT

output CEF
//#*#CAPTURE_SCREENSHOTScreenshot del pannello browser CEF
Cattura lo stato visuale corrente del browser CEF e salva il file JPG nella cartella Settings.Data.screenShotPath. Il nome file è costruito automaticamente come ExcelLogLineNameBase_N.jpg. Il percorso viene aggiunto alla screenShotList del risultato operazione, recuperabile via API.
//#*#CAPTURE_SCREENSHOT //#*#SNIPPET=BEGIN $('#btnAvanti').click(); //#*#SNIPPET=END

OS_SCREENSHOT

output OS
//#*#OS_SCREENSHOT[=nomeFile]Screenshot dell'intero desktop Windows
Cattura l'intero desktop Windows (non solo il browser CEF) e salva il file JPG. Utile per catturare dialog nativi, popup di sistema e qualsiasi finestra fuori dal browser. Il percorso generato viene aggiunto alla screenShotList del risultato operazione.
VarianteDescrizione
//#*#OS_SCREENSHOTNome file automatico: ExcelLogLineNameBase_OS_N.jpg
//#*#OS_SCREENSHOT=nomeFileNome file personalizzato: nomeFile.jpg
//#*#OS_SCREENSHOT //#*#OS_SCREENSHOT=popup_conferma
Differenza con CAPTURE_SCREENSHOT: CAPTURE_SCREENSHOT cattura solo il pannello CEF. OS_SCREENSHOT cattura l'intero desktop Windows incluse finestre native, taskbar e popup di sistema.

Funzione di risultato

javascript

Per restituire un risultato recuperabile via API, definire una funzione JS in un blocco SNIPPET e richiamarla con EVALJS. Per convenzione si chiama preventivo(), ma è possibile usare qualsiasi nome.

//#*#SNIPPET=BEGIN function preventivo() { var el = document.querySelector('#tabs-Proposta0-label font'); return el ? el.innerHTML : 'non trovato'; } //#*#SNIPPET=END //#*#WAIT=2 //#*#EVALJS=preventivo(); // Recupera via API: GET /?operation=getresult // GET /?getresult=preventivo

OS_MOUSE_MOVE

OS — mouse
//#*#OS_MOUSE_MOVE=<X>,<Y>Sposta il cursore a coordinate assolute di schermo
Sposta il cursore del mouse alle coordinate assolute di schermo usando le API del sistema operativo. Agisce sull'intero desktop, non solo sul browser CEF. Utile per interagire con dialog nativi Windows, popup di sistema o altre applicazioni.
//#*#OS_MOUSE_MOVE=640,480 //#*#WAIT=1

OS_MOUSE_CLICK

OS — mouse
//#*#OS_MOUSE_CLICKClick sinistro nella posizione corrente del cursore SO
Simula un click sinistro del mouse nella posizione corrente del cursore di sistema. Usare dopo OS_MOUSE_MOVE. Agisce a livello SO e può interagire con qualsiasi finestra visibile sul desktop.
//#*#OS_MOUSE_MOVE=500,350 //#*#OS_MOUSE_CLICK

OS_MOUSE_RCLICK

OS — mouse
//#*#OS_MOUSE_RCLICKClick destro nella posizione corrente del cursore SO
Simula un click destro del mouse nella posizione corrente del cursore di sistema, per aprire menu contestuali nativi del SO o di applicazioni Windows.
//#*#OS_MOUSE_MOVE=500,350 //#*#OS_MOUSE_RCLICK //#*#WAIT=1

OS_MOUSE_MOVE_CLICK

OS — mouse
//#*#OS_MOUSE_MOVE_CLICK=<X>,<Y>Move + click sinistro SO in un'unica istruzione
Combina OS_MOUSE_MOVE e OS_MOUSE_CLICK in una sola direttiva usando coordinate assolute di schermo. Le coordinate dipendono dalla posizione della finestra JuliuS sul desktop: se la finestra viene spostata, le coordinate devono essere ricalcolate. Per click indipendenti dalla posizione della finestra, preferire OS_BROWSER_CLICK.
//#*#OS_MOUSE_MOVE_CLICK=640,400 //#*#WAIT=1

OS_MOUSE_DOUBLE_CLICK

OS — mouse
//#*#OS_MOUSE_DOUBLE_CLICK=<X>,<Y>Doppio click a coordinate assolute di schermo
Sposta il cursore a X,Y ed esegue due click sinistri rapidi (con 80ms di pausa tra i due) per simulare un doppio click nativo del sistema operativo. Usa coordinate assolute di schermo.
//#*#OS_MOUSE_DOUBLE_CLICK=300,200 //#*#WAIT=1

OS_BROWSER_CLICK

OS — mouse
//#*#OS_BROWSER_CLICK=<X>,<Y>Click SO a coordinate relative al pannello browser
Esegue un click sinistro a livello sistema operativo usando coordinate relative al pannello browser CEF — la stessa origine di MOUSE_CLICK e degli snapshot. Internamente, JuliuS converte le coordinate browser in coordinate assolute di schermo tramite ClickOnBrowserRelative, quindi il click funziona correttamente indipendentemente da dove è posizionata la finestra JuliuS sul desktop e dal fattore di scala DPI. È la scelta più robusta quando si vuole un click OS su un elemento del browser.

Quando usarlo rispetto alle alternative

DirettivaTipo clickCoordinateQuando usarla
MOUSE_CLICKCEF internoRelative browserCaso generale: elementi DOM standard
OS_MOUSE_MOVE_CLICKSOAssolute schermoClick su finestre esterne al browser
OS_BROWSER_CLICKSORelative browserElementi che non rispondono al CEF, scrollbar, dropdown nativi, pointer-events:none
//#*#OS_BROWSER_CLICK=374,494 //#*#WAIT=1

OS_BROWSER_INFO

OS — debug
//#*#OS_BROWSER_INFOScrive nel log le coordinate della finestra e del browser
Scrive nel log di JuliuS le informazioni sulla posizione e le dimensioni della finestra principale e del pannello browser CEF, incluse le coordinate assolute di schermo e il fattore di scala DPI. Non produce output nello script né interagisce con il browser. È uno strumento di sviluppo e debug: utile per calibrare le coordinate assolute da usare nei comandi OS_MOUSE_MOVE e OS_MOUSE_MOVE_CLICK.
//#*#OS_BROWSER_INFO // Esempio output nel log: // [OS_BROWSER_INFO] {"mainForm":{"left":100,"top":50,"width":1200,"height":800}, // "browser":{"left":0,"top":60,"width":1024,"height":768, // "screenLeft":100,"screenTop":110},"scaleFactor":1.25}

OS_KEY_PRESS

OS — tastiera
//#*#OS_KEY_PRESS=<keycode>Preme un tasto tramite keycode numerico VK
Invia la pressione di un tasto alla finestra attiva del sistema operativo tramite il suo keycode Virtual Key. A differenza di WRITETO che scrive nel browser CEF, questo comando agisce a livello SO e funziona su qualsiasi finestra attiva: dialog nativi Windows, form non web, applicazioni desktop.
KeycodeTastoKeycodeTasto
13ENTER27ESC
9TAB32SPAZIO
8BACKSPACE46DELETE
37← LEFT38↑ UP
39→ RIGHT40↓ DOWN
112–123F1–F1265–90A–Z
//#*#OS_KEY_PRESS=13 // ENTER //#*#OS_KEY_PRESS=27 // ESC

OS_KEY

OS — tastiera
//#*#OS_KEY=<nome_tasto>Preme un tasto per nome (alternativa leggibile a OS_KEY_PRESS)
Alternativa più leggibile a OS_KEY_PRESS: accetta il nome del tasto invece del keycode numerico. Agisce a livello sistema operativo sulla finestra attiva. I nomi non riconosciuti vengono loggati e ignorati senza interrompere lo script.
ValoreTastoValoreTasto
enterENTEResc / escapeESC
tabTABbackspaceBACKSPACE
spaceSPAZIOarrow_up
arrow_downarrow_left
arrow_right
//#*#OS_KEY=enter //#*#OS_KEY=arrow_down //#*#OS_KEY=tab

OS_KEY_COMBO

OS — tastiera
//#*#OS_KEY_COMBO=<modifier>+<key>Combinazione di tasti SO (Ctrl/Shift/Alt + tasto)
Simula una combinazione di tasti a livello SO tramite keybd_event. La sintassi è modificatore+tasto in minuscolo. Disponibile solo su Windows: su altre piattaforme il comando viene loggato come non implementato e ignorato senza interrompere lo script.
CampoValori accettati
modifierctrl, shift, alt
keyLettera singola (az), oppure: f4f12, tab, enter, esc, del, home, end, pageup, pagedown
//#*#OS_KEY_COMBO=ctrl+c // Copia //#*#OS_KEY_COMBO=ctrl+v // Incolla //#*#OS_KEY_COMBO=ctrl+a // Seleziona tutto //#*#OS_KEY_COMBO=alt+f4 // Chiudi finestra //#*#OS_KEY_COMBO=shift+tab // Tab inverso

Best practice

Gestione banner cookie

//#*#SNIPPET=BEGIN&NOFRAME (function() { var btn = document.getElementById('onetrust-accept-btn-handler'); if (btn) { btn.click(); return; } var fallback = Array.from(document.querySelectorAll('button')) .find(function(b) { return b.textContent.trim().toLowerCase().includes('accetta'); }); if (fallback) { fallback.click(); return; } })(); //#*#SNIPPET=END //#*#WAIT=3

Re-inject variabili dopo navigazione

//#*#SNIPPET=BEGIN $('#btnAvanti').click(); //#*#SNIPPET=END //#*#WAIT=8 //#*#GLOBALV=INJECT // ← obbligatorio dopo ogni navigazione

Compilazione dropdown con jQuery

//#*#SNIPPET=BEGIN $('#lblctl00_ddlCampo').val(variabile).keydown(); //#*#SNIPPET=END //#*#WAIT=2 //#*#SNIPPET=BEGIN $('#ulctl00_ddlCampo li a').each(function() { if ($(this).text() == variabile) { $(this).mouseenter().click(); } }); //#*#SNIPPET=END

Esecuzione condizionale con IF

Usare IF per gestire variazioni di pagina o errori senza interrompere il flusso principale:

// Clicca il pulsante solo se è presente e visibile //#*#GLOBALV=INJECT //#*#IF=(function(){ var b=document.querySelector('#btnCalcola'); return b && b.offsetParent!==null; })() //#*#SNIPPET=BEGIN document.querySelector('#btnCalcola').click(); //#*#SNIPPET=END //#*#WAIT=5 //#*#ELSE //#*#OS_BROWSER_CLICK=915,684 // fallback: click OS sulle coordinate //#*#WAIT=3 //#*#ENDIF

Click OS con coordinate relative al browser

Preferire OS_BROWSER_CLICK a OS_MOUSE_MOVE_CLICK per non dover ricalcolare le coordinate assolute se la finestra viene spostata:

// ✗ Fragile: dipende dalla posizione assoluta della finestra //#*#OS_MOUSE_MOVE_CLICK=1015,734 // ✓ Robusto: coordinate relative al browser, indipendenti dalla posizione finestra //#*#OS_BROWSER_CLICK=374,494

Script completo — template con IF

// TEMPLATE: form + condizione + estrazione risultato //#*#GLOBALV=BEGIN var nome="Mario"; var data_nasc="01/01/1985"; var guida="Libera"; //#*#GLOBALV=END //#*#SCREEN_SIZE=1024,768 //#*#TARGET_FRAME=MAIN //#*#URL_GOTO=https://www.esempio.it/form //#*#WAIT=5 //#*#GLOBALV=INJECT // Accetta cookie (solo se presente) //#*#IF=document.getElementById('onetrust-accept-btn-handler') !== null //#*#SNIPPET=BEGIN&NOFRAME document.getElementById('onetrust-accept-btn-handler').click(); //#*#SNIPPET=END //#*#WAIT=2 //#*#ENDIF // Compila nome //#*#WRITETO="ELEMENTID=inputNome"&"TEXT={{{NOME}}}{{TAB}}"&"CHAR_INT_MS=150" //#*#WAIT=1 // Tipo guida condizionale //#*#IF=guida === "Libera" //#*#OS_BROWSER_CLICK=200,400 //#*#ELSE //#*#OS_BROWSER_CLICK=260,400 //#*#ENDIF //#*#WAIT=1 //#*#CAPTURE_SCREENSHOT // Submit //#*#SNIPPET=BEGIN document.getElementById('btnSubmit').click(); //#*#SNIPPET=END //#*#WAIT=10 //#*#GLOBALV=INJECT // Gestione errore post-submit //#*#IF=document.querySelector('.msg-errore') !== null //#*#OS_SCREENSHOT=errore_submit //#*#PAUSE //#*#ENDIF // Estrazione risultato //#*#SNIPPET=BEGIN function preventivo() { return document.querySelector('.risultato-finale')?.innerText || ''; } //#*#SNIPPET=END //#*#WAIT=2 //#*#EVALJS=preventivo(); //#*#CAPTURE_SCREENSHOT