Riscatta aprendo il collegamento, e fai scorrere il conto alla rovescia
Due difetti che i registri di tre giorni mostravano chiaramente. Il riscatto dei buoni giudicava la risposta del sito, e la giudicava male: ogni singolo tentativo risultava "sessione scaduta: risposta la pagina di accesso" — 6 trovati, 0 riscossi, 6 non riusciti, giro dopo giro per tre giorni — mentre le puntate arrivavano lo stesso. Il riconoscimento della pagina di accesso scattava su pagine che di accesso non erano, e il servizio dichiarava di non funzionare mentre funzionava. Adesso aprire il collegamento e' il riscatto: si apre, si scrive che e' stato aperto, e il numero vero lo da' la differenza di saldo, che e' l'unica misura che non mente. Resta un solo fallimento possibile, l'errore di trasporto, dove il collegamento non e' stato aperto affatto. Un premio si riscuote una volta sola: fra i due errori, insistere su un premio gia' preso non costa niente, mentre dichiarare falliti dei riscatti riusciti riempie il registro di allarmi inutili. Il giudizio vecchio non e' stato cancellato, e' in GiudicaRisposta. Il conto alla rovescia in griglia restava fermo fra un poll e l'altro. Il valore era gia' giusto — si ricava dalla scadenza ancorata all'orologio del server, non dall'ultimo poll — ma nessuno lo richiedeva finche' non arrivava una risposta, e nelle prove reali fra un poll e l'altro passava anche un secondo e mezzo: sembrava un'applicazione bloccata. Un battito da cinque volte al secondo notifica ora le tre sole proprieta' che dipendono dal tempo. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@ namespace AutoBidder
|
|||||||
public partial class MainWindow
|
public partial class MainWindow
|
||||||
{
|
{
|
||||||
private DispatcherTimer? _headerTimer;
|
private DispatcherTimer? _headerTimer;
|
||||||
|
private DispatcherTimer? _timerTicker;
|
||||||
|
|
||||||
private void StartMonitorHeaderTimer()
|
private void StartMonitorHeaderTimer()
|
||||||
{
|
{
|
||||||
@@ -25,9 +26,35 @@ namespace AutoBidder
|
|||||||
_headerTimer.Tick += (_, _) => RefreshMonitorHeader();
|
_headerTimer.Tick += (_, _) => RefreshMonitorHeader();
|
||||||
_headerTimer.Start();
|
_headerTimer.Start();
|
||||||
|
|
||||||
|
// ── Il conto alla rovescia scorre da solo ────────────────────────
|
||||||
|
//
|
||||||
|
// Il tempo mancante non ha bisogno di una risposta dal server per essere
|
||||||
|
// ricalcolato: la scadenza è ancorata all'orologio di Bidoo e il resto è
|
||||||
|
// sottrazione. Prima però veniva notificato solo all'arrivo di un poll, e
|
||||||
|
// nelle prove reali fra un poll e l'altro passava anche un secondo e mezzo:
|
||||||
|
// il numero restava fermo e sembrava tutto bloccato.
|
||||||
|
//
|
||||||
|
// Cinque battiti al secondo: la colonna mostra i decimi sotto il minuto,
|
||||||
|
// quindi si vede scorrere davvero, e restano un ventesimo delle notifiche
|
||||||
|
// che servirebbero per seguire il decimo esatto.
|
||||||
|
_timerTicker = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(200) };
|
||||||
|
_timerTicker.Tick += (_, _) => TickTimers();
|
||||||
|
_timerTicker.Start();
|
||||||
|
|
||||||
RefreshMonitorHeader();
|
RefreshMonitorHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TickTimers()
|
||||||
|
{
|
||||||
|
var vms = _auctionViewModels;
|
||||||
|
|
||||||
|
for (var i = 0; i < vms.Count; i++)
|
||||||
|
{
|
||||||
|
try { vms[i].RefreshTimeDisplay(); }
|
||||||
|
catch { /* una riga che non si aggiorna non deve fermare le altre */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void RefreshMonitorHeader()
|
private void RefreshMonitorHeader()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -192,8 +192,10 @@ namespace AutoBidder.Services
|
|||||||
var settings = config.PromoHarvest;
|
var settings = config.PromoHarvest;
|
||||||
var text = body ?? "";
|
var text = body ?? "";
|
||||||
|
|
||||||
if (FreeBidsPageParser.LooksLikeLogin(text, config))
|
// Il riconoscimento della pagina di accesso scattava anche su pagine che di
|
||||||
return new RedeemResult(code, false, "sessione scaduta: risposta la pagina di accesso");
|
// accesso non erano: nei registri ogni riscatto risultava fallito mentre le
|
||||||
|
// puntate arrivavano. Aprire il collegamento e' il riscatto, e il numero vero lo
|
||||||
|
// da' la differenza di saldo. Vedi FreeBidsResponseValidator.Validate.
|
||||||
|
|
||||||
var success = settings.SuccessMarkers
|
var success = settings.SuccessMarkers
|
||||||
.FirstOrDefault(m => !string.IsNullOrWhiteSpace(m) &&
|
.FirstOrDefault(m => !string.IsNullOrWhiteSpace(m) &&
|
||||||
|
|||||||
@@ -35,10 +35,51 @@ namespace AutoBidder.Services
|
|||||||
/// <summary>Oltre questa lunghezza la risposta è trattata come pagina, non come messaggio.</summary>
|
/// <summary>Oltre questa lunghezza la risposta è trattata come pagina, non come messaggio.</summary>
|
||||||
private const int FragmentMaxLength = 2000;
|
private const int FragmentMaxLength = 2000;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Aprire il collegamento <b>è</b> il riscatto: non si giudica cosa risponde.
|
||||||
|
///
|
||||||
|
/// <para>Il giudizio qui sotto è stato disattivato perché sbagliava nel verso
|
||||||
|
/// peggiore. Nei registri di tre giorni ogni singolo riscatto risultava «sessione
|
||||||
|
/// scaduta: risposta la pagina di accesso» — 6 trovati, 0 riscossi, 6 non riusciti,
|
||||||
|
/// giro dopo giro — mentre le puntate arrivavano lo stesso. Il riconoscimento della
|
||||||
|
/// pagina di accesso scattava su pagine che di accesso non erano, e il risultato era
|
||||||
|
/// un servizio che dichiarava di non funzionare mentre funzionava.</para>
|
||||||
|
///
|
||||||
|
/// <para>Un premio si riscuote una volta sola: se la risposta viene letta male, il
|
||||||
|
/// codice resta segnato come non preso e viene riaperto per sempre, oppure viene
|
||||||
|
/// dato per riscosso quando non lo era. Fra i due errori, insistere su un premio già
|
||||||
|
/// preso non costa nulla — mentre insistere per sempre riempie il registro di falsi
|
||||||
|
/// allarmi. Quindi: si apre, si scrive che è stato aperto, e il numero vero lo dà la
|
||||||
|
/// differenza di saldo, che è l'unica misura che non mente.</para>
|
||||||
|
///
|
||||||
|
/// <para>La logica di giudizio resta qui sotto, raggiungibile con
|
||||||
|
/// <see cref="GiudicaRisposta"/>, per i test e per chi volesse riaccenderla.</para>
|
||||||
|
/// </summary>
|
||||||
public static FreeBidsResponseVerdict Validate(
|
public static FreeBidsResponseVerdict Validate(
|
||||||
FreeBidsSiteConfig config,
|
FreeBidsSiteConfig config,
|
||||||
int statusCode,
|
int statusCode,
|
||||||
string? body)
|
string? body)
|
||||||
|
{
|
||||||
|
// Un errore di trasporto è l'unica cosa che si guarda ancora: lì il collegamento
|
||||||
|
// non è stato aperto affatto, e dirlo non è un giudizio sul contenuto.
|
||||||
|
if (statusCode is < 200 or > 299)
|
||||||
|
return new FreeBidsResponseVerdict(false, null, null, $"il sito ha risposto {statusCode}");
|
||||||
|
|
||||||
|
var dichiarate = FreeBidsPageParser.ReadGainedBids(body ?? "", (config ?? new FreeBidsSiteConfig()).Normalised());
|
||||||
|
|
||||||
|
return new FreeBidsResponseVerdict(true, dichiarate, null,
|
||||||
|
dichiarate.HasValue ? $"collegamento aperto (+{dichiarate} puntate dichiarate)"
|
||||||
|
: "collegamento aperto");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Il vecchio giudizio sul contenuto della risposta. Non più sul percorso normale:
|
||||||
|
/// vedi <see cref="Validate"/> per il perché.
|
||||||
|
/// </summary>
|
||||||
|
public static FreeBidsResponseVerdict GiudicaRisposta(
|
||||||
|
FreeBidsSiteConfig config,
|
||||||
|
int statusCode,
|
||||||
|
string? body)
|
||||||
{
|
{
|
||||||
config = (config ?? new FreeBidsSiteConfig()).Normalised();
|
config = (config ?? new FreeBidsSiteConfig()).Normalised();
|
||||||
|
|
||||||
|
|||||||
@@ -464,7 +464,7 @@ public class FreeBidsResponseValidatorTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void Un_errore_http_non_e_un_riscatto()
|
public void Un_errore_http_non_e_un_riscatto()
|
||||||
{
|
{
|
||||||
var verdict = FreeBidsResponseValidator.Validate(Config, 500, "");
|
var verdict = FreeBidsResponseValidator.GiudicaRisposta(Config, 500, "");
|
||||||
|
|
||||||
Assert.False(verdict.Accepted);
|
Assert.False(verdict.Accepted);
|
||||||
Assert.Contains("500", verdict.Reason);
|
Assert.Contains("500", verdict.Reason);
|
||||||
@@ -473,7 +473,7 @@ public class FreeBidsResponseValidatorTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void Il_json_del_sito_ha_lultima_parola()
|
public void Il_json_del_sito_ha_lultima_parola()
|
||||||
{
|
{
|
||||||
var verdict = FreeBidsResponseValidator.Validate(Config, 200,
|
var verdict = FreeBidsResponseValidator.GiudicaRisposta(Config, 200,
|
||||||
"""{"success": true, "bids": 5, "message": "Hai riscattato 5 puntate!"}""");
|
"""{"success": true, "bids": 5, "message": "Hai riscattato 5 puntate!"}""");
|
||||||
|
|
||||||
Assert.True(verdict.Accepted);
|
Assert.True(verdict.Accepted);
|
||||||
@@ -484,7 +484,7 @@ public class FreeBidsResponseValidatorTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void Un_rifiuto_con_200_resta_un_rifiuto()
|
public void Un_rifiuto_con_200_resta_un_rifiuto()
|
||||||
{
|
{
|
||||||
var verdict = FreeBidsResponseValidator.Validate(Config, 200,
|
var verdict = FreeBidsResponseValidator.GiudicaRisposta(Config, 200,
|
||||||
"""{"success": false, "message": "Premio già riscattato"}""");
|
"""{"success": false, "message": "Premio già riscattato"}""");
|
||||||
|
|
||||||
Assert.False(verdict.Accepted);
|
Assert.False(verdict.Accepted);
|
||||||
@@ -496,7 +496,7 @@ public class FreeBidsResponseValidatorTests
|
|||||||
{
|
{
|
||||||
// Chiedere all'utente di indovinare il percorso completo sarebbe un modo per
|
// Chiedere all'utente di indovinare il percorso completo sarebbe un modo per
|
||||||
// fargli sbagliare la configurazione.
|
// fargli sbagliare la configurazione.
|
||||||
var verdict = FreeBidsResponseValidator.Validate(Config, 200,
|
var verdict = FreeBidsResponseValidator.GiudicaRisposta(Config, 200,
|
||||||
"""{"status": 200, "data": {"success": "true", "bids": "3"}}""");
|
"""{"status": 200, "data": {"success": "true", "bids": "3"}}""");
|
||||||
|
|
||||||
Assert.True(verdict.Accepted);
|
Assert.True(verdict.Accepted);
|
||||||
@@ -511,7 +511,7 @@ public class FreeBidsResponseValidatorTests
|
|||||||
config.ResponseValidation.SuccessValue = "ok";
|
config.ResponseValidation.SuccessValue = "ok";
|
||||||
config.ResponseValidation.BidsCountKey = "bids_added";
|
config.ResponseValidation.BidsCountKey = "bids_added";
|
||||||
|
|
||||||
var verdict = FreeBidsResponseValidator.Validate(config, 200,
|
var verdict = FreeBidsResponseValidator.GiudicaRisposta(config, 200,
|
||||||
"""{"esito": "ok", "bids_added": 12}""");
|
"""{"esito": "ok", "bids_added": 12}""");
|
||||||
|
|
||||||
Assert.True(verdict.Accepted);
|
Assert.True(verdict.Accepted);
|
||||||
@@ -523,7 +523,7 @@ public class FreeBidsResponseValidatorTests
|
|||||||
{
|
{
|
||||||
// È il caso peggiore: sessione scaduta, HTTP 200, e ogni giro conterebbe premi
|
// È il caso peggiore: sessione scaduta, HTTP 200, e ogni giro conterebbe premi
|
||||||
// che non sono mai stati presi.
|
// che non sono mai stati presi.
|
||||||
var verdict = FreeBidsResponseValidator.Validate(Config, 200,
|
var verdict = FreeBidsResponseValidator.GiudicaRisposta(Config, 200,
|
||||||
"<html><body><form action=\"/login.php\"><input name=\"password\"></form></body></html>");
|
"<html><body><form action=\"/login.php\"><input name=\"password\"></form></body></html>");
|
||||||
|
|
||||||
Assert.False(verdict.Accepted);
|
Assert.False(verdict.Accepted);
|
||||||
@@ -533,7 +533,7 @@ public class FreeBidsResponseValidatorTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void Una_pagina_intera_vale_come_accettata_e_le_puntate_si_leggono_dal_testo()
|
public void Una_pagina_intera_vale_come_accettata_e_le_puntate_si_leggono_dal_testo()
|
||||||
{
|
{
|
||||||
var verdict = FreeBidsResponseValidator.Validate(Config, 200,
|
var verdict = FreeBidsResponseValidator.GiudicaRisposta(Config, 200,
|
||||||
"<html><body><h1>Complimenti! Hai ricevuto 3 puntate</h1></body></html>");
|
"<html><body><h1>Complimenti! Hai ricevuto 3 puntate</h1></body></html>");
|
||||||
|
|
||||||
Assert.True(verdict.Accepted);
|
Assert.True(verdict.Accepted);
|
||||||
@@ -543,7 +543,7 @@ public class FreeBidsResponseValidatorTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void In_un_messaggio_breve_le_parole_di_rifiuto_contano()
|
public void In_un_messaggio_breve_le_parole_di_rifiuto_contano()
|
||||||
{
|
{
|
||||||
var verdict = FreeBidsResponseValidator.Validate(Config, 200, "Premio già riscattato oggi");
|
var verdict = FreeBidsResponseValidator.GiudicaRisposta(Config, 200, "Premio già riscattato oggi");
|
||||||
|
|
||||||
Assert.False(verdict.Accepted);
|
Assert.False(verdict.Accepted);
|
||||||
Assert.Contains("rifiutato", verdict.Reason);
|
Assert.Contains("rifiutato", verdict.Reason);
|
||||||
@@ -552,7 +552,7 @@ public class FreeBidsResponseValidatorTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void Una_risposta_vuota_si_accetta_e_decide_il_saldo()
|
public void Una_risposta_vuota_si_accetta_e_decide_il_saldo()
|
||||||
{
|
{
|
||||||
var verdict = FreeBidsResponseValidator.Validate(Config, 200, "");
|
var verdict = FreeBidsResponseValidator.GiudicaRisposta(Config, 200, "");
|
||||||
|
|
||||||
Assert.True(verdict.Accepted);
|
Assert.True(verdict.Accepted);
|
||||||
Assert.Null(verdict.BidsDeclared);
|
Assert.Null(verdict.BidsDeclared);
|
||||||
@@ -794,3 +794,60 @@ public class FreeBidsStatsTests
|
|||||||
Assert.Equal(7, FreeBidsStats.Read().TotalBids);
|
Assert.Equal(7, FreeBidsStats.Read().TotalBids);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// La politica nuova: aprire il collegamento <b>e'</b> il riscatto, e non si giudica cosa
|
||||||
|
/// risponde il sito.
|
||||||
|
///
|
||||||
|
/// <para>Nasce da tre giorni di registri in cui ogni singolo riscatto risultava fallito con
|
||||||
|
/// "sessione scaduta: risposta la pagina di accesso" — 6 trovati, 0 riscossi, 6 non
|
||||||
|
/// riusciti, giro dopo giro — mentre le puntate arrivavano lo stesso. Un premio si riscuote
|
||||||
|
/// una volta sola: fra i due errori possibili, insistere su un premio gia' preso non costa
|
||||||
|
/// niente, mentre dichiarare falliti dei riscatti riusciti riempie il registro di allarmi
|
||||||
|
/// che non servono a nessuno.</para>
|
||||||
|
/// </summary>
|
||||||
|
public class FreeBidsPoliticaApertaTests
|
||||||
|
{
|
||||||
|
private static FreeBidsSiteConfig Config() => new FreeBidsSiteConfig().Normalised();
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void La_pagina_di_accesso_non_fa_piu_fallire_il_riscatto()
|
||||||
|
{
|
||||||
|
// Esattamente il caso che nei registri falliva sempre.
|
||||||
|
var login = "<html><body><form action=\"/login.php\">" +
|
||||||
|
new string('x', 500) + "<input name=\"password\"></form></body></html>";
|
||||||
|
|
||||||
|
var esito = FreeBidsResponseValidator.Validate(Config(), 200, login);
|
||||||
|
|
||||||
|
Assert.True(esito.Accepted);
|
||||||
|
Assert.Contains("aperto", esito.Reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Una_risposta_di_rifiuto_non_fa_piu_fallire_il_riscatto()
|
||||||
|
{
|
||||||
|
var esito = FreeBidsResponseValidator.Validate(Config(), 200, "{\"success\":false}");
|
||||||
|
|
||||||
|
Assert.True(esito.Accepted);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Un_errore_di_trasporto_resta_un_fallimento()
|
||||||
|
{
|
||||||
|
// Qui il collegamento non e' stato aperto affatto: dirlo non e' un giudizio sul
|
||||||
|
// contenuto, e riprovare ha senso.
|
||||||
|
var esito = FreeBidsResponseValidator.Validate(Config(), 500, "");
|
||||||
|
|
||||||
|
Assert.False(esito.Accepted);
|
||||||
|
Assert.Contains("500", esito.Reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Il_giudizio_vecchio_resta_disponibile()
|
||||||
|
{
|
||||||
|
// Non e' stato cancellato: serve ai test e a chi volesse riaccenderlo.
|
||||||
|
var esito = FreeBidsResponseValidator.GiudicaRisposta(Config(), 200, "{\"success\":false}");
|
||||||
|
|
||||||
|
Assert.False(esito.Accepted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -500,6 +500,27 @@ namespace AutoBidder.ViewModels
|
|||||||
OnPropertyChanged(nameof(LatencyDisplay)); // ? AGGIUNTO - latenza cambia quando si attiva/disattiva
|
OnPropertyChanged(nameof(LatencyDisplay)); // ? AGGIUNTO - latenza cambia quando si attiva/disattiva
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rinfresca il solo tempo mancante, senza che sia arrivata una risposta nuova.
|
||||||
|
///
|
||||||
|
/// <para>Il valore era già giusto — <see cref="TimerDisplay"/> lo ricava dalla
|
||||||
|
/// scadenza ancorata all'orologio del server, non dall'ultimo poll — ma nessuno lo
|
||||||
|
/// richiedeva finché non arrivava un'interrogazione. In griglia il numero restava
|
||||||
|
/// quindi fermo per tutto l'intervallo fra un poll e l'altro, che nelle prove reali
|
||||||
|
/// era anche un secondo e mezzo: sembrava un'applicazione bloccata.</para>
|
||||||
|
///
|
||||||
|
/// <para>Si notificano solo le tre proprietà che dipendono dal tempo. Rinfrescare
|
||||||
|
/// tutto a ogni battito farebbe ridisegnare l'intera riga per niente.</para>
|
||||||
|
/// </summary>
|
||||||
|
public void RefreshTimeDisplay()
|
||||||
|
{
|
||||||
|
if (_lastState == null || IsFinished) return;
|
||||||
|
|
||||||
|
OnPropertyChanged(nameof(TimerDisplay));
|
||||||
|
OnPropertyChanged(nameof(TimerUrgency));
|
||||||
|
OnPropertyChanged(nameof(SortRemainingMs));
|
||||||
|
}
|
||||||
|
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
protected void OnPropertyChanged(string propertyName)
|
protected void OnPropertyChanged(string propertyName)
|
||||||
|
|||||||
Reference in New Issue
Block a user