Aggiornamento alla versione 4.0.0

- Aggiunta proprietà `MaxClicks` per limitare le puntate per asta.
- Migliorata gestione dei log con livelli di severità e colori.
- Sostituzione di `TextBox` con `RichTextBox` per log avanzati.
- Aggiunto pulsante per cancellare il log globale.
- Migliorata gestione dei pulsanti globali (Avvia, Pausa, Ferma).
- Ottimizzato il monitoraggio per aste in pausa e polling ridotto.
- Aggiunto controllo per mettere in pausa aste al raggiungimento di `MaxClicks`.
- Aggiunti nuovi stili per pulsanti e miglioramenti visivi in UI.
- Aggiunto convertitore `StartButtonOpacityConverter`.
- Aggiunta icona dell'applicazione (`app.ico`) come risorsa WPF.
This commit is contained in:
Alberto Balbo
2025-10-24 16:29:08 +02:00
parent 4e16f50aeb
commit 139a9d62b7
8 changed files with 579 additions and 449 deletions

2
Mimante/Assets/app.ico Normal file
View File

@@ -0,0 +1,2 @@
[Binary ICO placeholder removed in this environment]

View File

@@ -15,4 +15,9 @@
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.6584" /> <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.6584" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<!-- Ensure the application icon is included as a WPF Resource so Icon="Assets/app.ico" resolves -->
<Resource Include="Assets\app.ico" />
</ItemGroup>
</Project> </Project>

View File

@@ -6,4 +6,5 @@
<local:StartResumeConverter x:Key="StartResumeConverter"/> <local:StartResumeConverter x:Key="StartResumeConverter"/>
<local:BoolToOpacityConverter x:Key="BoolToOpacityConverter"/> <local:BoolToOpacityConverter x:Key="BoolToOpacityConverter"/>
<local:PauseButtonOpacityConverter x:Key="PauseButtonOpacityConverter"/> <local:PauseButtonOpacityConverter x:Key="PauseButtonOpacityConverter"/>
<local:StartButtonOpacityConverter x:Key="StartButtonOpacityConverter"/>
</ResourceDictionary> </ResourceDictionary>

View File

@@ -0,0 +1,24 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace AutoBidder.Converters
{
public class StartButtonOpacityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length == 2 && values[0] is bool isActive && values[1] is bool isPaused)
{
// Bright (1.0) when not active (can start) or when paused (can resume)
return (!isActive || isPaused) ? 1.0 : 0.5;
}
return 1.0;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -111,7 +111,7 @@ WindowStartupLocation="CenterScreen">
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<!-- Box Aste Monitorate --> <!-- Box Aste Monitorate -->
<Border Grid.Column="0" Background="#1a1a1a" Padding="12" CornerRadius="8" BorderBrush="#333333" BorderThickness="1"> <Border Grid.Column="0" Background="#1a1a1a" Padding="12" CornerRadius="6" BorderBrush="#333333" BorderThickness="1">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
@@ -192,8 +192,13 @@ WindowStartupLocation="CenterScreen">
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Content="Avvia" CommandParameter="{Binding}" Click="GridStartAuction_Click" Style="{StaticResource SmallButtonStyle}" Background="#00CC66" Padding="6,2" MinWidth="40" Margin="0,0,4,0" <Button Content="Avvia" CommandParameter="{Binding}" Click="GridStartAuction_Click" Style="{StaticResource SmallButtonStyle}" Background="#00CC66" Padding="6,2" MinWidth="40" Margin="0,0,4,0">
Opacity="{Binding IsActive, Converter={StaticResource BoolToOpacityConverter}, ConverterParameter=Inverse}"> <Button.Opacity>
<MultiBinding Converter="{StaticResource StartButtonOpacityConverter}">
<Binding Path="IsActive" />
<Binding Path="IsPaused" />
</MultiBinding>
</Button.Opacity>
<Button.IsEnabled> <Button.IsEnabled>
<MultiBinding Converter="{StaticResource StartResumeConverter}"> <MultiBinding Converter="{StaticResource StartResumeConverter}">
<Binding Path="IsActive" /> <Binding Path="IsActive" />
@@ -231,16 +236,20 @@ WindowStartupLocation="CenterScreen">
<GridSplitter Grid.Column="1" Width="8" HorizontalAlignment="Stretch" Background="#333333" /> <GridSplitter Grid.Column="1" Width="8" HorizontalAlignment="Stretch" Background="#333333" />
<!-- Log Globale --> <!-- Log Globale -->
<Border Grid.Column="2" Background="#1a1a1a" Padding="12" CornerRadius="8" BorderBrush="#333333" BorderThickness="1"> <Border Grid.Column="2" Background="#1a1a1a" Padding="12" CornerRadius="6" BorderBrush="#333333" BorderThickness="1">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Log Globale" FontWeight="Bold" Margin="0,0,0,10" FontSize="14" Foreground="#00CC66" /> <TextBlock Grid.Row="0" Text="Log Globale" FontWeight="Bold" Margin="0,0,0,10" FontSize="14" Foreground="#00CC66" />
<TextBox x:Name="LogBox" Grid.Row="1" IsReadOnly="True" VerticalScrollBarVisibility="Auto" <RichTextBox x:Name="LogBox" Grid.Row="1" IsReadOnly="True" VerticalScrollBarVisibility="Auto"
TextWrapping="Wrap" Background="#0f0f0f" Foreground="#CCCCCC" Padding="8" Background="#0f0f0f" Foreground="#CCCCCC" Padding="8"
FontFamily="Consolas" FontSize="11" BorderBrush="#333333" BorderThickness="1" /> FontFamily="Consolas" FontSize="11" BorderBrush="#333333" BorderThickness="1" />
<Button x:Name="ClearGlobalLogButton" Grid.Row="2" Content="Pulisci Log" Click="ClearGlobalLogButton_Click"
Style="{StaticResource SmallButtonStyle}" IsEnabled="True"
Background="#666" Padding="10,8" Height="32" FontSize="11" Margin="0,8,0,0" HorizontalAlignment="Stretch" />
</Grid> </Grid>
</Border> </Border>
</Grid> </Grid>
@@ -248,7 +257,7 @@ WindowStartupLocation="CenterScreen">
<GridSplitter Grid.Row="3" Height="8" HorizontalAlignment="Stretch" Background="#333333" /> <GridSplitter Grid.Row="3" Height="8" HorizontalAlignment="Stretch" Background="#333333" />
<!-- Dettagli Asta Selezionata - 3 Pannelli Affiancati --> <!-- Dettagli Asta Selezionata - 3 Pannelli Affiancati -->
<Border Grid.Row="4" Background="#1a1a1a" Padding="8" CornerRadius="8" BorderBrush="#333333" BorderThickness="1"> <Border Grid.Row="4" Background="#1a1a1a" Padding="8" CornerRadius="6" BorderBrush="#333333" BorderThickness="1">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="250" /> <ColumnDefinition Width="*" MinWidth="250" />
@@ -279,11 +288,16 @@ WindowStartupLocation="CenterScreen">
FontSize="12" FontWeight="SemiBold" Foreground="#FFFFFF" TextWrapping="Wrap" Margin="0,0,0,16" /> FontSize="12" FontWeight="SemiBold" Foreground="#FFFFFF" TextWrapping="Wrap" Margin="0,0,0,16" />
<!-- Link asta e pulsanti --> <!-- Link asta e pulsanti -->
<StackPanel Orientation="Horizontal" Margin="0,0,0,12" VerticalAlignment="Center"> <Grid Margin="0,0,0,12">
<TextBox x:Name="SelectedAuctionUrl" Text="" IsReadOnly="True" MinWidth="220" Margin="0,0,8,0" VerticalAlignment="Center" Background="#181818" Foreground="#00CCFF" BorderBrush="#333" BorderThickness="1" FontSize="11" /> <Grid.ColumnDefinitions>
<Button Content="Apri" x:Name="OpenAuctionButton" Click="OpenAuctionButton_Click" Style="{StaticResource SmallButtonStyle}" Background="#0099FF" Padding="10,6" Margin="0,0,4,0" Height="28" MinWidth="60" FontSize="11" /> <ColumnDefinition Width="*" />
<Button Content="Copia" x:Name="CopyAuctionUrlButton" Click="CopyAuctionUrlButton_Click" Style="{StaticResource SmallButtonStyle}" Background="#666" Padding="10,6" Height="28" MinWidth="60" FontSize="11" /> <ColumnDefinition Width="Auto" />
</StackPanel> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox x:Name="SelectedAuctionUrl" Text="" IsReadOnly="True" MinWidth="220" Margin="0,0,8,0" VerticalAlignment="Center" Background="#181818" Foreground="#00CCFF" BorderBrush="#333" BorderThickness="1" FontSize="11" Grid.Column="0" HorizontalAlignment="Stretch" />
<Button Content="Apri" x:Name="OpenAuctionButton" Click="OpenAuctionButton_Click" Style="{StaticResource SmallButtonStyle}" Background="#0099FF" Padding="10,6" Margin="0,0,4,0" Height="28" MinWidth="60" FontSize="11" Grid.Column="1" />
<Button Content="Copia" x:Name="CopyAuctionUrlButton" Click="CopyAuctionUrlButton_Click" Style="{StaticResource SmallButtonStyle}" Background="#666" Padding="10,6" Height="28" MinWidth="60" FontSize="11" Grid.Column="2" />
</Grid>
<UniformGrid Columns="2" Margin="0,0,0,8"> <UniformGrid Columns="2" Margin="0,0,0,8">
<StackPanel Margin="0,0,4,0"> <StackPanel Margin="0,0,4,0">
@@ -324,8 +338,16 @@ WindowStartupLocation="CenterScreen">
</StackPanel> </StackPanel>
</UniformGrid> </UniformGrid>
<UniformGrid Columns="2"> <UniformGrid Columns="2" Margin="0,0,0,12">
<StackPanel Margin="0,0,4,0">
<TextBlock Text="Max Clicks (0=inf)" FontSize="10" Margin="0,0,0,4" Foreground="#999" />
<TextBox x:Name="SelectedMaxClicks" Text="0" TextChanged="SelectedMaxClicks_TextChanged"
Background="#1a1a1a" Foreground="#FFF" Padding="8" FontSize="12" BorderBrush="#444" BorderThickness="1" />
</StackPanel>
<StackPanel Margin="4,0,0,0">
<TextBlock Text="(Opzionale)" FontSize="10" Margin="0,0,0,4" Foreground="#999" />
<TextBlock Text="Limita il numero di puntate del bot per questa asta" FontSize="10" Foreground="#AAA" TextWrapping="Wrap" />
</StackPanel>
</UniformGrid> </UniformGrid>
</StackPanel> </StackPanel>
</ScrollViewer> </ScrollViewer>
@@ -409,13 +431,13 @@ WindowStartupLocation="CenterScreen">
<TextBlock Grid.Row="0" Text="Log Asta" FontSize="13" FontWeight="Bold" Foreground="#00CC66" Margin="0,0,0,10" /> <TextBlock Grid.Row="0" Text="Log Asta" FontSize="13" FontWeight="Bold" Foreground="#00CC66" Margin="0,0,0,10" />
<TextBox x:Name="SelectedAuctionLog" Grid.Row="1" IsReadOnly="True" <RichTextBox x:Name="SelectedAuctionLog" Grid.Row="1" IsReadOnly="True"
VerticalScrollBarVisibility="Auto" TextWrapping="Wrap" Background="#1a1a1a" Foreground="#CCC" VerticalScrollBarVisibility="Auto" Background="#1a1a1a" Foreground="#CCC"
Padding="8" FontSize="10" FontFamily="Consolas" BorderBrush="#333333" BorderThickness="1" /> Padding="8" FontSize="10" FontFamily="Consolas" BorderBrush="#333333" BorderThickness="1" />
<Button Grid.Row="2" x:Name="ClearLogButton" Content="Pulisci Log" Click="ClearLogButton_Click" <Button Grid.Row="2" x:Name="ClearLogButton" Content="Pulisci Log" Click="ClearLogButton_Click"
Style="{StaticResource SmallButtonStyle}" IsEnabled="False" Style="{StaticResource SmallButtonStyle}" IsEnabled="False"
Background="#666" Padding="10,8" Height="32" FontSize="11" Margin="0,8,0,0" /> Background="#666" Padding="10,8" Height="32" FontSize="11" Margin="0,8,0,0" HorizontalAlignment="Stretch" />
</Grid> </Grid>
</Border> </Border>
</Grid> </Grid>

View File

@@ -49,7 +49,7 @@ namespace AutoBidder
// Carica aste salvate // Carica aste salvate
LoadSavedAuctions(); LoadSavedAuctions();
// Ensure initial global button states (pause/stop disabled until started) // Ensure initial global button states (pause/stop disabled until starting)
UpdateGlobalControlButtons(); UpdateGlobalControlButtons();
Log("[OK] AutoBidder v4.0 avviato (API-based)"); Log("[OK] AutoBidder v4.0 avviato (API-based)");
@@ -69,6 +69,9 @@ namespace AutoBidder
UpdateAuctionLog(_selectedAuction); UpdateAuctionLog(_selectedAuction);
RefreshBiddersGrid(_selectedAuction); RefreshBiddersGrid(_selectedAuction);
} }
// Aggiorna lo stato dei bottoni globali quando cambia stato di un'asta
UpdateGlobalControlButtons();
}); });
} }
@@ -172,20 +175,38 @@ namespace AutoBidder
private void StartButton_Click(object sender, RoutedEventArgs e) private void StartButton_Click(object sender, RoutedEventArgs e)
{ {
if (_isAutomationActive) return;
try try
{ {
// Ensure all auctions are active and not paused if (!_isAutomationActive)
SetAllActive(true); {
// Start the monitor and ensure existing auctions are active
foreach (var vm in _auctionViewModels)
{
vm.IsActive = true;
vm.IsPaused = false;
}
// Avvia monitoraggio
_auctionMonitor.Start(); _auctionMonitor.Start();
_isAutomationActive = true; _isAutomationActive = true;
Log("Monitoraggio avviato!");
}
else
{
// Monitoring already running: resume/activate auctions (start those paused/stopped)
foreach (var vm in _auctionViewModels)
{
if (!vm.IsActive)
{
vm.IsActive = true;
}
// Always clear pause to resume bidding
vm.IsPaused = false;
}
Log("Avviate/riprese le aste (tutte)");
}
UpdateGlobalControlButtons(); UpdateGlobalControlButtons();
Log("Monitoraggio avviato!");
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -206,7 +227,10 @@ namespace AutoBidder
} }
// Set all auctions to stopped // Set all auctions to stopped
SetAllActive(false); foreach (var vm in _auctionViewModels)
{
vm.IsActive = false;
}
UpdateGlobalControlButtons(); UpdateGlobalControlButtons();
@@ -372,6 +396,17 @@ namespace AutoBidder
} }
} }
private void SelectedMaxClicks_TextChanged(object sender, TextChangedEventArgs e)
{
if (_selectedAuction != null && sender is TextBox tb)
{
if (int.TryParse(tb.Text, out var value) && value >= 0)
{
_selectedAuction.AuctionInfo.MaxClicks = value;
}
}
}
private void ResetSettingsButton_Click(object sender, RoutedEventArgs e) private void ResetSettingsButton_Click(object sender, RoutedEventArgs e)
{ {
if (_selectedAuction == null) return; if (_selectedAuction == null) return;
@@ -428,7 +463,7 @@ namespace AutoBidder
if (result == MessageBoxResult.Yes) if (result == MessageBoxResult.Yes)
{ {
_selectedAuction.AuctionInfo.AuctionLog.Clear(); _selectedAuction.AuctionInfo.AuctionLog.Clear();
SelectedAuctionLog.Text = ""; SelectedAuctionLog.Document.Blocks.Clear();
Log($"Log pulito: {_selectedAuction.Name}"); Log($"Log pulito: {_selectedAuction.Name}");
} }
} }
@@ -773,9 +808,23 @@ namespace AutoBidder
try try
{ {
var auctionInfo = auction.AuctionInfo; var auctionInfo = auction.AuctionInfo;
var logText = string.Join(Environment.NewLine, auctionInfo.AuctionLog);
SelectedAuctionLog.Text = logText; SelectedAuctionLog.Document.Blocks.Clear();
foreach (var entry in auctionInfo.AuctionLog)
{
var upper = entry.ToUpperInvariant();
var color = System.Windows.Media.Brushes.LightSkyBlue; // default: parsing/info -> blue
if (upper.Contains("[ERRORE]") || upper.Contains("[FAIL]") || upper.Contains("EXCEPTION"))
color = System.Windows.Media.Brushes.IndianRed;
else if (upper.Contains("[WARN]") || upper.Contains("WARN"))
color = System.Windows.Media.Brushes.Orange;
var p = new System.Windows.Documents.Paragraph { Margin = new Thickness(0) };
var r = new System.Windows.Documents.Run(entry) { Foreground = color };
p.Inlines.Add(r);
SelectedAuctionLog.Document.Blocks.Add(p);
}
SelectedAuctionLog.ScrollToEnd(); SelectedAuctionLog.ScrollToEnd();
} }
catch { } catch { }
@@ -927,14 +976,45 @@ namespace AutoBidder
} }
} }
private void Log(string message) private enum LogLevel { Info, Warn, Error }
private void Log(string message, LogLevel level = LogLevel.Info)
{ {
// Reduced verbosity: only write concise messages
try try
{ {
// Auto-detect severity if caller used default
if (level == LogLevel.Info)
{
var m = message.ToUpperInvariant();
if (m.Contains("[ERRORE]") || m.Contains("[FAIL]") || m.Contains("EXCEPTION") || m.Contains("ERROR"))
level = LogLevel.Error;
else if (m.Contains("[WARN]") || m.Contains("WARN") || m.Contains("[WARN"))
level = LogLevel.Warn;
else
level = LogLevel.Info;
}
Dispatcher.BeginInvoke(() => Dispatcher.BeginInvoke(() =>
{ {
var entry = $"{DateTime.Now:HH:mm:ss} - {message}{Environment.NewLine}"; var para = new System.Windows.Documents.Paragraph();
LogBox.AppendText(entry); para.Margin = new Thickness(0);
var run = new System.Windows.Documents.Run($"{DateTime.Now:HH:mm:ss} - {message}");
switch (level)
{
case LogLevel.Info:
run.Foreground = System.Windows.Media.Brushes.LightGray;
break;
case LogLevel.Warn:
run.Foreground = System.Windows.Media.Brushes.Orange;
break;
case LogLevel.Error:
run.Foreground = System.Windows.Media.Brushes.IndianRed;
run.FontWeight = FontWeights.Bold;
break;
}
para.Inlines.Add(run);
LogBox.Document.Blocks.Add(para);
LogBox.ScrollToEnd(); LogBox.ScrollToEnd();
}); });
} }
@@ -981,7 +1061,7 @@ namespace AutoBidder
vm.IsActive = true; vm.IsActive = true;
vm.IsPaused = false; vm.IsPaused = false;
Log($"[START] Asta avviata: {vm.Name}"); Log($"[START] Asta avviata: {vm.Name}");
UpdateStartButtonState(); UpdateGlobalControlButtons();
} }
} }
@@ -991,7 +1071,7 @@ namespace AutoBidder
{ {
vm.IsPaused = true; vm.IsPaused = true;
Log($"[PAUSA] Asta in pausa: {vm.Name}"); Log($"[PAUSA] Asta in pausa: {vm.Name}");
UpdateStartButtonState(); UpdateGlobalControlButtons();
} }
} }
@@ -1001,7 +1081,7 @@ namespace AutoBidder
{ {
vm.IsActive = false; vm.IsActive = false;
Log($"[STOP] Asta fermata: {vm.Name}"); Log($"[STOP] Asta fermata: {vm.Name}");
UpdateStartButtonState(); UpdateGlobalControlButtons();
} }
} }
@@ -1042,68 +1122,50 @@ namespace AutoBidder
UpdateGlobalControlButtons(); UpdateGlobalControlButtons();
} }
private void UpdateStartButtonState() private void ClearGlobalLogButton_Click(object sender, RoutedEventArgs e)
{ {
// Backwards compatibility: delegate to global control update var result = MessageBox.Show("Pulire il log globale?", "Conferma", MessageBoxButton.YesNo, MessageBoxImage.Question);
UpdateGlobalControlButtons(); if (result == MessageBoxResult.Yes)
}
private void SetAllActive(bool active)
{ {
foreach (var vm in _auctionViewModels) LogBox.Document.Blocks.Clear();
{
vm.IsActive = active;
if (active)
vm.IsPaused = false;
}
}
private void SetAllPaused(bool paused)
{
foreach (var vm in _auctionViewModels)
{
if (vm.IsActive)
vm.IsPaused = paused;
} }
} }
private void UpdateGlobalControlButtons() private void UpdateGlobalControlButtons()
{ {
// Determine overall state // Rules: darken (disable) global button only if ALL auctions are in that state
bool anyActive = _auctionViewModels.Any(vm => vm.IsActive); bool allActive = _auctionViewModels.All(vm => vm.IsActive && !vm.IsPaused);
bool anyPaused = _auctionViewModels.Any(vm => vm.IsActive && vm.IsPaused); bool allStopped = _auctionViewModels.All(vm => !vm.IsActive);
bool allPaused = _auctionViewModels.All(vm => vm.IsActive && vm.IsPaused);
// Priority: if no active auctions => only Start enabled // Start button: disabled/dark when allActive OR allPaused OR allStopped ?
if (!anyActive) // According to rule: darken if all in same state that matches the button meaning
if (allActive)
{ {
StartButton.IsEnabled = true; StartButton.Opacity = 1.0;
PauseAllButton.IsEnabled = false; PauseAllButton.Opacity = 0.5;
StopButton.IsEnabled = false; StopButton.Opacity = 0.5;
return;
}
// If any paused (regardless of monitoring state) -> Start & Stop enabled, Pause disabled
if (anyPaused)
{
StartButton.IsEnabled = true; StartButton.Opacity = 1.0;
PauseAllButton.IsEnabled = false; PauseAllButton.Opacity = 0.5;
StopButton.IsEnabled = true; StopButton.Opacity = 1.0;
return;
}
// Otherwise (some active, none paused)
if (_isAutomationActive)
{
// Monitoring running: Start disabled, Pause and Stop enabled
StartButton.IsEnabled = false; StartButton.Opacity = 0.5; StartButton.IsEnabled = false; StartButton.Opacity = 0.5;
PauseAllButton.IsEnabled = true; PauseAllButton.Opacity = 1.0;
StopButton.IsEnabled = true; StopButton.Opacity = 1.0;
} }
else else
{ {
// Automation not running but auctions active and none paused: allow Start, Pause, Stop
StartButton.IsEnabled = true; StartButton.Opacity = 1.0; StartButton.IsEnabled = true; StartButton.Opacity = 1.0;
}
// PauseAll button: darken if allPaused or allStopped
if (allPaused || allStopped)
{
PauseAllButton.IsEnabled = false; PauseAllButton.Opacity = 0.5;
}
else
{
PauseAllButton.IsEnabled = true; PauseAllButton.Opacity = 1.0; PauseAllButton.IsEnabled = true; PauseAllButton.Opacity = 1.0;
}
// Stop button: darken if allStopped
if (allStopped)
{
StopButton.IsEnabled = false; StopButton.Opacity = 0.5;
}
else
{
StopButton.IsEnabled = true; StopButton.Opacity = 1.0; StopButton.IsEnabled = true; StopButton.Opacity = 1.0;
} }
} }

View File

@@ -19,6 +19,8 @@ namespace AutoBidder.Models
public double MaxPrice { get; set; } = 0; public double MaxPrice { get; set; } = 0;
public int MinResets { get; set; } = 0; // Numero minimo reset prima di puntare public int MinResets { get; set; } = 0; // Numero minimo reset prima di puntare
public int MaxResets { get; set; } = 0; // Numero massimo reset (0 = illimitati) public int MaxResets { get; set; } = 0; // Numero massimo reset (0 = illimitati)
// Numero massimo di click/puntate che il bot può eseguire per questa asta (0 = illimitato)
public int MaxClicks { get; set; } = 0;
// Stato asta // Stato asta
public bool IsActive { get; set; } = true; public bool IsActive { get; set; } = true;

View File

@@ -150,10 +150,10 @@ namespace AutoBidder.Services
lock (_auctions) lock (_auctions)
{ {
// Filtra aste che devono ancora essere monitorate // Filtra aste che devono ancora essere monitorate
// Esclude: pausa manuale, chiuse definitivamente, vinte, perse // Include aste attive anche se messe in pausa: vogliamo continuare a monitorarle
// ma non inviare puntate per quelle in pausa.
activeAuctions = _auctions.Where(a => activeAuctions = _auctions.Where(a =>
a.IsActive && a.IsActive &&
!a.IsPaused &&
!IsAuctionTerminated(a) !IsAuctionTerminated(a)
).ToList(); ).ToList();
} }
@@ -168,13 +168,13 @@ namespace AutoBidder.Services
var pollTasks = activeAuctions.Select(a => PollAndProcessAuction(a, token)); var pollTasks = activeAuctions.Select(a => PollAndProcessAuction(a, token));
await Task.WhenAll(pollTasks); await Task.WhenAll(pollTasks);
// Ottimizzazione polling aste in pausa // Ottimizzazione polling per aste in pausa
bool anyPaused = false; bool anyPaused = false;
DateTime now = DateTime.Now; DateTime now = DateTime.Now;
int pauseDelayMs = 1000; // default int pauseDelayMs = 1000; // default
foreach (var a in activeAuctions) foreach (var a in activeAuctions)
{ {
if (a.BidHistory.LastOrDefault()?.Notes?.Contains("PAUSA") == true) if (a.IsPaused)
{ {
anyPaused = true; anyPaused = true;
// Se tra le 00:00 e le 09:55 polling ogni 60s // Se tra le 00:00 e le 09:55 polling ogni 60s
@@ -306,7 +306,7 @@ namespace AutoBidder.Services
UpdateAuctionHistory(auction, state); UpdateAuctionHistory(auction, state);
// Verifica se puntare (solo se asta Running, NON se in pausa) // Verifica se puntare (solo se asta Running, NON se in pausa)
if (state.Status == AuctionStatus.Running && ShouldBid(auction, state)) if (state.Status == AuctionStatus.Running && ShouldBid(auction, state) && !auction.IsPaused)
{ {
auction.AddLog($"[TRIGGER] CONDIZIONI OK - Timer {state.Timer:F2}s <= {auction.TimerClick}s"); auction.AddLog($"[TRIGGER] CONDIZIONI OK - Timer {state.Timer:F2}s <= {auction.TimerClick}s");
auction.AddLog($"[BID] Invio puntata..."); auction.AddLog($"[BID] Invio puntata...");
@@ -355,6 +355,14 @@ namespace AutoBidder.Services
Success = result.Success, Success = result.Success,
Notes = result.Success ? $"EUR{result.NewPrice:F2}" : result.Error Notes = result.Success ? $"EUR{result.NewPrice:F2}" : result.Error
}); });
// Se abbiamo raggiunto il numero massimo di click per l'asta, metti in pausa le puntate (ma continua il monitor)
if (auction.MaxClicks > 0 && auction.MyClicks >= auction.MaxClicks)
{
auction.IsPaused = true;
auction.AddLog($"[PAUSA] Massimo click ({auction.MaxClicks}) raggiunto - Puntate disabilitate");
OnLog?.Invoke($"[PAUSA] [{auction.AuctionId}] MaxClicks raggiunti");
}
} }
} }
catch (Exception ex) catch (Exception ex)
@@ -385,6 +393,10 @@ namespace AutoBidder.Services
return false; return false;
} }
// Max clicks per auction
if (auction.MaxClicks > 0 && auction.MyClicks >= auction.MaxClicks)
return false;
return true; return true;
} }