Alby96andClaude Fable 5.1 7993ca44a0 Database SQLite al posto dei dossier, gestore del rischio, decisioni in shadow, rete neurale, bandit e simulatore
Le aste non si salvano più in un file JSON per asta più tre archivi da tenere allineati:
un solo database SQLite (winsqlite3.dll di Windows via P/Invoke, nessun pacchetto) con
prodotti, aste, puntate, interrogazioni coalescenti, reset con profondità del ciclo,
nostre puntate, decisioni del motore, misure di rete, puntatori, sessioni e contabilità.
Scrittura in coda su un thread dedicato, letture in WAL. Per scelta dell'utente si parte
da zero: il database si riempie man mano che si osservano le aste; i vecchi dossier
restano leggibili per un'importazione futura ma non si scrivono più. Storico, schede,
apprendimento, rigiocata ed esportazioni leggono tutti da lì.

Il documento di progetto (Modifiche.txt) tradotto in C# senza librerie:
- RiskManager: kill-switch (file KILL_SWITCH, anche dalla barra), HALT persistente per
  stop-loss e drawdown, tetto del giorno, aste in gioco insieme, contabilità in euro.
- Theory: valore atteso con fee, spedizione, valore reale per prodotto (nuova colonna
  nella scheda Prodotti) e copertura «Compralo Ora»; null-model; sopravvivenza empirica e
  Kaplan-Meier dai reset; arrivi di Poisson.
- Shadow per asta: in Osserva il cecchino arriva allo stesso istante, registra cosa
  avrebbe fatto e non punta; alla chiusura ogni decisione riceve l'esito, e ShadowReport
  confronta le policy sugli stessi istanti con intervallo di confidenza sul ROI.
- NeuralNet (MLP, Adam) come sfidante del logistico, scelto dal Brier prequenziale;
  ThompsonBandit come seconda policy che impara dagli esiti delle decisioni;
  PennyAuctionEnv con avversari tarati sul database e QLearningAgent conservativo;
  SimulationLab per il confronto fra policy; esportazione CSV delle decisioni con
  reason_detail. Tutto nella scheda Apprendimento.

Ml/LEGGIMI.md riscritto con le avvertenze su termini d'uso, quadro legale e realtà
economica, lo schema del database e lo stato delle milestone.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 11:00:49 +02:00

AutoBidder — Bot desktop per aste Bidoo

App desktop Windows nativa (WPF, .NET 10) per monitorare e puntare automaticamente sulle aste di Bidoo. Reimplementazione desktop, leggera e veloce, del bot web/Docker "Mimante": stessa logica di puntata e stesse strategie, ma senza server, Docker, database o login — un unico .exe che si avvia con un doppio click.

⚠️ Uso personale/educativo. Rispetta i Termini di Servizio di Bidoo e usa un account tuo.


Caratteristiche

  • Monitor aste — barra con contatori (in elenco / attive / osserva / ferme / vinte / perse) e stato del conto e del motore: puntate, credito, aste vinte da confermare, richieste inviate e pallino di stato con il ritardo medio di rete (verde sotto i 150 ms, giallo fino a 400, rosso oltre — o giallo finché l'orologio non è agganciato). Questi ultimi sono numeri nudi: passandoci sopra il suggerimento dice cosa sono. Griglia ordinabile con badge di stato — In testa, Attiva, Osserva, Ferma, Sospesa, Vinta, Persa — prezzo, timer e ping colorati per urgenza.
  • Pannello dettagli a schedePuntate automatiche (anticipo, prezzo min/max, puntate massime, verifica asta aperta, contatori del motore su quell'asta), Prodotto (valore, spedizione, costo totale se vinci, verdetto di convenienza), Puntate, Puntatori (con quota di ciascuno) e Log dell'asta.
  • Tre modi per astaFerma (nessuna richiesta), Osserva (segue prezzo, scadenza e avversari ma non punta mai: utile per studiare un'asta prima di entrare), Attiva (segue e punta secondo le regole). Raggiungibili direttamente l'uno dall'altro.
  • Motore di precisione — un motore indipendente per ogni asta, con due anelli paralleli: polling a cadenza adattiva e "cecchino" che dorme fino all'istante di fuoco. Le strategie decidono se puntare, non quando. Vedi Come funziona il motore.
  • Strategie avanzate — controllo convenienza (valore prodotto vs "Compra Subito"), anti-bot, rilevamento competizione/heat, soft-retreat, profiling avversari e duelli, bankroll/budget, puntata probabilistica. Tutte configurabili.
  • Esplora — due modi. Catalogo nativo: barra laterale delle categorie e schede prodotto con immagine, prezzo, timer, valore e ultimo puntatore, con ricerca per nome, filtro nascondi aste manuali e aggiornamento prezzi facoltativo. Il listato si sfoglia a pagine come sul sito, quindi si va ben oltre le cinquanta aste della prima schermata. Browser Bidoo integrato (WebView2) per navigare e fare login — il cookie viene estratto automaticamente.
  • Prodotti seguiti — la stellina su una scheda segue quel prodotto: da lì in poi le aste nuove dello stesso articolo entrano nel monitor da sole. Vedi Prodotti seguiti.
  • Storico — le aste concluse in due letture: per prodotto (aste, vinte, % vittorie, chiusura media, min/max, prezzo massimo consigliato) e asta per asta. Salvato su file JSON, nessun database.
  • Registri e dossier — registro applicativo e del riscatto su file di testo, e un dossier per ogni asta con ogni interrogazione, ogni puntata di ogni utente con l'ora al millisecondo, ping e anticipo effettivo. È il materiale su cui si affinano le strategie: Bidoo non espone nulla di tutto questo a posteriori. Vedi Registri e dossier.
  • Esporta — una scheda per mettere in un file unico le aste scelte con i filtri (periodo, prodotto, esito, solo quelle seguite dall'inizio alla fine…), pronte da dare in pasto a un'analisi. Vedi Esporta.
  • Taratura dell'anticipo — l'applicazione misura l'anticipo effettivo di ogni puntata (quanto mancava davvero alla scadenza) e, con abbastanza dati, dice se c'è margine per stringere. Il consiglio non viene mai applicato da solo.
  • Puntate gratis automatiche — l'applicazione raccoglie i collegamenti promozionali pubblicati (?promocode=…&sign=…), scarta quelli già presi e apre i nuovi con la tua sessione, uno alla volta; e riscuote le ricompense in attesa sul conto. Indirizzi, selettori e parametri stanno in un file di configurazione, non nel codice: se un sito cambia, si corregge il file. Vedi Riscatto puntate.
  • Notifiche di Windows — avviso quando vinci un'asta, così te ne accorgi anche a finestra chiusa.
  • Esportazione dello storico — CSV per il foglio di calcolo, JSON con serie prezzi e puntate per singolo utente per le analisi.
  • Impostazioni — sessione, cartelle dei dati, parametri di default, motore di precisione, aste programmate, notifiche e strategie.
  • Tema chiaro/scuro — palette Material 3 a densità compatta, con interruttore in Impostazioni ▸ Aspetto. Il cambio è immediato (nessun riavvio) e viene ricordato.

Requisiti

  • Windows 10/11 x64.
  • WebView2 Runtime — già preinstallato su Windows 11 (e sulla maggior parte dei Windows 10 aggiornati). Nient'altro da installare: il runtime .NET è incluso nell'exe.

Avvio rapido (utente finale)

  1. Scarica/copia AutoBidder.exe (versione pubblicata, self-contained).
  2. Doppio click. Nessuna installazione.
  3. Autenticati (vedi sotto).
  4. In Monitor aggiungi le aste (pulsante , dal catalogo in Esplora o dal browser), imposta l'anticipo puntata e scegli il modo:
    • Osserva per seguirla senza rischiare nulla e capire come si comporta;
    • Attiva quando vuoi che punti davvero.

Si accede solo dalla scheda Browser: apri Bidoo lì dentro, fai il login, e il cookie viene rilevato e importato da solo. Vale per tutta l'applicazione.

Non c'è più un campo per incollare il cookie a mano: due modi di accedere significavano due sessioni possibili e diverse — quella digitata e quella del browser — con la domanda "quale sta usando adesso?" senza una risposta visibile.

In Impostazioni ▸ Sessione Bidoo trovi lo stato corrente (utente, puntate residue, credito), il pulsante per aprire il browser e "Verifica connessione", che ricontrolla la sessione salvata contro il sito.

Il cookie viene salvato in %AppData%\AutoBidder\session.dat protetto con DPAPI: Windows lo lega al tuo account, quindi nessun altro utente della macchina può leggerlo e l'applicazione non custodisce alcuna chiave. "Disconnetti" cancella la sessione.

Le sessioni salvate dalle versioni precedenti (cifrate con una chiave contenuta nell'eseguibile) vengono lette una volta sola e riscritte subito con DPAPI.


Build da sorgente (sviluppatore)

Prerequisito: .NET SDK 10 (dotnet --version ≥ 10).

# Compila ed esegui in debug
dotnet run --project AutoBidder.csproj

# Oppure apri AutoBidder.sln in Visual Studio 2022+ e premi F5

Verifica dopo ogni modifica

dotnet msbuild build/Release.proj -t:Verifica

Compila la soluzione e lancia i test, restituendo un codice di uscita diverso da zero se qualcosa non va — utilizzabile anche in un hook.

I test coprono la logica pura e deterministica, cioè quella che può rompersi senza dare segnali: ServerClock (compreso l'invariante che l'errore cada sempre verso l'anticipo, mai verso il ritardo), il parsing di data.php (formato posizionale e non documentato, dove una risposta malformata produrrebbe solo uno stato sbagliato), le pagine del listato (dove confondere "fine dell'elenco" con "risposta incomprensibile" fermerebbe la discesa alla prima pagina), ProductKeyHelper (che decide come si raggruppano le statistiche) e la taratura dell'anticipo (che non deve mai proporre un valore sotto il margine di rete).

I test scrivono in una cartella temporanea, mai nei tuoi dati reali.

Rigiocata sui dossier (backtest)

powershell -ExecutionPolicy Bypass -File .\build/Release.proj
powershell -ExecutionPolicy Bypass -File .\build/Release.proj -Max 200   # giro veloce

Rigioca le aste già concluse a partire dai loro dossier e conta cosa avrebbe fatto il motore: quanti cicli sarebbero arrivati fino all'anticipo (cioè quante puntate si sarebbero spese) e quante volte una strategia avrebbe rifiutato la puntata. Serve a tarare l'anticipo e come controllo di regressione: se una strategia comincia a bloccare puntate che prima passavano, qui si vede come un numero invece che come aste perse una alla volta.

Sui 2.095 dossier raccolti finora (1.007.850 cicli di timer):

Anticipo Cicli raggiunti Puntate per asta (mediana) p90
1000 ms 5.999 (0,6%) 1 6
2000 ms 59.375 (5,9%) 15 63
3000 ms 205.758 (20,4%) 46 220
4000 ms 800.891 (79,5%) 97 734

Il grosso degli avversari punta con circa tre secondi ancora sul cronometro; sotto i due secondi la concorrenza si dirada. Ogni secondo di anticipo in più moltiplica per dieci le puntate spese, quindi l'anticipo va tenuto stretto — ma non sotto i 600 ms, perché il ping tocca 444 ms nel p99,9 e una puntata tardiva costa l'asta intera.

La rigiocata misura i costi, non l'esito. Una nostra puntata rimette in gioco l'asta, e come avrebbero reagito gli avversari non è registrato da nessuna parte: dichiarare delle vittorie sarebbe inventarle.

Creare l'eseguibile unico self-contained

# Genera bin\publish\win-x64\AutoBidder.exe (un unico file, ~65 MB, nessuna installazione)
dotnet msbuild build/Release.proj -t:Pubblica

Lo script usa pubblicazione self-contained, PublishSingleFile, win-x64, senza trimming (WPF). Il runtime .NET è incluso; il file .pdb accanto all'exe è solo per il debug e non serve per l'esecuzione.

Installatore e rilascio su Gitea

# Solo il pacchetto: bin\installer\AutoBidder-<versione>-setup.exe
dotnet msbuild build/Release.proj -t:Pacchetto

# Tutto: verifica, pubblica, installatore, tag, release su Gitea con i file allegati
dotnet msbuild build/Release.proj -t:Rilascia

Da VS Code gli stessi passi sono attività pronte (Terminale ▸ Esegui attività…): verifica, backtest, crea installatore, rilascia su Gitea.

L'installatore è generato con Inno Setup 6 (winget install -e --id JRSoftware.InnoSetup) a partire da build/AutoBidder.iss. Installa per utente — nessuna richiesta di amministratore — crea i collegamenti, registra la voce in App installate e offre l'avvio automatico all'accesso. I dati in %LocalAppData%\AutoBidder non vengono mai toccati, né dall'aggiornamento né dalla disinstallazione: sono aste, statistiche e sessione, cioè mesi di raccolta.

La versione ha una sola origine, <Version> in AutoBidder.csproj; lo script la legge da lì e si ferma se l'eseguibile compilato ne dichiara un'altra.

Per Gitea servono quattro valori — copia build/gitea.example.json in build/gitea.json e riempilo, oppure imposta GITEA_URL, GITEA_OWNER, GITEA_REPO, GITEA_TOKEN. Il file build/gitea.json è escluso dal controllo di versione perché contiene un token. Nella release vengono caricati sia l'installatore sia l'eseguibile nudo: chi non vuole installare nulla continua a poter scaricare il solo .exe.


Dove vengono salvati i dati

In %AppData%\AutoBidder\:

File Contenuto
settings.json Impostazioni e strategie
session.dat Cookie di sessione Bidoo, protetto con DPAPI (legato al tuo account Windows)
site-config.json Riferimenti del riscatto puntate: indirizzi, intestazioni, parametri e chiavi di risposta. Vedi Riscatto puntate

E nelle cartelle dati, configurabili in Impostazioni ▸ Cartelle dei Dati:

File Contenuto
Dati/auctions.json Aste attualmente nel monitor
Dati/watched-products.json Prodotti seguiti e aste già proposte
Dati/products.json Scheda di ogni prodotto: opzioni scelte e statistiche accumulate
Dati/claimed-promos.json Collegamenti promozionali già aperti (evita di riaprire gli stessi)
Dati/Registri/app-AAAA-MM-GG.log Registro applicativo, scritto mentre l'app lavora
Dati/Registri/puntate-AAAA-MM-GG.log Registro del riscatto puntate
Dati/Registri/Aste/*.jsonl Dossier di ogni asta: vedi Registri e dossier
Statistiche/completed-auctions.json Riepilogo delle aste concluse (alimenta lo Storico)
Statistiche/aste-AAAA-MM.jsonl Scheda dettagliata di ogni asta: serie prezzi, puntate per utente, durata
Statistiche/bid-lead-stats.json Misure dell'anticipo effettivo delle puntate
Statistiche/Esportazioni/ File generati dalla scheda Esporta

I tre percorsi (dati, statistiche, registri) sono configurabili e i campi mostrano sempre quello in uso: svuotarne uno lo riporta al predefinito. La cartella dei registri ha un campo suo perché è quella che cresce di più — con i poll grezzi accesi un'asta lunga vale qualche megabyte.


Registri e dossier

Tutto si scrive mentre l'applicazione lavora, non alla chiusura: quello che vedi a video è già sul disco, e un blocco improvviso non porta via il registro con sé. La scrittura passa da una coda svuotata da un thread di fondo, quindi non tocca mai i percorsi caldi del motore: l'anello di polling accoda una riga e prosegue.

Il dossier di ogni asta (Dati/Registri/Aste/) è il pezzo che conta per migliorare le strategie. Un file per asta, in JSON Lines: intestazione, un evento per riga, riepilogo. Ogni riga è JSON valido per conto suo, quindi il file si legge a pezzi e un'interruzione perde al massimo l'ultima riga.

{"type":"header","auctionId":"85559716","name":"Cuffie Sony WH-1000XM5",
 "product":{"buyNowPrice":299,"shippingCost":6.90,"bidCostEuro":0.20},
 "config":{"bidBeforeDeadlineMs":800,"maxPrice":0,"pollCriticalMs":220}}

{"t":0.07,"type":"poll","at":"19:30:14.512","price":1.24,"timer":14.6,
 "status":"Running","lastBidder":"marco_82","pingMs":73,"expiryUnix":1785000000}

{"t":0.08,"type":"bid","at":"19:30:14.520","user":"marco_82","price":1.24,
 "bidAt":"2026-07-28T19:30:14+02:00","bidType":"Auto"}

{"t":0.09,"type":"my_bid","at":"19:30:15.203","price":1.25,"plannedLeadMs":800,
 "actualLeadMs":762.4,"leadErrorMs":-37.6,"pingMs":68,"result":"ok","remainingBids":255}

{"type":"summary","outcome":"Persa","finalPrice":4.87,
 "coverage":{"observedFromStart":true,"observedToEnd":true,"complete":true},
 "participation":{"myBids":9,"totalObservedBids":387,"distinctBidders":11,
                  "topBidderShare":34.1,"bidsByUser":{"marco_82":132}},
 "value":{"buyNowPrice":299,"totalCostIfWon":13.57,"finalPriceRatio":0.0163},
 "network":{"avgPingMs":71.4,"minPingMs":54,"medianPingMs":69,"p95PingMs":118,"polls":8412},
 "priceSeries":[{"t":0,"price":0.01},{"t":12.4,"price":1.24}]}

Tre cose da sapere:

  • coverage.complete distingue le aste seguite dall'inizio alla fine da quelle prese a metà corsa. Sono le uniche su cui i confronti reggono: su un'asta raccolta a metà non si sa quante puntate erano già state spese, quindi né la velocità del prezzo né le quote dei puntatori sono paragonabili alle altre. Il filtro dell'esportazione le seleziona da sole.
  • plannedLeadMs / actualLeadMs sono la coppia con cui si tara l'anticipo. Da soli non dicono nulla: è la differenza sistematica fra i due, su molte puntate, a dire se conviene stringere o allargare.
  • I poll grezzi (una riga per interrogazione, ~4 al secondo nella finestra critica) si possono spegnere in Impostazioni ▸ Registri su file. Il ping continua a essere misurato lo stesso: si perde la risoluzione al millisecondo, non le statistiche di rete.

I registri scaduti si cancellano da soli (predefinito: 90 giorni, 0 = per sempre).


Esporta

La scheda Esporta mette in un unico file JSON le aste che rispondono ai filtri — periodo, prodotto, prezzo finale, esito, solo quelle seguite dall'inizio alla fine, solo quelle su cui hai puntato, solo quelle col dossier — completo degli eventi registrati. Il conteggio si aggiorna mentre imposti i filtri, così non scopri a file scritto che non selezionavano nulla.

Il file porta con sé una legenda dei campi e i filtri con cui è stato prodotto: un'analisi si rilegge mesi dopo, quando non si ha più davanti né il codice né la memoria di com'era impostata l'applicazione quel giorno.


Come funziona il motore

Piazzare una puntata poco prima della scadenza richiede di sapere quando scade davvero e di arrivarci puntuali. Entrambe le cose sono meno ovvie di quanto sembri.

1. Quando scade (Engine/ServerClock.cs). data.php dichiara i timestamp in secondi interi: un singolo campione vale ±1 s, che è enorme rispetto a un anticipo di 200 ms. Ma ogni risposta dice anche "il mio secondo corrente è S", ed è stata generata prima di arrivare qui: ogni campione fornisce quindi un limite superiore per l'origine dell'orologio remoto, e il minimo su molti campioni converge al valore vero (filtro alla NTP, finestra di 256 campioni). L'errore residuo è per costruzione verso l'anticipo: si punta un filo prima, mai dopo.

In Impostazioni ▸ Motore di Precisione la diagnostica mostra la "finestra campioni": un valore intorno a 1000 ms è normale — è l'ampiezza della quantizzazione al secondo, non l'errore della stima. Molto oltre, invece, indica una rete instabile.

2. Arrivarci puntuali (Engine/PrecisionWait.cs). Task.Delay si appoggia al timer di sistema, che di default scatta ogni 15,6 ms. Qui si dorme finché mancano più di 22 ms, poi si copre l'ultimo tratto in attesa attiva (Thread.Yield(), e SpinWait negli ultimi 2 ms). Costo: qualche decina di millisecondi di CPU per puntata. Guadagno: errore sotto il millisecondo. In più il processo richiede la granularità timer a 1 ms (timeBeginPeriod), come fanno browser e riproduttori multimediali.

3. Un motore per asta (Engine/AuctionRunner.cs). Ogni asta ha due anelli indipendenti: il polling, che aggiorna prezzo e scadenza a cadenza adattiva, e il cecchino, che dorme fino a "scadenza meno anticipo" e spara lì. Tenerli separati è la differenza sostanziale rispetto a un ticker unico condiviso: una risposta lenta del server su un'asta non sposta di un millisecondo la puntata sulle altre, e il numero di aste seguite non degrada la precisione. Una sola puntata per ciclo è garantita ancorandosi alla scadenza del ciclo stesso.

4. Cadenza adattiva. Un'asta a otto minuti dalla scadenza non ha bisogno di quattro chiamate al secondo; una a tre secondi sì.

Tempo alla scadenza Cadenza
oltre 60 s 2000 ms
1060 s 900 ms
sotto 10 s 400 ms
finestra critica (< 4 s) 220 ms

La finestra critica si attiva solo in stato Attiva: in Osserva non c'è puntata da azzeccare, quindi si risparmiano chiamate.

5. Trasporto (Net/BidooHttpClient.cs). Un solo pool di connessioni tenute calde (HTTP/2 con fallback 1.1, 64 connessioni), header costanti impostati una volta, e un limitatore a token bucket. Le puntate viaggiano su corsia preferenziale: bid.php scavalca limitatore e coda: è l'unica richiesta il cui ritardo si paga in aste perse.


Riscatto puntate

La scheda Puntate raccoglie puntate gratis da due fonti indipendenti, percorse entrambe a ogni giro (predefinito: ogni 30 minuti). Un guasto sull'una non ferma l'altra.

1. Collegamenti promozionali pubblicati. È il meccanismo principale. Bidoo regala puntate con collegamenti già firmati della forma it.bidoo.com/index.php?promocode=…&sign=…, che i siti di raccolta ripubblicano. L'applicazione legge la pagina sorgente (predefinito puntateaste.it), prende i <a class="aste_links">, tiene solo gli indirizzi che puntano davvero al dominio e al percorso del riscatto e portano tutti i parametri richiesti, scarta quelli già presi e apre i nuovi con la tua sessione, uno alla volta con una pausa di 1,5 s.

Tre scelte che contano:

  • la pagina sorgente è di terzi e viene letta senza il cookie di Bidoo — non le serve, e mandarglielo significherebbe consegnarle la tua sessione;
  • i collegamenti presi restano in claimed-promos.json: senza quella memoria ogni giro riaprirebbe gli stessi indirizzi, perché sulla pagina restano pubblicati per giorni. Un codice fallito si ritenta al massimo tre volte, poi si lascia perdere;
  • l'indirizzo si apre esattamente com'è: porta una firma, e aggiungerci parametri la invaliderebbe.

2. Ricompense in attesa sul tuo account, dalla pagina dei premi di Bidoo.

Resta poi il campo per un collegamento singolo ricevuto per email o per messaggio, che la pagina di raccolta non ha: incollalo intero e premi Riscatta. Un codice da solo di norma non basta — il sign non si può ricostruire.

Tutti i riferimenti stanno in %LocalAppData%\AutoBidder\site-config.json, non nel codice: se il sito cambia un indirizzo o il nome di un campo, si corregge il file — il giro successivo usa già i valori nuovi, senza ricompilare e senza riavviare. Il pulsante Apri i riferimenti nella scheda lo apre con l'editor predefinito; cancellandolo si torna ai valori di fabbrica.

{
  "SiteSettings":  { "BaseUrl": "https://it.bidoo.com", "TimeoutSeconds": 8 },

  // ── Raccolta dei collegamenti pubblicati: il meccanismo principale ──
  "PromoHarvest": {
    "Enabled":              true,
    "SourceUrl":            "https://www.puntateaste.it",  // letta SENZA il cookie di Bidoo
    "LinkClass":            "aste_links",                  // <a class="aste_links" href="…">
    "TargetDomain":         "it.bidoo.com",
    "TargetPath":           "/index.php",
    "RequiredQueryParams":  ["promocode", "sign"],         // senza "sign" non è un riscatto
    "CodeQueryParam":       "promocode",                   // identità del codice, per i doppioni
    "DelayBetweenClaimsMs": 1500,
    "MaxLinksPerRound":     15,
    "MaxAttemptsPerCode":   3,
    "SuccessMarkers":       ["puntate gratis", "hai ricevuto", "complimenti"],
    "ExpiredMarkers":       ["già utilizzat", "non valido", "scadut"]
  },

  "Endpoints": {
    "Rewards":          "/my_chests.php",   // pagina dei premi: da qui si leggono premi e token
    "Login":            "/login.php",       // serve a riconoscere una sessione scaduta
    "ClaimDailyReward": "/open_chest.php",  // usato solo se la pagina non offre il collegamento
    "ClaimPromoLink":   "/index.php",       // riscatto promozionale (?promocode=…&sign=…)
    "Vouchers":         "/my_vouchers.php",
    "BuyBids":          "/buy_bids.php"
  },

  // Applicate alle chiamate di riscatto. Cookie e Referer non stanno qui: il primo è la
  // sessione vera, il secondo è la pagina dei premi.
  "DefaultHeaders": {
    "Accept": "application/json, text/javascript, */*; q=0.01",
    "X-Requested-With": "XMLHttpRequest"
  },

  "ClaimParameters": {
    // QueryKey è il nome del parametro con GET e del campo con POST.
    "Daily":     { "HttpMethod": "GET", "QueryKey": "id",        "FormFields": { "action": "open_chest" },
                   "TokenField": "csrf_token", "CacheBusterKey": "" },
    // Niente token né anti-cache: questi indirizzi portano una firma, e modificarli la invalida.
    "PromoCode": { "HttpMethod": "GET", "QueryKey": "promocode", "FormFields": {},
                   "TokenField": "", "CacheBusterKey": "" }
  },

  // Come si giudica la risposta. Le parole di rifiuto valgono solo per le risposte brevi.
  "ResponseValidation": {
    "SuccessKey": "success", "SuccessValue": "true",
    "BidsCountKey": "bids",  "MessageKey": "message",
    "FailureMarkers": ["già riscatt", "non disponibile"],
    "BidWords": ["puntate", "bids", "crediti"]
  },

  // Parole, non espressioni regolari: la forma dell'espressione la mette il codice.
  "PageRecognition": {
    "ClaimLinkKeywords":    ["open_chest", "riscuoti", "redeem"],
    "ClaimScriptFunctions": ["openChest", "apriBaule"],
    "ClaimIdAttributes":    ["data-chest-id"],
    "CsrfFieldNames":       ["csrf_token", "_token"]
  }
}

Le puntate contate sono la differenza di saldo prima/dopo, non il messaggio del sito. Vale soprattutto per i collegamenti promozionali: Bidoo risponde con la stessa pagina sia quando accredita le puntate sia quando il codice era già stato usato, e i testi in SuccessMarkers/ExpiredMarkers sono l'unico appiglio per distinguerli.

Cosa è verificato. Il meccanismo dei collegamenti (a.aste_links su puntateaste.it → it.bidoo.com/index.php?promocode=…&sign=…) e gli indirizzi delle pagine (my_chests.php, my_vouchers.php, buy_bids.php) sono riferimenti reali. Restano ipotesi i testi di conferma e ClaimDailyReward con i suoi riconoscitori, scritti senza aver visto quelle pagine da loggato. Se nel registro della scheda compare "pagina delle ricompense non riconosciuta" o "la pagina è cambiata", serve una correzione del solo file.


Prodotti seguiti

Bidoo rimette all'asta lo stesso articolo di continuo. Invece di cercare ogni volta la nuova asta, in Esplora si preme la stellina sulla scheda: il prodotto diventa "seguito" e le aste future dello stesso articolo entrano nel monitor da sole.

Il riconoscimento usa l'id prodotto di Bidoo quando c'è (il nome può cambiare fra un'asta e l'altra, l'id no) e ripiega sul nome normalizzato quando manca.

Le aste già aggiunte — o rimosse a mano — vengono ricordate: un'asta che hai tolto dal monitor non ci torna alla scansione successiva.

Le aste entrano in stato Osserva. È il valore predefinito e la scelta ha una ragione precisa: un'asta che entra da sola e comincia subito a puntare spenderebbe puntate vere senza che tu l'abbia mai vista. In Osserva la segui, la valuti e decidi tu quando attivarla. Si può cambiare in Impostazioni ▸ Prodotti Seguiti, insieme alla frequenza di ricerca e al numero massimo di aste aggiunte insieme.

Puntate spese dai vincitori

Per ogni asta conclusa l'applicazione registra quante puntate ha speso chi l'ha vinta. È il numero che il sito mostra come Puntate utilizzate sulla pagina dell'asta chiusa, e arriva dal server insieme allo stato finale: in data.php, quando l'asta passa a OFF, i dati correnti portano in coda |pagate|gratis|….

Perché non si contano da soli: lo storico che il server restituisce si ferma alle ultime cinquanta puntate. Su un'asta chiusa a 15 € — cioè 1.500 puntate in tutto — contarle osservando darebbe un numero sbagliato senza dare alcun segnale. Il valore del server è invece esatto e disponibile anche mesi dopo.

Ogni conteggio passa da un controllo di coerenza prima di entrare nelle statistiche (Utilities/AuctionIntegrity): ogni puntata alza il prezzo di un centesimo, quindi il prezzo finale in centesimi è il totale delle puntate dell'asta. Il vincitore non può averne fatte di più, e non può averne fatte zero. Quello che non torna viene scartato e detto nel registro: una statistica sbagliata è peggio di una assente, perché entra nelle medie e sposta i limiti di prezzo senza che nessuno se ne accorga.

Le aste registrate prima che l'applicazione sapesse leggere questo dato non sono perse: Storico ▸ Recupera puntate dei vincitori le richiede al server una alla volta, in corsia di fondo, senza rubare banda alle aste in corso.

Valore e risparmio reale

Nella scheda Prodotti, accanto ai prezzi, ci sono Valore, Punt. vinc., Costo reale e Risparmio.

Il costo reale è prezzo tipico di aggiudicazione + puntate tipiche × costo puntata. È la cifra che conta: un buono da 50 € aggiudicato a 4 € con 200 puntate è costato 44 €, non 4. Il risparmio si calcola su quella, non sul prezzo — e diventa rosso quando è negativo, cioè quando vincere costa più che comprare. Se le puntate del vincitore non sono note per nessuna asta del prodotto, le due colonne restano vuote invece di mostrare un numero lusinghiero e falso.

Limiti consigliati per prodotto

La scheda Prodotti propone, per ogni articolo, un prezzo minimo e uno massimo ricavati da come sono andate le sue aste. Si applicano con il segno di spunta sulla riga (un prodotto) oppure con Usa consigliati su tutti dalla barra, che dice prima quante righe cambierebbe.

Il massimo nasce da due vincoli, e vince il piu' stretto:

  1. Dove il prodotto si vende davvero — il prezzo che copre l'85% delle chiusure passate (regolabile in Impostazioni ▸ Limiti Consigliati).
  2. Quanto si puo' spenderevalore × (1 + tolleranza) puntate massime × costo puntata spedizione. E' qui che entra il numero massimo di puntate: alzarlo abbassa il prezzo che ci si puo' permettere di raggiungere.

Il minimo e' il decimo percentile delle chiusure: sotto quel prezzo l'asta non si chiude quasi mai, quindi puntare li' e' quasi sempre speso a vuoto.

Perche' non basta la moda. Il prezzo a cui un articolo si aggiudica piu' spesso copre fra il 15% e il 44% delle sue aste, a seconda del prodotto: fermandosi li' si resterebbe fuori dalla maggior parte delle occasioni. La moda serve a dire dove sta la massa, e compare nella spiegazione; il tetto lo decide la copertura.

Il criterio precedente era massimo storico × 0,7. Non andava: le chiusure di Bidoo hanno una coda lunghissima — il massimo e' mediamente 8,3 volte la chiusura tipica — quindi quel numero inseguiva l'asta anomala. Su 250€ Idea Shopping diceva 142,66 €, dove il consiglio attuale dice 4,8218,13 € su una mediana di 6,86 €.

Passando il puntatore sui valori consigliati compare il ragionamento completo: campione, moda, mediana, copertura raggiunta e — quando e' il budget a comandare — il conto che l'ha prodotto. In arancione quando il tetto non arriva al prezzo tipico: con quel numero di puntate, quell'articolo non conviene.

Orizzonte temporale. Le aste che cominciano fra più di un'ora (predefinito) non entrano ancora: vengono riprese alla scansione successiva, quando si sono avvicinate. Senza questo limite la ricerca — che scende in fondo al listato apposta per trovare le aste programmate — riempirebbe il monitor di aste che non succederanno per mezza giornata, consumando il tetto delle aste aggiunte e nascondendo quelle vicine. Il valore si cambia in Impostazioni ▸ Prodotti Seguiti (0 = nessun limite), e ogni prodotto può averne uno suo nella colonna Entro min della scheda Prodotti: utile per gli articoli rari, che passano una volta al giorno e conviene prendere in anticipo. A mano puoi sempre aggiungere qualunque asta: il limite vale solo per l'aggiunta automatica.

Solo aste non ancora cominciate. Con questa opzione (spenta di predefinito) entrano nel monitor soltanto le aste su cui nessuno ha ancora puntato, così le si può seguire dal primo istante all'ultimo. Un'asta presa a metà si può comunque seguire, ma la sua storia è già scritta per buona parte: non si sa quanto è durata davvero, chi c'era prima, come è salito il prezzo — e i dossier, che sono il materiale su cui si tarano le strategie, raccontano solo la seconda metà. Il prezzo da pagare è che si perdono le occasioni già in corso.

Nota sul catalogo. Il listato si sfoglia come sul sito quando si scorre verso il basso: cinquanta aste per richiesta, in ordine di scadenza. Non c'è un numero di pagina — il client manda l'elenco di ciò che ha già e il server risponde con le successive — e la fine si riconosce da una pagina vuota. Le categorie piccole si esauriscono da sole, le grandi si fermano al tetto impostato in Impostazioni ▸ Catalogo.


Architettura

AutoBidder/
├─ App.xaml / MainWindow.xaml        # Guscio WPF, sidebar a tab (Aste/Cerca/Prodotti/Puntate/Storico/Esporta/Impostazioni)
├─ Controls/                         # UserControl: AuctionMonitorControl, SettingsControl, BrowserControl
├─ Dialogs/                          # Aggiungi asta, Sessione
├─ ViewModels/AuctionViewModel.cs    # Riga della griglia aste (binding)
├─ Core/                             # Code-behind MainWindow (partial): aste, UI, WebView, catalogo, log...
├─ Engine/                           # Motore di precisione
│   ├─ ServerClock.cs                # Aggancio all'orologio del server (filtro al minimo)
│   ├─ PrecisionWait.cs              # Attesa sotto il millisecondo + timer di sistema a 1 ms
│   └─ AuctionRunner.cs              # Un motore per asta: anello polling + anello cecchino
├─ Net/BidooHttpClient.cs            # Trasporto: pool caldo, HTTP/2, token bucket, corsia puntate
├─ Services/
│   ├─ BidooApiClient.cs             # Chiamate Bidoo (polling, puntata, info utente)
│   ├─ BidooCatalogClient.cs         # Catalogo pubblico: categorie + listato paginato (Esplora)
│   ├─ CatalogPageParser.cs          # Interpreta le pagine del listato (puro, sotto test)
│   ├─ AuctionMonitor.cs             # Coordina i motori per asta, strategie ed eventi
│   ├─ BidStrategyService.cs         # Strategie (se puntare)
│   ├─ SessionService/SessionManager # Sessione + cookie cifrato
│   └─ HtmlCacheService.cs           # Fetch HTML con cache e rate-limit
├─ Models/                           # AuctionInfo, AuctionState, CatalogAuction, CompletedAuctionRecord, ...
├─ Themes/                           # Design system dell'interfaccia
│   ├─ Tokens.Dark.xaml              # Token colore tema scuro
│   ├─ Tokens.Light.xaml             # Token colore tema chiaro (stesse chiavi)
│   └─ Controls.xaml                 # Stili controlli (compatti, DynamicResource)
└─ Utilities/
    ├─ ThemeManager.cs               # Switch tema a runtime + barra titolo
    ├─ SettingsManager.cs            # AppSettings (JSON, cache 2s)
    ├─ PersistenceManager.cs         # Salva/carica aste attive (JSON)
    ├─ CompletedAuctionsStore.cs     # Storico aste concluse (JSON) + aggregazioni
    ├─ ProductStatsStore.cs          # Scheda per prodotto: opzioni + statistiche accumulate
    ├─ FileLogWriter.cs              # Scrittura su file accodata, fuori dai percorsi caldi
    ├─ TextLogService.cs             # Registro applicativo e del riscatto, un file per giorno
    ├─ AuctionDossier.cs             # Dossier JSONL di ogni asta (eventi, poll, riepilogo)
    ├─ AuctionExporter.cs            # Esportazione filtrata in un file unico
    └─ ProductValueCalculator.cs     # Calcolo convenienza prodotto

Rispetto alla versione web "Mimante" sono stati rimossi: Blazor Server, Kestrel, ASP.NET Identity/login, PostgreSQL, SignalR e Docker. Le strategie sono le stesse; il motore di timing è stato invece riscritto (vedi Come funziona il motore).

Tema e stili

Nessun colore è cablato nella UI: la XAML referenzia solo token semantici via DynamicResource (es. {DynamicResource Brush.Surface}). Tokens.Dark.xaml e Tokens.Light.xaml espongono le stesse chiavi; ThemeManager sostituisce a runtime lo slot 0 dei MergedDictionaries di App.xaml, quindi l'intera interfaccia (barra del titolo inclusa, via DWM) cambia istantaneamente.

Per modificare la grafica:

  • cambiare i colori → edita solo i due file Tokens.*.xaml;
  • cambiare forma/densità dei controlliThemes/Controls.xaml (stili impliciti + RoundedButton, SmallRoundedButton, CardBorder, InfoBox, SectionHeader, …).

Convenzione: il colore ha significato. Neutro = azione secondaria; accento = azione primaria; verde/ambra/rosso = successo/attenzione/distruttivo; oro = asta vinta (per distinguerla da un semplice successo).

Le icone vengono da Segoe MDL2 Assets, font di sistema: monocromatiche, coerenti col tema e — soprattutto — senza file da distribuire, che sotto PublishSingleFile non verrebbero estratti.

S
Description
Programma che legge il sito di Bidoo per provare a piazzare offerte poco prima dello scadere del tempo utile
Readme
32 MiB
2026-09-08 14:50:30 +02:00
Languages
C# 69.6%
HTML 14.9%
CSS 14.6%
PowerShell 0.7%
Inno Setup 0.2%