diff --git a/Mimante/App.xaml b/Mimante/App.xaml
new file mode 100644
index 0000000..8a3dd23
--- /dev/null
+++ b/Mimante/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/Mimante/App.xaml.cs b/Mimante/App.xaml.cs
new file mode 100644
index 0000000..ec7348a
--- /dev/null
+++ b/Mimante/App.xaml.cs
@@ -0,0 +1,14 @@
+using System.Configuration;
+using System.Data;
+using System.Windows;
+
+namespace Mimante
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+
+}
diff --git a/Mimante/AssemblyInfo.cs b/Mimante/AssemblyInfo.cs
new file mode 100644
index 0000000..b0ec827
--- /dev/null
+++ b/Mimante/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/Mimante/MainWindow.xaml b/Mimante/MainWindow.xaml
new file mode 100644
index 0000000..dd894de
--- /dev/null
+++ b/Mimante/MainWindow.xaml
@@ -0,0 +1,155 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Max Clicks
+
+
+
+
+ Max Resets
+
+
+
+
+
+
+ Min Price
+
+
+
+
+ Max Price
+
+
+
+
+ Prezzo corrente
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Mimante/MainWindow.xaml.cs b/Mimante/MainWindow.xaml.cs
new file mode 100644
index 0000000..694f711
--- /dev/null
+++ b/Mimante/MainWindow.xaml.cs
@@ -0,0 +1,636 @@
+using System;
+using System.Text.Json;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using Microsoft.Web.WebView2.Core;
+
+namespace AutoBidder
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ private CancellationTokenSource? _cts;
+ private Task? _automationTask;
+
+ public MainWindow()
+ {
+ InitializeComponent();
+ // create a simple programmatic icon (circle with AB) and assign to window
+ 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; // set window and taskbar icon
+ }
+ catch
+ {
+ // ignore icon errors
+ }
+
+ // initial visual states: start is primary action
+ StartButton.IsEnabled = true;
+ StartButton.Opacity = 1.0;
+ StopButton.IsEnabled = false;
+ StopButton.Opacity = 0.5;
+ BackButton.IsEnabled = false;
+
+ // navigation completed -> update back button state
+ webView.NavigationCompleted += WebView_NavigationCompleted;
+ }
+
+ private void WebView_NavigationCompleted(object? sender, CoreWebView2NavigationCompletedEventArgs e)
+ {
+ UpdateBackButton();
+ }
+
+ private void UpdateBackButton()
+ {
+ try
+ {
+ Dispatcher.Invoke(() =>
+ {
+ BackButton.IsEnabled = webView.CoreWebView2 != null && webView.CoreWebView2.CanGoBack;
+ try
+ {
+ string? url = null;
+ if (webView.CoreWebView2 != null)
+ {
+ url = webView.CoreWebView2.Source;
+ }
+ else if (webView.Source != null)
+ {
+ url = webView.Source.ToString();
+ }
+
+ if (!string.IsNullOrEmpty(url)) SiteLinkText.Text = url;
+ }
+ catch { }
+ });
+ }
+ catch { }
+ }
+
+ private async void StartButton_Click(object sender, RoutedEventArgs e)
+ {
+ StartButton.IsEnabled = false;
+ StopButton.IsEnabled = true;
+ // visual feedback: running -> stop is primary
+ StartButton.Opacity = 0.5;
+ StopButton.Opacity = 1.0;
+
+ Log("Inizializzazione web...");
+
+ try
+ {
+ // Ensure WebView2 is initialized
+ if (webView.CoreWebView2 == null)
+ {
+ await webView.EnsureCoreWebView2Async();
+ }
+ UpdateBackButton();
+ }
+ catch (Exception ex)
+ {
+ var msg = "Errore inizializzazione WebView2: " + ex.Message;
+ Log(msg);
+ StartButton.IsEnabled = true;
+ StopButton.IsEnabled = false;
+ StartButton.Opacity = 1.0;
+ StopButton.Opacity = 0.5;
+ return;
+ }
+
+ Log("Avviato: WebView inizializzato. Avvio monitoraggio pagina.");
+
+ _cts = new CancellationTokenSource();
+ var token = _cts.Token;
+ _automationTask = Task.Run(async () => await AutomationLoop(token), token);
+ }
+
+ private void StopButton_Click(object sender, RoutedEventArgs e)
+ {
+ // visual feedback: stopped -> start is primary
+ 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)
+ {
+ // if not initialized, try ensuring
+ _ = 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.Dispatcher.Invoke(() =>
+ {
+ StartButton.IsEnabled = true;
+ StopButton.IsEnabled = false;
+ // reset visual states: start primary
+ StartButton.Opacity = 1.0;
+ StopButton.Opacity = 0.5;
+ });
+
+ Log("STOP: " + reason);
+ }
+
+ 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();
+ }
+ });
+ }
+ catch
+ {
+ // ignore logging errors
+ }
+ }
+
+ private async Task AutomationLoop(CancellationToken token)
+ {
+ int clickCount = 0;
+ int resetCount = 0;
+ string? previousTimer = null;
+
+ // read limits from UI
+ int maxClicks = 0;
+ int maxResets = 0;
+ double minPrice = 0.0;
+ double maxPrice = double.MaxValue;
+ try
+ {
+ Dispatcher.Invoke(() =>
+ {
+ int.TryParse(MaxClicksBox.Text, out maxClicks);
+ int.TryParse(MaxResetsBox.Text, out maxResets);
+ if (maxClicks <= 0) maxClicks = int.MaxValue;
+ if (maxResets <= 0) maxResets = int.MaxValue;
+
+ // price bounds: 0 = no limit
+ if (!double.TryParse(MinPriceBox.Text.Replace(',', '.'), System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out minPrice)) minPrice = 0.0;
+ if (!double.TryParse(MaxPriceBox.Text.Replace(',', '.'), System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out var tmpMax)) tmpMax = 0.0;
+ if (tmpMax <= 0) maxPrice = double.MaxValue; else maxPrice = tmpMax;
+ });
+ }
+ catch { maxClicks = int.MaxValue; maxResets = int.MaxValue; minPrice = 0.0; maxPrice = double.MaxValue; }
+
+ // post-click delay to avoid duplicate actions
+ // increased delay to click as late as possible (milliseconds)
+ const int postClickDelayMs = 1200;
+
+ // Improved JS snippet: find PUNTA anchor/button by class or text, find nearby numeric timer elements (including svg text) and price
+ const string findScript = @"(function(){
+ function isVisible(el){ if(!el) return false; try{ var r=el.getBoundingClientRect(); var s=window.getComputedStyle(el); return r.width>0 && r.height>0 && s.visibility!=='hidden' && s.display!=='none'; }catch(e){ return false; } }
+
+ // find price
+ var priceText = '';
+ try{ var pEl = document.querySelector('.auction-action-price strong, .auction-action-price'); if(pEl) priceText = (pEl.textContent||pEl.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){}
+
+ // Try to find a bid button that might say PUNTA or INIZIA
+ 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});
+ }
+ if(/\bPUNTA\b/i.test(txt)){
+ // continue to find timer normally
+ }
+ }
+
+ // Prefer direct countdown element by known class
+ 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});
+ // if no digits, continue to fallback
+ }
+ }catch(err){ /* ignore */ }
+
+ 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 btnRect; try{ btnRect = btn.getBoundingClientRect(); }catch(e){ btnRect = {top:0,left:0,right:0,bottom:0,width:0,height:0}; }
+
+ // collect possible numeric-containing elements, including svg text
+ 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});
+
+ // prefer elements that are visually near and above the button
+ 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); }
+
+ 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});
+})();";
+
+ const string clickScript = @"(function(){
+ 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); } }
+})();";
+
+ try
+ {
+ 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;
+ }
+
+ string json = result ?? "";
+ if (json.Length >= 2 && json[0] == '"' && json[^1] == '"')
+ {
+ json = System.Text.Json.JsonSerializer.Deserialize(result) ?? json;
+ }
+
+ try
+ {
+ using var doc = JsonDocument.Parse(json);
+ var root = doc.RootElement;
+ var status = root.GetProperty("status").GetString();
+
+ // price may be present
+ string? priceStr = null;
+ double? priceVal = null;
+ if (root.TryGetProperty("price", out var priceEl) && priceEl.ValueKind != JsonValueKind.Null)
+ {
+ priceStr = priceEl.GetString();
+ if (!string.IsNullOrEmpty(priceStr))
+ {
+ if (double.TryParse(priceStr.Replace(',', '.').Trim(), System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out var pval))
+ {
+ priceVal = pval;
+ Dispatcher.Invoke(() => CurrentPriceText.Text = pval.ToString("0.##") + " €");
+ }
+ }
+ }
+
+ if (status == "soon")
+ {
+ Log("Stato: INIZIA TRA POCO. In attesa che appaia il pulsante PUNTA...");
+ // poll until PUNTA button appears (or cancellation)
+ while (!token.IsCancellationRequested)
+ {
+ string? chk = null;
+ try
+ {
+ 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);
+ chk = await inner2.ConfigureAwait(false);
+ }
+ catch { chk = null; }
+
+ if (!string.IsNullOrEmpty(chk))
+ {
+ if (chk.Length >= 2 && chk[0] == '"' && chk[^1] == '"') chk = JsonSerializer.Deserialize(chk) ?? chk;
+ if (chk == "yes")
+ {
+ Log("Pulsante PUNTA trovato. Riprendo esecuzione.");
+ break;
+ }
+ }
+
+ await Task.Delay(700, token).ConfigureAwait(false);
+ }
+
+ continue; // next main loop iteration
+ }
+
+ 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")
+ {
+ // Pause: wait until timer reappears
+ Log("Timer non trovato: pausa in corso, verrà ripreso quando il timer ricompare");
+ bool resumed = false;
+ while (!token.IsCancellationRequested)
+ {
+ string? pollResult = null;
+ try
+ {
+ var op3 = Dispatcher.InvokeAsync(() => webView.ExecuteScriptAsync(findScript));
+ var inner3 = await op3.Task.ConfigureAwait(false);
+ pollResult = await inner3.ConfigureAwait(false);
+ }
+ catch { pollResult = null; }
+
+ if (!string.IsNullOrEmpty(pollResult))
+ {
+ if (pollResult.Length >= 2 && pollResult[0] == '"' && pollResult[^1] == '"') pollResult = JsonSerializer.Deserialize(pollResult) ?? pollResult;
+ try
+ {
+ using var doc2 = JsonDocument.Parse(pollResult);
+ var st2 = doc2.RootElement.GetProperty("status").GetString();
+ if (st2 == "found" || st2 == "soon")
+ {
+ Log("Timer ricomparso, ripresa.");
+ resumed = true;
+ break;
+ }
+ if (st2 == "no-button")
+ {
+ Log("Nessun pulsante durante pausa; arresto");
+ StopAutomation("Nessun pulsante durante pausa");
+ return;
+ }
+ }
+ catch { }
+ }
+
+ await Task.Delay(800, token).ConfigureAwait(false);
+ }
+
+ if (!resumed) break;
+
+ continue;
+ }
+
+ if (status == "found")
+ {
+ var timerValue = root.GetProperty("timer").GetString();
+
+ // only log when timer value actually changes
+ if (timerValue != previousTimer)
+ {
+ Log("Timer rilevato: " + timerValue);
+
+ // detect resets: if price available, enforce min/max bounds
+ 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());
+ Log("Timer resettato (contatore): " + resetCount);
+ // stop if reached limit
+ if (resetCount >= maxResets)
+ {
+ StopAutomation($"Limite reset raggiunto: {resetCount}");
+ return;
+ }
+ }
+ }
+ }
+ catch { }
+
+ previousTimer = timerValue;
+ }
+
+ // price check: if price available, enforce min/max bounds
+ if (priceVal.HasValue)
+ {
+ if (priceVal.Value < minPrice)
+ {
+ Log($"Prezzo {priceVal.Value:0.##}€ sotto limite minimo ({minPrice:0.##}). Pausa.");
+ // wait until price >= minPrice
+ while (!token.IsCancellationRequested)
+ {
+ // poll price only
+ string? pricePoll = null;
+ try
+ {
+ var opP = Dispatcher.InvokeAsync(() => webView.ExecuteScriptAsync(@"(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; })();"));
+ var innerP = await opP.Task.ConfigureAwait(false);
+ pricePoll = await innerP.ConfigureAwait(false);
+ }
+ catch { pricePoll = null; }
+
+ if (!string.IsNullOrEmpty(pricePoll))
+ {
+ if (pricePoll.Length >= 2 && pricePoll[0] == '"' && pricePoll[^1] == '"') pricePoll = JsonSerializer.Deserialize(pricePoll) ?? pricePoll;
+ if (double.TryParse(pricePoll.Replace(',', '.'), System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out var pp))
+ {
+ Dispatcher.Invoke(() => CurrentPriceText.Text = pp.ToString("0.##") + " €");
+ if (pp >= minPrice)
+ {
+ Log("Prezzo salito sopra il minimo; ripresa esecuzione.");
+ break;
+ }
+ }
+ }
+
+ await Task.Delay(700, token).ConfigureAwait(false);
+ }
+ }
+
+ if (priceVal.Value > maxPrice)
+ {
+ Log($"Prezzo {priceVal.Value:0.##}€ sopra limite massimo ({maxPrice:0.##}). Pausa.");
+ while (!token.IsCancellationRequested)
+ {
+ string? pricePoll = null;
+ try
+ {
+ var opP = Dispatcher.InvokeAsync(() => webView.ExecuteScriptAsync(@"(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; })();"));
+ var innerP = await opP.Task.ConfigureAwait(false);
+ pricePoll = await innerP.ConfigureAwait(false);
+ }
+ catch { pricePoll = null; }
+
+ if (!string.IsNullOrEmpty(pricePoll))
+ {
+ if (pricePoll.Length >= 2 && pricePoll[0] == '"' && pricePoll[^1] == '"') pricePoll = JsonSerializer.Deserialize(pricePoll) ?? pricePoll;
+ if (double.TryParse(pricePoll.Replace(',', '.'), System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out var pp))
+ {
+ Dispatcher.Invoke(() => CurrentPriceText.Text = pp.ToString("0.##") + " €");
+ if (pp <= maxPrice)
+ {
+ Log("Prezzo sceso sotto il massimo; ripresa esecuzione.");
+ break;
+ }
+ }
+ }
+
+ await Task.Delay(700, token).ConfigureAwait(false);
+ }
+ }
+ }
+
+ if (timerValue == "0")
+ {
+ // immediate click when timer reaches 0 (extreme test)
+ string clickResult;
+ try
+ {
+ var op2 = Dispatcher.InvokeAsync(() => webView.ExecuteScriptAsync(clickScript));
+ var inner2 = await op2.Task.ConfigureAwait(false);
+ clickResult = await inner2.ConfigureAwait(false);
+ }
+ catch (OperationCanceledException)
+ {
+ break;
+ }
+ catch (Exception ex)
+ {
+ Log("Errore durante click JS: " + ex.Message);
+ StopAutomation("Errore durante click JS: " + ex.Message);
+ return;
+ }
+
+ if (clickResult.Length >= 2 && clickResult[0] == '"' && clickResult[^1] == '"')
+ {
+ clickResult = JsonSerializer.Deserialize(clickResult) ?? clickResult;
+ }
+
+ // increment click counter and update UI
+ clickCount++;
+ Dispatcher.Invoke(() => ClickCountText.Text = clickCount.ToString());
+
+ Log("Click eseguito: " + clickResult + " (totale: " + clickCount + ")");
+
+ // stop if reached max clicks
+ if (clickCount >= maxClicks)
+ {
+ StopAutomation($"Limite click raggiunto: {clickCount}");
+ return;
+ }
+
+ await Task.Delay(postClickDelayMs, token).ConfigureAwait(false);
+ continue;
+ }
+
+ await Task.Delay(200, token).ConfigureAwait(false);
+ }
+
+ await Task.Delay(200, token).ConfigureAwait(false);
+ }
+ catch (JsonException ex)
+ {
+ Log("Errore parsing JSON: " + ex.Message);
+ await Task.Delay(300, token).ConfigureAwait(false);
+ }
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ }
+ catch (Exception ex)
+ {
+ Log("Errore loop: " + ex.Message);
+ StopAutomation("Errore loop: " + ex.Message);
+ }
+ finally
+ {
+ StartButton.Dispatcher.Invoke(() =>
+ {
+ StartButton.IsEnabled = true;
+ StopButton.IsEnabled = false;
+ StartButton.Opacity = 1.0;
+ StopButton.Opacity = 0.5;
+ });
+
+ Log("Automazione terminata");
+ }
+ }
+
+ protected override void OnClosed(EventArgs e)
+ {
+ try { _cts?.Cancel(); } catch { }
+ base.OnClosed(e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Mimante/Mimante.csproj b/Mimante/Mimante.csproj
new file mode 100644
index 0000000..f60561e
--- /dev/null
+++ b/Mimante/Mimante.csproj
@@ -0,0 +1,17 @@
+
+
+
+ WinExe
+ net8.0-windows
+ enable
+ enable
+ true
+ AutoBidder
+ AutoBidder
+
+
+
+
+
+
+
diff --git a/Mimante/Mimante.sln b/Mimante/Mimante.sln
new file mode 100644
index 0000000..9ba9265
--- /dev/null
+++ b/Mimante/Mimante.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.14.36511.14 d17.14
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mimante", "Mimante.csproj", "{9BBAEF93-DF66-432C-9349-459E272D6538}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {9BBAEF93-DF66-432C-9349-459E272D6538}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9BBAEF93-DF66-432C-9349-459E272D6538}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9BBAEF93-DF66-432C-9349-459E272D6538}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9BBAEF93-DF66-432C-9349-459E272D6538}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {1C55CA56-D270-4D9A-91DA-410BF131E905}
+ EndGlobalSection
+EndGlobal