Miglioramenti logica e gestione attacco finale
- 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.
This commit is contained in:
@@ -50,6 +50,11 @@ namespace AutoBidder.Models
|
|||||||
[System.Text.Json.Serialization.JsonIgnore]
|
[System.Text.Json.Serialization.JsonIgnore]
|
||||||
public bool IsAttackInProgress { get; set; } = false;
|
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;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Aggiunge una voce al log dell'asta
|
/// Aggiunge una voce al log dell'asta
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -335,37 +335,82 @@ namespace AutoBidder.Services
|
|||||||
var latencyThreshold = 0.5; // seconds
|
var latencyThreshold = 0.5; // seconds
|
||||||
if (!auction.IsAttackInProgress && state.Timer <= latencyThreshold)
|
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;
|
auction.IsAttackInProgress = true;
|
||||||
|
AuctionState? lastConfirmedState = state;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Log detailed info into auction log
|
auction.AddLog($"[ATTACK] Candidate final attack: Timer {state.Timer:F3}s <= {latencyThreshold}s. Performing quick re-polls to confirm...");
|
||||||
auction.AddLog($"[ATTACK] Final attack: Timer {state.Timer:F3}s <= {latencyThreshold}s -> executing final bid...");
|
|
||||||
|
int attempts = 2;
|
||||||
// Place final minimal bid (one GET with auctionID & submit=1)
|
for (int i = 0; i < attempts; i++)
|
||||||
var finalResult = await _apiClient.PlaceBidFinalAsync(auction.AuctionId, auction.OriginalUrl);
|
{
|
||||||
|
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;
|
auction.LastClickAt = DateTime.UtcNow;
|
||||||
OnBidExecuted?.Invoke(auction, finalResult);
|
OnBidExecuted?.Invoke(auction, finalResult);
|
||||||
|
|
||||||
if (finalResult.Success)
|
if (finalResult.Success)
|
||||||
{
|
{
|
||||||
// Success: log concise global, detailed per-auction
|
|
||||||
auction.AddLog($"[OK] Final bid OK: {finalResult.LatencyMs}ms -> EUR{finalResult.NewPrice:F2}");
|
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");
|
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
|
else
|
||||||
{
|
{
|
||||||
// Failure cases: log and do not attempt immediate retries
|
|
||||||
auction.AddLog($"[FAIL] Final bid failed: {finalResult.Error}");
|
auction.AddLog($"[FAIL] Final bid failed: {finalResult.Error}");
|
||||||
OnLog?.Invoke($"[FAIL] Puntata fallita su {auction.Name} ({auction.AuctionId}): {finalResult.Error}");
|
OnLog?.Invoke($"[FAIL] Puntata fallita su {auction.Name} ({auction.AuctionId}): {finalResult.Error}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always add history entry
|
|
||||||
auction.BidHistory.Add(new BidHistory
|
auction.BidHistory.Add(new BidHistory
|
||||||
{
|
{
|
||||||
Timestamp = finalResult.Timestamp,
|
Timestamp = finalResult.Timestamp,
|
||||||
@@ -382,8 +427,7 @@ namespace AutoBidder.Services
|
|||||||
{
|
{
|
||||||
auction.IsAttackInProgress = false;
|
auction.IsAttackInProgress = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Done with final attack; skip the older branch below
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -234,8 +234,12 @@ namespace AutoBidder.Services
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log($"[ERRORE] [{auctionId}] API non ha risposto (motivo: eccezione)", null); // globale
|
// Global concise message
|
||||||
Log($"API non ha risposto: {ex.GetType().Name} - {ex.Message}", auctionId); // asta
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -392,7 +396,17 @@ namespace AutoBidder.Services
|
|||||||
{
|
{
|
||||||
result.NewPrice = priceIndex * 0.01;
|
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))
|
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;
|
result.Error = string.IsNullOrEmpty(responseText) ? $"HTTP {(int)response.StatusCode}" : responseText;
|
||||||
Log($"[BID ERROR] Unexpected response format: {result.Error}", auctionId);
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
result.Success = false;
|
result.Success = false;
|
||||||
result.Error = ex.Message;
|
result.Error = ex.Message;
|
||||||
Log($"[BID EXCEPTION] {ex.GetType().Name}: {ex.Message}", auctionId);
|
// Generic global-style hint (via auction log event, AuctionMonitor will emit concise global message)
|
||||||
Log($"[BID EXCEPTION] StackTrace available in debug logs", auctionId);
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -492,7 +530,14 @@ namespace AutoBidder.Services
|
|||||||
{
|
{
|
||||||
result.Success = false;
|
result.Success = false;
|
||||||
result.Error = ex.Message;
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user