- Aggiunto stile personalizzato al `DataGrid` in `MainWindow.xaml`. - Modificato il dizionario `_bidders` per tracciare ultima puntata. - Aggiunta colonna "Ultima puntata" nella griglia `BiddersGrid`. - Migliorata gestione degli errori negli script JavaScript. - Implementata logica per rilevare pause programmate nelle aste. - Migliorata gestione dei limiti di prezzo (minimo/massimo). - Ottimizzata gestione dei click e aggiornamento degli offerenti. - Rimossi metodi obsoleti e migliorata gestione delle eccezioni. - Aggiunti log dettagliati per stato e operazioni dell'automazione.
543 lines
32 KiB
C#
543 lines
32 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text.Json;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Media;
|
|
using System.Windows.Media.Imaging;
|
|
using Microsoft.Web.WebView2.Core;
|
|
using System.Text.RegularExpressions;
|
|
using System.Globalization;
|
|
|
|
namespace AutoBidder
|
|
{
|
|
public partial class MainWindow : Window
|
|
{
|
|
private CancellationTokenSource? _cts;
|
|
private Task? _automationTask;
|
|
private volatile bool _pauseBids = false;
|
|
private readonly Dictionary<string, (int Count, DateTime LastBid)> _bidders = new(StringComparer.OrdinalIgnoreCase);
|
|
private readonly List<string> _pendingLogs = new();
|
|
|
|
public MainWindow()
|
|
{
|
|
InitializeComponent();
|
|
|
|
// flush any pending logs that may have been recorded during XAML initialization
|
|
try
|
|
{
|
|
if (LogBox != null && _pendingLogs.Count > 0)
|
|
{
|
|
foreach (var e in _pendingLogs)
|
|
{
|
|
try { LogBox.AppendText(e); } catch { }
|
|
}
|
|
try { LogBox.ScrollToEnd(); } catch { }
|
|
_pendingLogs.Clear();
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
try
|
|
{
|
|
const int size = 64;
|
|
var dv = new DrawingVisual();
|
|
using (var dc = dv.RenderOpen())
|
|
{
|
|
dc.DrawRoundedRectangle(new SolidColorBrush(Color.FromRgb(16, 163, 74)), null, new Rect(0, 0, size, size), 8, 8);
|
|
var ft = new FormattedText("AB", System.Globalization.CultureInfo.InvariantCulture, FlowDirection.LeftToRight,
|
|
new Typeface("Segoe UI"), 28, Brushes.White, 1.0);
|
|
var pt = new Point((size - ft.Width) / 2, (size - ft.Height) / 2 - 2);
|
|
dc.DrawText(ft, pt);
|
|
}
|
|
var rtb = new RenderTargetBitmap(size, size, 96, 96, PixelFormats.Pbgra32);
|
|
rtb.Render(dv);
|
|
this.Icon = rtb;
|
|
}
|
|
catch { }
|
|
|
|
// UI initial state
|
|
StartButton.IsEnabled = true;
|
|
StartButton.Opacity = 1.0;
|
|
StopButton.IsEnabled = false;
|
|
StopButton.Opacity = 0.5;
|
|
BackButton.IsEnabled = false;
|
|
|
|
webView.NavigationCompleted += WebView_NavigationCompleted;
|
|
UpdatePauseButtonContent();
|
|
}
|
|
|
|
private void WebView_NavigationCompleted(object? sender, CoreWebView2NavigationCompletedEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
BackButton.IsEnabled = webView.CoreWebView2 != null && webView.CoreWebView2.CanGoBack;
|
|
try
|
|
{
|
|
var url = webView.CoreWebView2?.Source ?? webView.Source?.ToString();
|
|
if (!string.IsNullOrEmpty(url)) SiteLinkText.Text = url!;
|
|
}
|
|
catch { }
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
private void MaxClicksBox_TextChanged(object sender, TextChangedEventArgs e) => Log("Impostazione Max Clicks cambiata: " + MaxClicksBox.Text);
|
|
private void MaxResetsBox_TextChanged(object sender, TextChangedEventArgs e) => Log("Impostazione Max Resets cambiata: " + MaxResetsBox.Text);
|
|
private void MinPriceBox_TextChanged(object sender, TextChangedEventArgs e) => Log("Impostazione Min Price cambiata: " + MinPriceBox.Text);
|
|
private void MaxPriceBox_TextChanged(object sender, TextChangedEventArgs e) => Log("Impostazione Max Price cambiata: " + MaxPriceBox.Text);
|
|
|
|
private void ClearStatsButton_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
ClickCountText.Text = "0";
|
|
ResetCountText.Text = "0";
|
|
Log("Statistiche resettate dall'utente");
|
|
_bidders.Clear();
|
|
UpdateBiddersGrid();
|
|
}
|
|
|
|
private void PauseBidButton_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
_pauseBids = !_pauseBids;
|
|
UpdatePauseButtonContent();
|
|
Log(_pauseBids ? "Modalità pausa puntata attivata" : "Modalità pausa puntata disattivata");
|
|
}
|
|
|
|
private void UpdatePauseButtonContent()
|
|
{
|
|
try
|
|
{
|
|
Dispatcher.Invoke(() =>
|
|
{
|
|
var btn = FindName("PauseBidButton") as Button;
|
|
if (btn != null) btn.Content = _pauseBids ? "Riprendi" : "Pausa";
|
|
});
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
private void UpdateBiddersGrid()
|
|
{
|
|
try
|
|
{
|
|
Dispatcher.Invoke(() =>
|
|
{
|
|
var grid = FindName("BiddersGrid") as DataGrid;
|
|
if (grid != null)
|
|
{
|
|
var list = _bidders.Select(kvp => new { Name = kvp.Key, Count = kvp.Value.Count, LastBid = kvp.Value.LastBid.ToString("g") }).OrderByDescending(x => x.Count).ToList();
|
|
grid.ItemsSource = list;
|
|
}
|
|
});
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
private void SetCurrentPriceText(string text)
|
|
{
|
|
try
|
|
{
|
|
Dispatcher.Invoke(() =>
|
|
{
|
|
var tb = FindName("CurrentPriceText") as TextBlock;
|
|
if (tb != null) tb.Text = text;
|
|
});
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
private void Log(string message)
|
|
{
|
|
var entry = $"{DateTime.Now:HH:mm:ss} - {message}{Environment.NewLine}";
|
|
try
|
|
{
|
|
Dispatcher.Invoke(() =>
|
|
{
|
|
if (LogBox != null)
|
|
{
|
|
LogBox.AppendText(entry);
|
|
LogBox.ScrollToEnd();
|
|
}
|
|
else
|
|
{
|
|
// fallback: buffer if LogBox not ready
|
|
try { _pendingLogs.Add(entry); } catch { }
|
|
}
|
|
});
|
|
}
|
|
catch
|
|
{
|
|
try { _pendingLogs.Add(entry); } catch { }
|
|
}
|
|
}
|
|
|
|
private async void StartButton_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
StartButton.IsEnabled = false;
|
|
StopButton.IsEnabled = true;
|
|
StartButton.Opacity = 0.5;
|
|
StopButton.Opacity = 1.0;
|
|
|
|
Log("Inizializzazione web...");
|
|
try { if (webView.CoreWebView2 == null) await webView.EnsureCoreWebView2Async(); }
|
|
catch (Exception ex) { Log("Errore inizializzazione WebView2: " + ex.Message); StartButton.IsEnabled = true; StopButton.IsEnabled = false; return; }
|
|
|
|
Log("Avviato: WebView inizializzato. Avvio monitoraggio pagina.");
|
|
_cts = new CancellationTokenSource();
|
|
_automationTask = Task.Run(() => AutomationLoop(_cts.Token));
|
|
}
|
|
|
|
private void StopButton_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
StartButton.Opacity = 1.0; StopButton.Opacity = 0.5; StopAutomation("Arrestato dall'utente");
|
|
}
|
|
|
|
private void BackButton_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
try { if (webView.CoreWebView2 == null) _ = webView.EnsureCoreWebView2Async(); if (webView.CoreWebView2 != null && webView.CoreWebView2.CanGoBack) webView.CoreWebView2.GoBack(); }
|
|
catch (Exception ex) { Log("Errore navigazione indietro: " + ex.Message); }
|
|
}
|
|
|
|
private async void RefreshButton_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
try { if (webView.CoreWebView2 == null) await webView.EnsureCoreWebView2Async(); webView.CoreWebView2?.Reload(); Log("Pagina aggiornata"); }
|
|
catch (Exception ex) { Log("Errore aggiornamento pagina: " + ex.Message); }
|
|
}
|
|
|
|
private void StopAutomation(string reason)
|
|
{
|
|
try { _cts?.Cancel(); } catch { }
|
|
StartButton.IsEnabled = true; StopButton.IsEnabled = false; StartButton.Opacity = 1.0; StopButton.Opacity = 0.5;
|
|
Log("STOP: " + reason);
|
|
}
|
|
|
|
private async Task AutomationLoop(CancellationToken token)
|
|
{
|
|
int clickCount = 0;
|
|
int resetCount = 0;
|
|
string? previousTimer = null;
|
|
const int postClickDelayMs = 1200;
|
|
|
|
const string findScript = @"(function(){ try{ function isVisible(el){ if(!el) return false; var r=el.getBoundingClientRect(); var s=window.getComputedStyle(el); return r.width>0 && r.height>0 && s.visibility!=='hidden' && s.display!=='none'; } var priceText=''; try{ var p=document.querySelector('.auction-action-price strong, .auction-action-price'); if(p) priceText=(p.textContent||p.innerText||'').trim(); }catch(e){} var priceVal=null; try{ if(priceText){ var p=priceText.replace('€','').replace(/\./g,'').replace(',','.').match(/\d+(?:\.\d+)?/); if(p) priceVal=p[0]; } }catch(e){} var btn=document.querySelector('a.auction-btn-bid, a.bid-button, .auction-btn-bid'); if(btn && isVisible(btn)){ var txt=(btn.textContent||btn.innerText||'').trim(); if(/\bINIZIA\b/i.test(txt)) return JSON.stringify({status:'soon', debug:txt, price: priceVal}); } try{ var direct=document.querySelector('.text-countdown-progressbar'); if(direct && isVisible(direct)){ var t=(direct.textContent||'').trim(); var m=t.match(/\d+/); if(m) return JSON.stringify({status:'found', timer:m[0], price: priceVal, debug: direct.outerHTML}); } }catch(e){} if(!btn || !isVisible(btn)){ var candidates=Array.from(document.querySelectorAll('a, button, div, span')).filter(e=> e && (e.innerText||e.textContent) && /\bPUNTA\b/i.test((e.InnerText||e.textContent)) && isVisible(e)); if(candidates.length>0) btn=candidates[0]; } if(!btn) return JSON.stringify({status:'no-button', price: priceVal}); var nodeList=Array.from(document.querySelectorAll('div, span, p, strong, b, i, em, label, small, a, svg text')); var nums=nodeList.map(e=>{ try{return {el:e, text:(e.textContent||'').trim(), rect:e.getBoundingClientRect(), html:(e.outerHTML||'')}; }catch(err){ return null; }}).filter(x=> x && /\d+/.test(x.text)).map(x=>({el:x.el, text:x.text, rect:x.rect, html:x.html})); if(nums.length==0) return JSON.stringify({status:'no-timer', price: priceVal}); function distanceRect(a,b){ var ax=(a.left+a.right)/2, ay=(a.top+a.bottom)/2; var bx=(b.left+b.right)/2, by=(b.top+b.bottom)/2; return Math.hypot(ax-bx, ay-by); } var btnRect; try{ btnRect = btn.getBoundingClientRect(); }catch(e){ btnRect = {top:0,left:0,right:0,bottom:0,width:0,height:0}; } nums.sort(function(a,b){ var da = distanceRect(a.rect, btnRect); var db = distanceRect(b.rect, btnRect); var ya = btnRect.top - a.rect.bottom; var yb = btnRect.top - b.rect.bottom; var pref = (ya>=0?0:200) - (yb>=0?0:200); return (da - db) + pref; }); var best=nums[0]; var m=(best.text||'').match(/\d+/); if(!m) return JSON.stringify({status:'no-timer-extracted', debug: best.html, price: priceVal}); return JSON.stringify({status:'found', timer:m[0], price: priceVal, debug: best.html}); }catch(e){ return JSON.stringify({status:'error', error: (e && e.message?e.message: e)}); } })();";
|
|
|
|
const string clickScript = @"(function(){ try{ var btn=document.querySelector('a.auction-btn-bid, a.bid-button, .auction-btn-bid'); if(!btn){ var candidates=Array.from(document.querySelectorAll('a, button, div, span')).filter(e=> e && (e.innerText||e.textContent) && /\bPUNTA\b/i.test((e.InnerText||e.textContent))); if(candidates.length>0) btn=candidates[0]; } if(!btn) return 'no-button'; try{ btn.click(); return 'clicked'; }catch(e){ try{ var evt=document.createEvent('MouseEvents'); evt.initEvent('click', true, true); btn.dispatchEvent(evt); return 'dispatched'; }catch(ex){ return 'error:'+ (ex && ex.message?ex.message:ex); } } }catch(e){ return 'error:'+ (e && e.message?e.message:e); } })();";
|
|
|
|
const string getLastBidderScript = @"(function(){ try{ var el=document.querySelector('.auction-current-winner'); if(el){ var name=(el.textContent||el.innerText||'').trim(); if(name) return JSON.stringify({status:'found', bidder:name}); } return JSON.stringify({status:'not-found'}); }catch(e){ return JSON.stringify({status:'error', error:(e&&e.message?e.message:e)}); } })();";
|
|
|
|
const string closedCountdownScript = @"(function(){ try{ var s=document.querySelector('.auction-action-countdown'); if(!s) return null; var ps=Array.from(s.querySelectorAll('p')).map(p=> (p.textContent||p.innerText||'').trim()); return JSON.stringify(ps); }catch(e){ return null; } })();";
|
|
|
|
const string pricePollScript = @"(function(){ var p=document.querySelector('.auction-action-price strong, .auction-action-price'); if(!p) return null; var t=(p.textContent||p.innerText||'').trim(); var num=t.replace('€','').replace(/\./g,'').replace(',','.').match(/\d+(?:\.\d+)?/); return num?num[0]:null; })();";
|
|
|
|
while (!token.IsCancellationRequested)
|
|
{
|
|
string? result = null;
|
|
try
|
|
{
|
|
var op = Dispatcher.InvokeAsync(() => webView.ExecuteScriptAsync(findScript));
|
|
var innerTask = await op.Task.ConfigureAwait(false);
|
|
result = await innerTask.ConfigureAwait(false);
|
|
}
|
|
catch (OperationCanceledException) { break; }
|
|
catch (Exception ex)
|
|
{
|
|
Log("Errore JS/interop: " + ex.Message);
|
|
StopAutomation("Errore JS/interop: " + ex.Message);
|
|
return;
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(result) && result.Length >= 2 && result[0] == '"' && result[^1] == '"') result = JsonSerializer.Deserialize<string>(result) ?? result;
|
|
|
|
// read limits each iteration (on UI thread)
|
|
int maxClicks = int.MaxValue, maxResets = int.MaxValue;
|
|
double minPrice = 0.0, maxPrice = double.MaxValue;
|
|
try
|
|
{
|
|
Dispatcher.Invoke(() =>
|
|
{
|
|
try { if (int.TryParse(MaxClicksBox.Text, out var mc) && mc > 0) maxClicks = mc; } catch { }
|
|
try { if (int.TryParse(MaxResetsBox.Text, out var mr) && mr > 0) maxResets = mr; } catch { }
|
|
try { if (double.TryParse(MinPriceBox.Text.Replace(',', '.'), System.Globalization.NumberStyles.Any, CultureInfo.InvariantCulture, out var mp)) minPrice = mp; } catch { }
|
|
try { if (double.TryParse(MaxPriceBox.Text.Replace(',', '.'), System.Globalization.NumberStyles.Any, CultureInfo.InvariantCulture, out var mxa)) maxPrice = mxa > 0 ? mxa : double.MaxValue; } catch { }
|
|
});
|
|
}
|
|
catch { }
|
|
|
|
try
|
|
{
|
|
using var doc = JsonDocument.Parse(string.IsNullOrEmpty(result) ? "{}" : result);
|
|
var root = doc.RootElement;
|
|
var status = root.GetProperty("status").GetString() ?? string.Empty;
|
|
|
|
if (root.TryGetProperty("price", out var priceEl) && priceEl.ValueKind != JsonValueKind.Null)
|
|
{
|
|
var priceStr = priceEl.GetString();
|
|
if (!string.IsNullOrEmpty(priceStr) && double.TryParse(priceStr.Replace(',', '.').Trim(), NumberStyles.Any, CultureInfo.InvariantCulture, out var pval))
|
|
{
|
|
SetCurrentPriceText(pval.ToString("0.##") + " €");
|
|
}
|
|
}
|
|
|
|
if (status == "soon")
|
|
{
|
|
Log("Stato: INIZIA TRA POCO. In attesa che appaia il pulsante PUNTA...");
|
|
while (!token.IsCancellationRequested)
|
|
{
|
|
var op2 = Dispatcher.InvokeAsync(() => webView.ExecuteScriptAsync("(function(){ var b=document.querySelector('a.auction-btn-bid, a.bid-button'); if(!b) return 'no'; var t=(b.textContent||b.innerText||'').trim(); return /PUNTA/i.test(t)?'yes':'no'; })();"));
|
|
var inner2 = await op2.Task.ConfigureAwait(false);
|
|
var chk = await inner2.ConfigureAwait(false);
|
|
if (!string.IsNullOrEmpty(chk))
|
|
{
|
|
if (chk.Length >= 2 && chk[0] == '"' && chk[^1] == '"') chk = JsonSerializer.Deserialize<string>(chk) ?? chk;
|
|
if (chk == "yes") { Log("Pulsante PUNTA trovato. Riprendo esecuzione."); break; }
|
|
}
|
|
await Task.Delay(700, token).ConfigureAwait(false);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (status == "no-button")
|
|
{
|
|
Log("Nessun pulsante PUNTA trovato; arresto");
|
|
StopAutomation("Nessun pulsante PUNTA trovato; arresto");
|
|
return;
|
|
}
|
|
|
|
if (status == "no-timer" || status == "no-timer-extracted")
|
|
{
|
|
Log("Timer non trovato: pausa in corso, verrà ripreso quando il timer ricompare");
|
|
|
|
// detect closed countdown / scheduled opening
|
|
try
|
|
{
|
|
var closedOp = Dispatcher.InvokeAsync(() => webView.ExecuteScriptAsync(closedCountdownScript));
|
|
var closedInner = await closedOp.Task.ConfigureAwait(false);
|
|
var closedRaw = await closedInner.ConfigureAwait(false);
|
|
|
|
if (!string.IsNullOrEmpty(closedRaw))
|
|
{
|
|
if (closedRaw.Length >= 2 && closedRaw[0] == '"' && closedRaw[^1] == '"') closedRaw = JsonSerializer.Deserialize<string>(closedRaw) ?? closedRaw;
|
|
try
|
|
{
|
|
var paragraphs = JsonSerializer.Deserialize<string[]>(closedRaw);
|
|
var combined = paragraphs != null ? string.Join(" ", paragraphs.Where(p => !string.IsNullOrWhiteSpace(p))) : string.Empty;
|
|
if (!string.IsNullOrWhiteSpace(combined) && (combined.IndexOf("RIAPRE", StringComparison.OrdinalIgnoreCase) >= 0 || combined.IndexOf("L'asta inizia", StringComparison.OrdinalIgnoreCase) >= 0))
|
|
{
|
|
Log("Asta in pausa rilevata: '" + combined + "'. Inizio countdown verso apertura.");
|
|
var m = Regex.Match(combined, "(\\d{1,2}:\\d{2})");
|
|
DateTime? target = null;
|
|
if (m.Success)
|
|
{
|
|
var timeStr = m.Groups[1].Value;
|
|
if (combined.IndexOf("Oggi", StringComparison.OrdinalIgnoreCase) >= 0)
|
|
{
|
|
if (DateTime.TryParseExact(timeStr, "H:mm", CultureInfo.GetCultureInfo("it-IT"), DateTimeStyles.None, out var tod))
|
|
{
|
|
target = DateTime.Today.Add(tod.TimeOfDay);
|
|
if (target <= DateTime.Now) target = target.Value.AddDays(1);
|
|
}
|
|
else if (TimeSpan.TryParse(timeStr, out var ts)) target = DateTime.Today.Add(ts);
|
|
}
|
|
else if (combined.IndexOf("Domani", StringComparison.OrdinalIgnoreCase) >= 0)
|
|
{
|
|
if (TimeSpan.TryParse(timeStr, out var ts2)) target = DateTime.Today.AddDays(1).Add(ts2);
|
|
}
|
|
else if (TimeSpan.TryParse(timeStr, out var ts3)) target = DateTime.Today.Add(ts3);
|
|
}
|
|
|
|
if (target.HasValue)
|
|
{
|
|
Log($"Apertura programmata: {target.Value:g}. Log minuto-per-minuto avviato.");
|
|
while (!token.IsCancellationRequested && DateTime.Now < target.Value)
|
|
{
|
|
var remaining = target.Value - DateTime.Now;
|
|
Log($"Tempo rimanente all'apertura: {Math.Floor(remaining.TotalHours)}h {remaining.Minutes}m {remaining.Seconds}s");
|
|
try { await Task.Delay(TimeSpan.FromMinutes(1), token).ConfigureAwait(false); } catch (OperationCanceledException) { break; }
|
|
}
|
|
|
|
if (!token.IsCancellationRequested) Log("Orario di apertura raggiunto. Riprendo monitoraggio normale.");
|
|
continue;
|
|
}
|
|
|
|
Log("Apertura programmata rilevata ma orario non interpretabile: '" + combined + "'");
|
|
try { await Task.Delay(TimeSpan.FromMinutes(5), token).ConfigureAwait(false); } catch (OperationCanceledException) { break; }
|
|
continue;
|
|
}
|
|
}
|
|
catch { }
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
try { await Task.Delay(800, token).ConfigureAwait(false); } catch (OperationCanceledException) { break; }
|
|
continue;
|
|
}
|
|
|
|
if (status == "found")
|
|
{
|
|
var timerValue = root.GetProperty("timer").GetString();
|
|
if (timerValue != previousTimer) Log("Timer rilevato: " + timerValue);
|
|
|
|
try
|
|
{
|
|
if (previousTimer != null && int.TryParse(previousTimer, out var prev) && int.TryParse(timerValue, out var curr))
|
|
{
|
|
if (curr > prev)
|
|
{
|
|
resetCount++;
|
|
Dispatcher.Invoke(() => ResetCountText.Text = resetCount.ToString());
|
|
|
|
try
|
|
{
|
|
var winOp = Dispatcher.InvokeAsync(() => webView.ExecuteScriptAsync(getLastBidderScript));
|
|
var winInner = await winOp.Task.ConfigureAwait(false);
|
|
var winRaw = await winInner.ConfigureAwait(false);
|
|
if (!string.IsNullOrEmpty(winRaw) && winRaw.Length >= 2 && winRaw[0] == '"' && winRaw[^1] == '"') winRaw = JsonSerializer.Deserialize<string>(winRaw) ?? winRaw;
|
|
if (!string.IsNullOrEmpty(winRaw))
|
|
{
|
|
try
|
|
{
|
|
using var dw = JsonDocument.Parse(winRaw);
|
|
var rw = dw.RootElement;
|
|
if (rw.GetProperty("status").GetString() == "found")
|
|
{
|
|
var winner = rw.GetProperty("bidder").GetString() ?? "Sconosciuto";
|
|
var now = DateTime.Now;
|
|
if (_bidders.ContainsKey(winner)) _bidders[winner] = (_bidders[winner].Count + 1, now);
|
|
else _bidders[winner] = (1, now);
|
|
UpdateBiddersGrid();
|
|
Log("Offerente registrato/reset: " + winner + " (" + now.ToString("g") + ")");
|
|
}
|
|
}
|
|
catch { }
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
Log("Timer resettato (contatore): " + resetCount);
|
|
if (resetCount >= maxResets) { StopAutomation($"Limite reset raggiunto: {resetCount}"); return; }
|
|
}
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
previousTimer = timerValue;
|
|
|
|
// price bounds handling
|
|
if (root.TryGetProperty("price", out var pEl2) && pEl2.ValueKind != JsonValueKind.Null)
|
|
{
|
|
var priceStr2 = pEl2.GetString();
|
|
if (!string.IsNullOrEmpty(priceStr2) && double.TryParse(priceStr2.Replace(',', '.').Trim(), NumberStyles.Any, CultureInfo.InvariantCulture, out var pcur))
|
|
{
|
|
if (pcur < minPrice)
|
|
{
|
|
Log($"Prezzo {pcur:0.##}€ sotto limite minimo ({minPrice:0.##}). Pausa.");
|
|
while (!token.IsCancellationRequested)
|
|
{
|
|
var opP = Dispatcher.InvokeAsync(() => webView.ExecuteScriptAsync(pricePollScript));
|
|
var innerP = await opP.Task.ConfigureAwait(false);
|
|
var pricePoll = await innerP.ConfigureAwait(false);
|
|
if (!string.IsNullOrEmpty(pricePoll) && pricePoll.Length >= 2 && pricePoll[0] == '"' && pricePoll[^1] == '"') pricePoll = JsonSerializer.Deserialize<string>(pricePoll) ?? pricePoll;
|
|
if (!string.IsNullOrEmpty(pricePoll) && double.TryParse(pricePoll.Replace(',', '.'), NumberStyles.Any, CultureInfo.InvariantCulture, out var pp))
|
|
{
|
|
SetCurrentPriceText(pp.ToString("0.##") + " €");
|
|
if (pp >= minPrice) { Log("Prezzo salito sopra il minimo; ripresa esecuzione."); break; }
|
|
}
|
|
try { await Task.Delay(700, token).ConfigureAwait(false); } catch (OperationCanceledException) { break; }
|
|
}
|
|
}
|
|
else if (pcur > maxPrice)
|
|
{
|
|
Log($"Prezzo {pcur:0.##}€ sopra limite massimo ({maxPrice:0.##}). Pausa.");
|
|
while (!token.IsCancellationRequested)
|
|
{
|
|
var opP = Dispatcher.InvokeAsync(() => webView.ExecuteScriptAsync(pricePollScript));
|
|
var innerP = await opP.Task.ConfigureAwait(false);
|
|
var pricePoll = await innerP.ConfigureAwait(false);
|
|
if (!string.IsNullOrEmpty(pricePoll) && pricePoll.Length >= 2 && pricePoll[0] == '"' && pricePoll[^1] == '"') pricePoll = JsonSerializer.Deserialize<string>(pricePoll) ?? pricePoll;
|
|
if (!string.IsNullOrEmpty(pricePoll) && double.TryParse(pricePoll.Replace(',', '.'), NumberStyles.Any, CultureInfo.InvariantCulture, out var pp))
|
|
{
|
|
SetCurrentPriceText(pp.ToString("0.##") + " €");
|
|
if (pp <= maxPrice) { Log("Prezzo sceso sotto il massimo; ripresa esecuzione."); break; }
|
|
}
|
|
try { await Task.Delay(700, token).ConfigureAwait(false); } catch (OperationCanceledException) { break; }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (timerValue == "0")
|
|
{
|
|
string? lastBidder = null;
|
|
try
|
|
{
|
|
var opL = Dispatcher.InvokeAsync(() => webView.ExecuteScriptAsync(getLastBidderScript));
|
|
var innerL = await opL.Task.ConfigureAwait(false);
|
|
var lr = await innerL.ConfigureAwait(false);
|
|
if (!string.IsNullOrEmpty(lr) && lr.Length >= 2 && lr[0] == '"' && lr[^1] == '"') lr = JsonSerializer.Deserialize<string>(lr) ?? lr;
|
|
if (!string.IsNullOrEmpty(lr))
|
|
{
|
|
try { using var d = JsonDocument.Parse(lr); var r = d.RootElement; if (r.GetProperty("status").GetString() == "found") lastBidder = r.GetProperty("bidder").GetString(); }
|
|
catch { lastBidder = lr; }
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
if (!_pauseBids)
|
|
{
|
|
try
|
|
{
|
|
var opClick = Dispatcher.InvokeAsync(() => webView.ExecuteScriptAsync(clickScript));
|
|
var innerClick = await opClick.Task.ConfigureAwait(false);
|
|
var clickRes = await innerClick.ConfigureAwait(false);
|
|
if (!string.IsNullOrEmpty(clickRes) && clickRes.Length >= 2 && clickRes[0] == '"' && clickRes[^1] == '"') clickRes = JsonSerializer.Deserialize<string>(clickRes) ?? clickRes;
|
|
Log("Click eseguito: " + clickRes);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log("Errore durante click JS: " + ex.Message);
|
|
}
|
|
|
|
clickCount++;
|
|
Dispatcher.Invoke(() => ClickCountText.Text = clickCount.ToString());
|
|
|
|
var id = !string.IsNullOrEmpty(lastBidder) ? lastBidder! : "AutoBidder";
|
|
var now = DateTime.Now;
|
|
if (_bidders.ContainsKey(id)) _bidders[id] = (_bidders[id].Count + 1, now);
|
|
else _bidders[id] = (1, now);
|
|
UpdateBiddersGrid();
|
|
|
|
if (clickCount >= maxClicks) { StopAutomation($"Limite click raggiunto: {clickCount}"); return; }
|
|
|
|
try { await Task.Delay(postClickDelayMs, token).ConfigureAwait(false); } catch (OperationCanceledException) { break; }
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (JsonException ex)
|
|
{
|
|
Log("Errore parsing JSON: " + ex.Message);
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
break;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log("Errore loop: " + ex.Message);
|
|
StopAutomation("Errore loop: " + ex.Message);
|
|
return;
|
|
}
|
|
|
|
try { await Task.Delay(200, token).ConfigureAwait(false); } catch (OperationCanceledException) { break; }
|
|
}
|
|
|
|
Dispatcher.Invoke(() => { StartButton.IsEnabled = true; StopButton.IsEnabled = false; StartButton.Opacity = 1.0; StopButton.Opacity = 0.5; });
|
|
Log("Automazione terminata");
|
|
}
|
|
}
|
|
} |