#!/bin/zsh # Jeton Bearer QUB (Cognito id-token, valide 1 h) obtenu en pilotant Safari par # AppleScript — solution de repli (ou de secours) à scripts/qub-token.mjs, qui # passe par Scrapfly et échoue parfois sur l'anti-bot de connect.qub.ca. # # S'exécute SUR LE LAPTOP (Safari + cliclick). Imprime le jeton sur stdout, les # étapes sur stderr. Identifiants : QUB_EMAIL / QUB_PASSWORD dans l'environnement # ou dans ~/.config/vrai-prix/qub.env (chmod 600). # # scripts/qub-token-safari.sh # → jeton # scripts/ingest-via-safari.sh [--since=…] # jeton + ingestion sur le nœud set -u PAGE='https://www.journaldequebec.com/argent/immobilier/transactions-immobilieres' [[ -f ~/.config/vrai-prix/qub.env ]] && source ~/.config/vrai-prix/qub.env : "${QUB_EMAIL:?QUB_EMAIL manquant}" "${QUB_PASSWORD:?QUB_PASSWORD manquant}" command -v cliclick >/dev/null || { echo "cliclick requis (brew install cliclick)" >&2; exit 2; } log() { print -u2 -- "[qub-token-safari] $*"; } js() { local code=${1//\"/\\\"}; osascript -e "tell application \"Safari\" to do JavaScript \"$code\" in front document" 2>/dev/null; } url() { osascript -e 'tell application "Safari" to return URL of front document' 2>/dev/null; } # jeton via /api/checklogin (XHR synchrone, même origine) — vide si non connecté grab() { js '(function(){try{var x=new XMLHttpRequest();x.open("GET","/api/checklogin",false);x.send();var j=JSON.parse(x.responseText);return j.userToken||""}catch(e){return ""}})()' } osascript -e "tell application \"Safari\" activate make new document with properties {URL:\"$PAGE\"} end tell" >/dev/null sleep 6 tok=$(grab) if [[ -n "$tok" ]]; then log "session déjà connectée"; print -- "$tok"; exit 0; fi log "non connecté : ouverture de connect.qub.ca" js 'Array.from(document.querySelectorAll("a")).filter(e=>/Me connecter|Se connecter/i.test(e.innerText))[0].click()' >/dev/null sleep 7 # écran « Sélectionner un compte » → formulaire classique if js 'document.body.innerText.includes("Changer de compte")' | grep -q true; then js 'Array.from(document.querySelectorAll("a")).filter(e=>/Changer de compte/i.test(e.innerText))[0].click()' >/dev/null sleep 5 fi if ! js 'String(!!document.querySelector("input[type=email]"))' | grep -q true; then log "formulaire de connexion introuvable ($(url))"; exit 3 fi # remplissage (setter natif + événements React), puis fermeture du remplissage # automatique Touch ID qui s'ouvre au focus, puis VRAI clic souris sur le bouton # (un click() JavaScript n'est pas honoré par le formulaire connect.qub.ca) js "(function(){function set(el,v){Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value').set.call(el,v);el.dispatchEvent(new Event('input',{bubbles:true}));el.dispatchEvent(new Event('change',{bubbles:true}))}set(document.querySelector('input[type=email]'),'$QUB_EMAIL');set(document.querySelector('input[type=password]'),'$QUB_PASSWORD');return 'ok'})()" >/dev/null sleep 1 osascript -e 'tell application "System Events" to key code 53' # Échap : popup Touch ID sleep 1 pos=$(js '(function(){var b=document.querySelector("button[type=submit]");var r=b.getBoundingClientRect();return [Math.round(r.left+r.width/2),Math.round(r.top+r.height/2),window.outerHeight-window.innerHeight].join(",")})()') bounds=$(osascript -e 'tell application "Safari" to get bounds of front window') x0=${${(s:, :)bounds}[1]}; y0=${${(s:, :)bounds}[2]} bx=${${(s:,:)pos}[1]}; by=${${(s:,:)pos}[2]}; chrome=${${(s:,:)pos}[3]} osascript -e 'tell application "Safari" to activate'; sleep 0.5 cliclick c:$((x0 + bx)),$((y0 + chrome + by)) log "formulaire soumis, attente de l'OAuth…" for i in {1..10}; do sleep 3 if [[ "$(url)" == *journaldequebec.com* ]]; then tok=$(grab) if [[ -n "$tok" ]]; then log "jeton obtenu (essai $i)"; print -- "$tok"; exit 0; fi fi done log "échec : $(url)"; exit 4