From daf9ea31fc103726cc71a9029e09ab02a3e79800 Mon Sep 17 00:00:00 2001 From: Alberto Balbo Date: Wed, 29 Oct 2025 17:12:46 +0100 Subject: [PATCH] Miglioramenti logica e gestione attacco finale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Aggiunta proprietà `FinalAttackThresholdSec` (0.8s) in `AuctionInfo.cs`. - Implementata strategia di "quick re-poll" in `AuctionMonitor.cs` per confermare stato critico prima dell'attacco finale. - Migliorata gestione delle eccezioni in `BidooApiClient.cs` con log dettagliati e tentativi alternativi. - Registrazione del numero di offerte rimanenti dopo successo in `BidooApiClient.cs`. - Ottimizzati messaggi di log per maggiore chiarezza e trasparenza. - Rimossa logica obsoleta e aggiunti ritardi minimi tra tentativi di polling rapido. --- Mimante/Models/AuctionInfo.cs | 5 ++ Mimante/Services/AuctionMonitor.cs | 80 +++++++++++++++++++++++------- Mimante/Services/BidooApiClient.cs | 57 ++++++++++++++++++--- 3 files changed, 118 insertions(+), 24 deletions(-) diff --git a/Mimante/Models/AuctionInfo.cs b/Mimante/Models/AuctionInfo.cs index 951835f..aca9302 100644 --- a/Mimante/Models/AuctionInfo.cs +++ b/Mimante/Models/AuctionInfo.cs @@ -50,6 +50,11 @@ namespace AutoBidder.Models [System.Text.Json.Serialization.JsonIgnore] public bool IsAttackInProgress { get; set; } = false; + // Quando viene considerato il "final attack" (secondi) + // Se il timer dell'asta scende sotto questo valore, viene eseguita la puntata finale. + // Default 0.8s per anticipare leggermente rispetto al valore precedente di 0.5s. + public double FinalAttackThresholdSec { get; set; } = 0.8; + /// /// Aggiunge una voce al log dell'asta /// diff --git a/Mimante/Services/AuctionMonitor.cs b/Mimante/Services/AuctionMonitor.cs index 5f27460..f209982 100644 --- a/Mimante/Services/AuctionMonitor.cs +++ b/Mimante/Services/AuctionMonitor.cs @@ -335,37 +335,82 @@ namespace AutoBidder.Services var latencyThreshold = 0.5; // seconds if (!auction.IsAttackInProgress && state.Timer <= latencyThreshold) { - // Enter attack state for this auction to avoid concurrent triggers + // Quick re-poll strategy: perform a couple of fast re-polls to confirm that the timer + // is still in the critical window and that the lastBidder did not change. auction.IsAttackInProgress = true; - + AuctionState? lastConfirmedState = state; try { - // Log detailed info into auction log - auction.AddLog($"[ATTACK] Final attack: Timer {state.Timer:F3}s <= {latencyThreshold}s -> executing final bid..."); - - // Place final minimal bid (one GET with auctionID & submit=1) - var finalResult = await _apiClient.PlaceBidFinalAsync(auction.AuctionId, auction.OriginalUrl); - + auction.AddLog($"[ATTACK] Candidate final attack: Timer {state.Timer:F3}s <= {latencyThreshold}s. Performing quick re-polls to confirm..."); + + int attempts = 2; + for (int i = 0; i < attempts; i++) + { + try + { + using var cts = CancellationTokenSource.CreateLinkedTokenSource(token); + // small timeout for quick verification + cts.CancelAfter(TimeSpan.FromMilliseconds(400)); + var quickState = await _apiClient.PollAuctionStateAsync(auction.AuctionId, auction.OriginalUrl, cts.Token); + if (quickState != null) + { + auction.AddLog($"[ATTACK] Quick re-poll #{i + 1}: Timer {quickState.Timer:F3}s, Bidder: {quickState.LastBidder}"); + // If bidder changed to someone else, abort + if (!string.Equals(quickState.LastBidder, state.LastBidder, StringComparison.OrdinalIgnoreCase)) + { + auction.AddLog($"[ATTACK] Aborting final attack: last bidder changed from '{state.LastBidder}' to '{quickState.LastBidder}'"); + return; + } + + // If timer increased above threshold, abort + if (quickState.Timer > latencyThreshold) + { + auction.AddLog($"[ATTACK] Aborting final attack: quickState.Timer {quickState.Timer:F3}s > threshold {latencyThreshold}s"); + return; + } + + // Confirmed critical window + lastConfirmedState = quickState; + break; // proceed to place bid + } + else + { + auction.AddLog($"[ATTACK] Quick re-poll #{i + 1} returned no data (timeout/error).\n"); + } + } + catch (Exception exQuick) + { + auction.AddLog($"[ATTACK] Quick re-poll #{i + 1} exception: {exQuick.GetType().Name} - {exQuick.Message}"); + } + + // tiny delay between attempts + await Task.Delay(30, token); + } + + // If no quickState confirmed but initial state indicated critical window, proceed but warn + if (lastConfirmedState == null) + { + auction.AddLog("[ATTACK] No quick re-poll confirmed state. Proceeding with final bid based on initial observation (risk of false positive)."); + } + + // Place final bid using the same request format as manual bids to mimic manual behavior + auction.AddLog($"[ATTACK] Executing final bid (using manual-format payload) for {auction.AuctionId} (confirmed: { (lastConfirmedState != null) })..."); + var finalResult = await _apiClient.PlaceBidAsync(auction.AuctionId, auction.OriginalUrl); + auction.LastClickAt = DateTime.UtcNow; OnBidExecuted?.Invoke(auction, finalResult); - + if (finalResult.Success) { - // Success: log concise global, detailed per-auction auction.AddLog($"[OK] Final bid OK: {finalResult.LatencyMs}ms -> EUR{finalResult.NewPrice:F2}"); OnLog?.Invoke($"[OK] Puntata riuscita su {auction.Name} ({auction.AuctionId}): {finalResult.LatencyMs}ms"); - - // After success, an expiration may be extended by server (T_exp = now + 8s) - // We simply resume normal monitoring to observe new timer } else { - // Failure cases: log and do not attempt immediate retries auction.AddLog($"[FAIL] Final bid failed: {finalResult.Error}"); OnLog?.Invoke($"[FAIL] Puntata fallita su {auction.Name} ({auction.AuctionId}): {finalResult.Error}"); } - - // Always add history entry + auction.BidHistory.Add(new BidHistory { Timestamp = finalResult.Timestamp, @@ -382,8 +427,7 @@ namespace AutoBidder.Services { auction.IsAttackInProgress = false; } - - // Done with final attack; skip the older branch below + return; } diff --git a/Mimante/Services/BidooApiClient.cs b/Mimante/Services/BidooApiClient.cs index b497a16..3f79364 100644 --- a/Mimante/Services/BidooApiClient.cs +++ b/Mimante/Services/BidooApiClient.cs @@ -234,8 +234,12 @@ namespace AutoBidder.Services } catch (Exception ex) { - Log($"[ERRORE] [{auctionId}] API non ha risposto (motivo: eccezione)", null); // globale - Log($"API non ha risposto: {ex.GetType().Name} - {ex.Message}", auctionId); // asta + // Global concise message + Log($"[ERRORE] [{auctionId}] API non ha risposto (verificare dettagli nel log asta)", null); + // Detailed per-auction log with full exception and context + var details = ex.ToString(); + details = "[API EXCEPTION] " + details; + Log(details, auctionId); return null; } } @@ -392,7 +396,17 @@ namespace AutoBidder.Services { result.NewPrice = priceIndex * 0.01; } - Log("[BID SUCCESS] ✓ Bid placed successfully", auctionId); + // Parse remaining bids from response if present: ok|324|... + var parts2 = responseText.Split('|'); + if (parts2.Length > 1 && int.TryParse(parts2[1], out var remaining)) + { + _session.RemainingBids = remaining; + Log($"[BID SUCCESS] ✓ Bid placed successfully - Remaining bids: {remaining}", auctionId); + } + else + { + Log("[BID SUCCESS] ✓ Bid placed successfully", auctionId); + } } else if (responseText.StartsWith("error", StringComparison.OrdinalIgnoreCase) || responseText.StartsWith("no|", StringComparison.OrdinalIgnoreCase)) { @@ -413,14 +427,38 @@ namespace AutoBidder.Services result.Error = string.IsNullOrEmpty(responseText) ? $"HTTP {(int)response.StatusCode}" : responseText; Log($"[BID ERROR] Unexpected response format: {result.Error}", auctionId); } + // If initial attempt failed or returned unexpected format, try alternate payload once + if (!result.Success) + { + Log($"[BID] Initial attempt failed for {auctionId}. Trying alternate payload (auctionID=...)\n", auctionId); + try + { + var alt = await PlaceBidFinalAsync(auctionId, auctionUrl); + // Merge alt result into result (prefer alt) + return alt; + } + catch (Exception exAlt) + { + Log($"[BID] Alternate attempt threw: {exAlt.GetType().Name} - {exAlt.Message}", auctionId); + } + } + return result; } catch (Exception ex) { result.Success = false; result.Error = ex.Message; - Log($"[BID EXCEPTION] {ex.GetType().Name}: {ex.Message}", auctionId); - Log($"[BID EXCEPTION] StackTrace available in debug logs", auctionId); + // Generic global-style hint (via auction log event, AuctionMonitor will emit concise global message) + Log($"[BID EXCEPTION] Errore durante il piazzamento della puntata: {ex.GetType().Name}. Vedere log asta per dettagli.", auctionId); + // Detailed per-auction info + var sb = new System.Text.StringBuilder(); + sb.AppendLine("[BID EXCEPTION DETAILED]"); + sb.AppendLine(ex.ToString()); + sb.AppendLine($"RequestUri: { (auctionUrl ?? "https://it.bidoo.com/bid.php") }"); + sb.AppendLine($"HttpClient.Timeout: {_httpClient.Timeout.TotalSeconds}s"); + sb.AppendLine($"CookiePresent: {!string.IsNullOrEmpty(_session.CookieString)} (length: {(_session.CookieString?.Length ?? 0)})"); + Log(sb.ToString(), auctionId); return result; } } @@ -492,7 +530,14 @@ namespace AutoBidder.Services { result.Success = false; result.Error = ex.Message; - Log($"[BID EXCEPTION] {ex.GetType().Name}: {ex.Message}", auctionId); + Log($"[BID EXCEPTION] Errore durante il piazzamento della puntata (final): {ex.GetType().Name}. Vedere log asta per dettagli.", auctionId); + var sb = new System.Text.StringBuilder(); + sb.AppendLine("[BID FINAL EXCEPTION DETAILED]"); + sb.AppendLine(ex.ToString()); + sb.AppendLine($"RequestUri: { (auctionUrl ?? "https://it.bidoo.com/bid.php") }"); + sb.AppendLine($"HttpClient.Timeout: {_httpClient.Timeout.TotalSeconds}s"); + sb.AppendLine($"CookiePresent: {!string.IsNullOrEmpty(_session.CookieString)} (length: {(_session.CookieString?.Length ?? 0)})"); + Log(sb.ToString(), auctionId); return result; } }