Aggiornamento alla versione 4.0.0
* Aggiunto `BooleanToOpacityConverter` per gestire opacità dinamica. * Introdotto nuovo sistema di timing con `BidBeforeDeadlineMs`. * Aggiunta opzione `CheckAuctionOpenBeforeBid` per maggiore sicurezza. * Implementato polling adattivo (10ms-1000ms) e cooldown di 800ms. * Migliorata gestione pulsanti globali con supporto `AUTO-START`/`AUTO-STOP`. * Fix per il tasto `Canc` e focus automatico sul `DataGrid`. * Fix per avvio singola asta senza necessità di "Avvia Tutti". * Aggiornati formati CSV/JSON/XML con nuovi campi. * Migliorata gestione cookie con endpoint unico `buy_bids.php`. * Miglioramenti UI/UX: tooltip, formattazione prezzi, feedback visivo. * Aggiornata documentazione e changelog per la versione 4.0.0.
This commit is contained in:
@@ -3,11 +3,15 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:util="clr-namespace:AutoBidder.Utilities"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignHeight="800" d:DesignWidth="1200"
|
d:DesignHeight="800" d:DesignWidth="1200"
|
||||||
Background="#1E1E1E">
|
Background="#1E1E1E">
|
||||||
|
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
|
<!-- Converters -->
|
||||||
|
<util:BooleanToOpacityConverter x:Key="BoolToOpacity"/>
|
||||||
|
|
||||||
<!-- Rounded Button Style -->
|
<!-- Rounded Button Style -->
|
||||||
<Style x:Key="RoundedButton" TargetType="Button">
|
<Style x:Key="RoundedButton" TargetType="Button">
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
@@ -51,43 +55,53 @@
|
|||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="*"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<!-- Header -->
|
<!-- Header - COMPATTO SU 2 RIGHE -->
|
||||||
<Border Grid.Row="0" Background="#2D2D30" Padding="15" BorderBrush="#3E3E42" BorderThickness="0,0,0,1">
|
<Border Grid.Row="0" Background="#2D2D30" Padding="15,10" BorderBrush="#3E3E42" BorderThickness="0,0,0,1">
|
||||||
<Grid>
|
<Grid>
|
||||||
<!-- Info Utente (Left) -->
|
<Grid.RowDefinitions>
|
||||||
<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left">
|
<RowDefinition Height="Auto"/>
|
||||||
<TextBlock x:Name="UsernameText"
|
<RowDefinition Height="Auto"/>
|
||||||
Text="Utente: Non configurato"
|
</Grid.RowDefinitions>
|
||||||
Foreground="#00D800"
|
|
||||||
FontSize="13"
|
|
||||||
FontWeight="SemiBold"
|
|
||||||
Margin="0,0,0,5"/>
|
|
||||||
|
|
||||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,3">
|
<!-- Riga 1: Puntate + Credito -->
|
||||||
|
<StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,0,0,5">
|
||||||
<TextBlock Text="Puntate: "
|
<TextBlock Text="Puntate: "
|
||||||
Foreground="#CCCCCC"
|
Foreground="#999999"
|
||||||
FontSize="12"/>
|
FontSize="13"
|
||||||
|
Margin="0,0,5,0"/>
|
||||||
<TextBlock x:Name="RemainingBidsText"
|
<TextBlock x:Name="RemainingBidsText"
|
||||||
Text="0"
|
Text="0"
|
||||||
Foreground="#CCCCCC"
|
Foreground="#00D800"
|
||||||
FontSize="12"
|
FontSize="13"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Margin="0,0,25,0"/>
|
||||||
|
|
||||||
|
<TextBlock Text="Credito Shop: "
|
||||||
|
Foreground="#999999"
|
||||||
|
FontSize="13"
|
||||||
|
Margin="0,0,5,0"/>
|
||||||
|
<TextBlock x:Name="ShopCreditText"
|
||||||
|
Text="EUR 0.00"
|
||||||
|
Foreground="#00D800"
|
||||||
|
FontSize="13"
|
||||||
FontWeight="Bold"/>
|
FontWeight="Bold"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<StackPanel Orientation="Horizontal">
|
<!-- Riga 2: Aste vinte -->
|
||||||
|
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Left">
|
||||||
<TextBlock Text="Aste vinte da confermare: "
|
<TextBlock Text="Aste vinte da confermare: "
|
||||||
Foreground="#FFB700"
|
Foreground="#999999"
|
||||||
FontSize="12"/>
|
FontSize="12"
|
||||||
|
Margin="0,0,5,0"/>
|
||||||
<TextBlock x:Name="BannerAsteDaRiscattare"
|
<TextBlock x:Name="BannerAsteDaRiscattare"
|
||||||
Text="0"
|
Text="0"
|
||||||
Foreground="#FFB700"
|
Foreground="#FFB700"
|
||||||
FontSize="12"
|
FontSize="12"
|
||||||
FontWeight="Bold"/>
|
FontWeight="Bold"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<!-- Control Buttons (Right) -->
|
<!-- Control Buttons (Right) - Su entrambe le righe -->
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
<StackPanel Grid.Row="0" Grid.RowSpan="2" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||||
<Button x:Name="StartButton"
|
<Button x:Name="StartButton"
|
||||||
Content="Avvia Tutti"
|
Content="Avvia Tutti"
|
||||||
Background="#00D800"
|
Background="#00D800"
|
||||||
@@ -197,7 +211,9 @@
|
|||||||
AlternatingRowBackground="#252526"
|
AlternatingRowBackground="#252526"
|
||||||
BorderThickness="0"
|
BorderThickness="0"
|
||||||
SelectionChanged="MultiAuctionsGrid_SelectionChanged"
|
SelectionChanged="MultiAuctionsGrid_SelectionChanged"
|
||||||
KeyDown="MultiAuctionsGrid_KeyDown">
|
PreviewKeyDown="MultiAuctionsGrid_PreviewKeyDown"
|
||||||
|
Focusable="True"
|
||||||
|
FocusVisualStyle="{x:Null}">
|
||||||
<DataGrid.ColumnHeaderStyle>
|
<DataGrid.ColumnHeaderStyle>
|
||||||
<Style TargetType="DataGridColumnHeader">
|
<Style TargetType="DataGridColumnHeader">
|
||||||
<Setter Property="Background" Value="#2D2D30"/>
|
<Setter Property="Background" Value="#2D2D30"/>
|
||||||
@@ -239,24 +255,40 @@
|
|||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<Button Content="Avvia"
|
<Button Content="Avvia"
|
||||||
|
Command="{Binding DataContext.GridStartCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
|
||||||
|
CommandParameter="{Binding}"
|
||||||
|
IsEnabled="{Binding CanStart}"
|
||||||
|
Opacity="{Binding CanStart, Converter={StaticResource BoolToOpacity}}"
|
||||||
Background="#00D800"
|
Background="#00D800"
|
||||||
Style="{StaticResource SmallRoundedButton}"
|
Style="{StaticResource SmallRoundedButton}"
|
||||||
Padding="6,3"
|
Padding="6,3"
|
||||||
FontSize="10"
|
FontSize="10"
|
||||||
Margin="1"/>
|
Margin="1"/>
|
||||||
<Button Content="Pausa"
|
<Button Content="Pausa"
|
||||||
|
Command="{Binding DataContext.GridPauseCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
|
||||||
|
CommandParameter="{Binding}"
|
||||||
|
IsEnabled="{Binding CanPause}"
|
||||||
|
Opacity="{Binding CanPause, Converter={StaticResource BoolToOpacity}}"
|
||||||
Background="#FFB700"
|
Background="#FFB700"
|
||||||
Style="{StaticResource SmallRoundedButton}"
|
Style="{StaticResource SmallRoundedButton}"
|
||||||
Padding="6,3"
|
Padding="6,3"
|
||||||
FontSize="10"
|
FontSize="10"
|
||||||
Margin="1"/>
|
Margin="1"/>
|
||||||
<Button Content="Ferma"
|
<Button Content="Ferma"
|
||||||
|
Command="{Binding DataContext.GridStopCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
|
||||||
|
CommandParameter="{Binding}"
|
||||||
|
IsEnabled="{Binding CanStop}"
|
||||||
|
Opacity="{Binding CanStop, Converter={StaticResource BoolToOpacity}}"
|
||||||
Background="#E81123"
|
Background="#E81123"
|
||||||
Style="{StaticResource SmallRoundedButton}"
|
Style="{StaticResource SmallRoundedButton}"
|
||||||
Padding="6,3"
|
Padding="6,3"
|
||||||
FontSize="10"
|
FontSize="10"
|
||||||
Margin="1"/>
|
Margin="1"/>
|
||||||
<Button Content="Punta"
|
<Button Content="Punta"
|
||||||
|
Command="{Binding DataContext.GridBidCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
|
||||||
|
CommandParameter="{Binding}"
|
||||||
|
IsEnabled="{Binding CanBid}"
|
||||||
|
Opacity="{Binding CanBid, Converter={StaticResource BoolToOpacity}}"
|
||||||
Background="#9B4F96"
|
Background="#9B4F96"
|
||||||
Style="{StaticResource SmallRoundedButton}"
|
Style="{StaticResource SmallRoundedButton}"
|
||||||
Padding="6,3"
|
Padding="6,3"
|
||||||
@@ -399,7 +431,7 @@
|
|||||||
FontSize="10"/>
|
FontSize="10"/>
|
||||||
</UniformGrid>
|
</UniformGrid>
|
||||||
|
|
||||||
<!-- Settings Grid - Campi piu larghi (100px) -->
|
<!-- Settings Grid - Campi aggiornati -->
|
||||||
<Grid Margin="0,0,0,8">
|
<Grid Margin="0,0,0,8">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto"/>
|
<ColumnDefinition Width="Auto"/>
|
||||||
@@ -412,25 +444,33 @@
|
|||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<!-- Row 1 -->
|
<!-- Row 1: Anticipo ms -->
|
||||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Timer (s):" Foreground="#CCCCCC" FontSize="11" Margin="0,6" VerticalAlignment="Center"/>
|
<TextBlock Grid.Row="0" Grid.Column="0" Text="Anticipo (ms):" Foreground="#CCCCCC" FontSize="11" Margin="0,6" VerticalAlignment="Center" ToolTip="Millisecondi prima della scadenza per puntare"/>
|
||||||
<TextBox Grid.Row="0" Grid.Column="1" x:Name="SelectedTimerClick" Background="#1E1E1E" Foreground="White" BorderBrush="#3E3E42" Padding="6" Margin="5,6" FontSize="11" TextChanged="SelectedTimerClick_TextChanged"/>
|
<TextBox Grid.Row="0" Grid.Column="1" x:Name="SelectedBidBeforeDeadlineMs" Background="#1E1E1E" Foreground="White" BorderBrush="#3E3E42" Padding="6" Margin="5,6" FontSize="11" TextChanged="SelectedBidBeforeDeadlineMs_TextChanged"/>
|
||||||
|
|
||||||
<TextBlock Grid.Row="0" Grid.Column="3" Text="Delay (ms):" Foreground="#CCCCCC" FontSize="11" Margin="0,6" VerticalAlignment="Center"/>
|
<TextBlock Grid.Row="0" Grid.Column="3" Text="Min EUR:" Foreground="#CCCCCC" FontSize="11" Margin="0,6" VerticalAlignment="Center"/>
|
||||||
<TextBox Grid.Row="0" Grid.Column="4" x:Name="SelectedDelayMs" Background="#1E1E1E" Foreground="White" BorderBrush="#3E3E42" Padding="6" Margin="5,6" FontSize="11" TextChanged="SelectedDelayMs_TextChanged"/>
|
<TextBox Grid.Row="0" Grid.Column="4" x:Name="SelectedMinPrice" Background="#1E1E1E" Foreground="White" BorderBrush="#3E3E42" Padding="6" Margin="5,6" FontSize="11" TextChanged="SelectedMinPrice_TextChanged"/>
|
||||||
|
|
||||||
<!-- Row 2 -->
|
<!-- Row 2 -->
|
||||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Min EUR:" Foreground="#CCCCCC" FontSize="11" Margin="0,6" VerticalAlignment="Center"/>
|
<TextBlock Grid.Row="1" Grid.Column="0" Text="Max EUR:" Foreground="#CCCCCC" FontSize="11" Margin="0,6" VerticalAlignment="Center"/>
|
||||||
<TextBox Grid.Row="1" Grid.Column="1" x:Name="SelectedMinPrice" Background="#1E1E1E" Foreground="White" BorderBrush="#3E3E42" Padding="6" Margin="5,6" FontSize="11" TextChanged="SelectedMinPrice_TextChanged"/>
|
<TextBox Grid.Row="1" Grid.Column="1" x:Name="SelectedMaxPrice" Background="#1E1E1E" Foreground="White" BorderBrush="#3E3E42" Padding="6" Margin="5,6" FontSize="11" TextChanged="SelectedMaxPrice_TextChanged"/>
|
||||||
|
|
||||||
<TextBlock Grid.Row="1" Grid.Column="3" Text="Max EUR:" Foreground="#CCCCCC" FontSize="11" Margin="0,6" VerticalAlignment="Center"/>
|
<TextBlock Grid.Row="1" Grid.Column="3" Text="Max Clicks:" Foreground="#CCCCCC" FontSize="11" Margin="0,6" VerticalAlignment="Center"/>
|
||||||
<TextBox Grid.Row="1" Grid.Column="4" x:Name="SelectedMaxPrice" Background="#1E1E1E" Foreground="White" BorderBrush="#3E3E42" Padding="6" Margin="5,6" FontSize="11" TextChanged="SelectedMaxPrice_TextChanged"/>
|
<TextBox Grid.Row="1" Grid.Column="4" x:Name="SelectedMaxClicks" Background="#1E1E1E" Foreground="White" BorderBrush="#3E3E42" Padding="6" Margin="5,6" FontSize="11" TextChanged="SelectedMaxClicks_TextChanged"/>
|
||||||
|
|
||||||
<!-- Row 3 -->
|
<!-- Row 3: Check Auction Open -->
|
||||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Max Clicks:" Foreground="#CCCCCC" FontSize="11" Margin="0,6" VerticalAlignment="Center"/>
|
<CheckBox Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="5"
|
||||||
<TextBox Grid.Row="2" Grid.Column="1" x:Name="SelectedMaxClicks" Background="#1E1E1E" Foreground="White" BorderBrush="#3E3E42" Padding="6" Margin="5,6" FontSize="11" TextChanged="SelectedMaxClicks_TextChanged"/>
|
x:Name="SelectedCheckAuctionOpen"
|
||||||
|
Content="Verifica stato asta prima di puntare"
|
||||||
|
Foreground="#CCCCCC"
|
||||||
|
FontSize="11"
|
||||||
|
Margin="0,10,0,0"
|
||||||
|
Checked="SelectedCheckAuctionOpen_Changed"
|
||||||
|
Unchecked="SelectedCheckAuctionOpen_Changed"
|
||||||
|
ToolTip="Aggiunge una chiamata API extra per verificare che l'asta sia ancora aperta"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
@@ -562,5 +602,17 @@
|
|||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
<!-- Hidden user info panel for compatibility -->
|
||||||
|
<Border x:Name="UserInfoPanel"
|
||||||
|
Visibility="Collapsed">
|
||||||
|
<StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock x:Name="UsernameText" Text=""/>
|
||||||
|
<TextBlock x:Name="UserIdText" Text=""/>
|
||||||
|
</StackPanel>
|
||||||
|
<TextBlock x:Name="UserEmailText" Text=""/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -14,6 +14,43 @@ namespace AutoBidder.Controls
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dependency Properties per Commands (da bindare al MainWindow)
|
||||||
|
public static readonly DependencyProperty GridStartCommandProperty =
|
||||||
|
DependencyProperty.Register(nameof(GridStartCommand), typeof(ICommand), typeof(AuctionMonitorControl));
|
||||||
|
|
||||||
|
public static readonly DependencyProperty GridPauseCommandProperty =
|
||||||
|
DependencyProperty.Register(nameof(GridPauseCommand), typeof(ICommand), typeof(AuctionMonitorControl));
|
||||||
|
|
||||||
|
public static readonly DependencyProperty GridStopCommandProperty =
|
||||||
|
DependencyProperty.Register(nameof(GridStopCommand), typeof(ICommand), typeof(AuctionMonitorControl));
|
||||||
|
|
||||||
|
public static readonly DependencyProperty GridBidCommandProperty =
|
||||||
|
DependencyProperty.Register(nameof(GridBidCommand), typeof(ICommand), typeof(AuctionMonitorControl));
|
||||||
|
|
||||||
|
public ICommand? GridStartCommand
|
||||||
|
{
|
||||||
|
get => (ICommand?)GetValue(GridStartCommandProperty);
|
||||||
|
set => SetValue(GridStartCommandProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICommand? GridPauseCommand
|
||||||
|
{
|
||||||
|
get => (ICommand?)GetValue(GridPauseCommandProperty);
|
||||||
|
set => SetValue(GridPauseCommandProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICommand? GridStopCommand
|
||||||
|
{
|
||||||
|
get => (ICommand?)GetValue(GridStopCommandProperty);
|
||||||
|
set => SetValue(GridStopCommandProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICommand? GridBidCommand
|
||||||
|
{
|
||||||
|
get => (ICommand?)GetValue(GridBidCommandProperty);
|
||||||
|
set => SetValue(GridBidCommandProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
// Event handlers - these will bubble up to MainWindow
|
// Event handlers - these will bubble up to MainWindow
|
||||||
private void StartButton_Click(object sender, RoutedEventArgs e)
|
private void StartButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
@@ -47,23 +84,38 @@ namespace AutoBidder.Controls
|
|||||||
|
|
||||||
private void MultiAuctionsGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
private void MultiAuctionsGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
|
// Forza il focus sul DataGrid quando viene selezionata una riga
|
||||||
|
// Questo assicura che il tasto Canc venga catturato
|
||||||
|
if (sender is DataGrid grid && grid.SelectedItem != null)
|
||||||
|
{
|
||||||
|
// Attende che il rendering sia completo prima di dare il focus
|
||||||
|
grid.Dispatcher.BeginInvoke(new Action(() =>
|
||||||
|
{
|
||||||
|
if (!grid.IsFocused)
|
||||||
|
{
|
||||||
|
grid.Focus();
|
||||||
|
System.Diagnostics.Debug.WriteLine("[FOCUS] DataGrid ora ha il focus keyboard");
|
||||||
|
}
|
||||||
|
}), System.Windows.Threading.DispatcherPriority.Background);
|
||||||
|
}
|
||||||
|
|
||||||
RaiseEvent(new RoutedEventArgs(AuctionSelectionChangedEvent, this));
|
RaiseEvent(new RoutedEventArgs(AuctionSelectionChangedEvent, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MultiAuctionsGrid_KeyDown(object sender, KeyEventArgs e)
|
// Usato PreviewKeyDown invece di KeyDown per catturare l'evento prima che venga consumato
|
||||||
|
private void MultiAuctionsGrid_PreviewKeyDown(object sender, KeyEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Key == Key.Delete && MultiAuctionsGrid.SelectedItem != null)
|
if (e.Key == Key.Delete && MultiAuctionsGrid.SelectedItem != null)
|
||||||
{
|
{
|
||||||
var result = MessageBox.Show(
|
System.Diagnostics.Debug.WriteLine("[DELETE KEY] Tasto Canc premuto su asta selezionata");
|
||||||
"Sei sicuro di voler eliminare l'asta selezionata?",
|
|
||||||
"Conferma Eliminazione",
|
|
||||||
MessageBoxButton.YesNo,
|
|
||||||
MessageBoxImage.Question);
|
|
||||||
|
|
||||||
if (result == MessageBoxResult.Yes)
|
// Lancia direttamente l'evento senza chiedere conferma
|
||||||
{
|
// La conferma verrà mostrata dal gestore RemoveUrlButton_Click
|
||||||
|
System.Diagnostics.Debug.WriteLine("[DELETE KEY] Lancio evento RemoveUrlClicked");
|
||||||
RaiseEvent(new RoutedEventArgs(RemoveUrlClickedEvent, this));
|
RaiseEvent(new RoutedEventArgs(RemoveUrlClickedEvent, this));
|
||||||
}
|
|
||||||
|
// Previeni che l'evento venga gestito da altri controlli
|
||||||
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,14 +144,14 @@ namespace AutoBidder.Controls
|
|||||||
RaiseEvent(new RoutedEventArgs(ClearGlobalLogClickedEvent, this));
|
RaiseEvent(new RoutedEventArgs(ClearGlobalLogClickedEvent, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SelectedTimerClick_TextChanged(object sender, TextChangedEventArgs e)
|
private void SelectedBidBeforeDeadlineMs_TextChanged(object sender, TextChangedEventArgs e)
|
||||||
{
|
{
|
||||||
RaiseEvent(new RoutedEventArgs(TimerClickChangedEvent, this));
|
RaiseEvent(new RoutedEventArgs(BidBeforeDeadlineMsChangedEvent, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SelectedDelayMs_TextChanged(object sender, TextChangedEventArgs e)
|
private void SelectedCheckAuctionOpen_Changed(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
RaiseEvent(new RoutedEventArgs(DelayMsChangedEvent, this));
|
RaiseEvent(new RoutedEventArgs(CheckAuctionOpenChangedEvent, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SelectedMinPrice_TextChanged(object sender, TextChangedEventArgs e)
|
private void SelectedMinPrice_TextChanged(object sender, TextChangedEventArgs e)
|
||||||
@@ -154,11 +206,11 @@ namespace AutoBidder.Controls
|
|||||||
public static readonly RoutedEvent ClearGlobalLogClickedEvent = EventManager.RegisterRoutedEvent(
|
public static readonly RoutedEvent ClearGlobalLogClickedEvent = EventManager.RegisterRoutedEvent(
|
||||||
"ClearGlobalLogClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
|
"ClearGlobalLogClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
|
||||||
|
|
||||||
public static readonly RoutedEvent TimerClickChangedEvent = EventManager.RegisterRoutedEvent(
|
public static readonly RoutedEvent BidBeforeDeadlineMsChangedEvent = EventManager.RegisterRoutedEvent(
|
||||||
"TimerClickChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
|
"BidBeforeDeadlineMsChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
|
||||||
|
|
||||||
public static readonly RoutedEvent DelayMsChangedEvent = EventManager.RegisterRoutedEvent(
|
public static readonly RoutedEvent CheckAuctionOpenChangedEvent = EventManager.RegisterRoutedEvent(
|
||||||
"DelayMsChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
|
"CheckAuctionOpenChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
|
||||||
|
|
||||||
public static readonly RoutedEvent MinPriceChangedEvent = EventManager.RegisterRoutedEvent(
|
public static readonly RoutedEvent MinPriceChangedEvent = EventManager.RegisterRoutedEvent(
|
||||||
"MinPriceChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
|
"MinPriceChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
|
||||||
@@ -240,5 +292,35 @@ namespace AutoBidder.Controls
|
|||||||
add { AddHandler(ClearGlobalLogClickedEvent, value); }
|
add { AddHandler(ClearGlobalLogClickedEvent, value); }
|
||||||
remove { RemoveHandler(ClearGlobalLogClickedEvent, value); }
|
remove { RemoveHandler(ClearGlobalLogClickedEvent, value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public event RoutedEventHandler BidBeforeDeadlineMsChanged
|
||||||
|
{
|
||||||
|
add { AddHandler(BidBeforeDeadlineMsChangedEvent, value); }
|
||||||
|
remove { RemoveHandler(BidBeforeDeadlineMsChangedEvent, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public event RoutedEventHandler CheckAuctionOpenChanged
|
||||||
|
{
|
||||||
|
add { AddHandler(CheckAuctionOpenChangedEvent, value); }
|
||||||
|
remove { RemoveHandler(CheckAuctionOpenChangedEvent, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public event RoutedEventHandler MinPriceChanged
|
||||||
|
{
|
||||||
|
add { AddHandler(MinPriceChangedEvent, value); }
|
||||||
|
remove { RemoveHandler(MinPriceChangedEvent, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public event RoutedEventHandler MaxPriceChanged
|
||||||
|
{
|
||||||
|
add { AddHandler(MaxPriceChangedEvent, value); }
|
||||||
|
remove { RemoveHandler(MaxPriceChangedEvent, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public event RoutedEventHandler MaxClicksChanged
|
||||||
|
{
|
||||||
|
add { AddHandler(MaxClicksChangedEvent, value); }
|
||||||
|
remove { RemoveHandler(MaxClicksChangedEvent, value); }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,24 +104,11 @@
|
|||||||
Style="{StaticResource FieldLabel}"/>
|
Style="{StaticResource FieldLabel}"/>
|
||||||
|
|
||||||
<TextBox x:Name="SettingsCookieTextBox"
|
<TextBox x:Name="SettingsCookieTextBox"
|
||||||
Height="100"
|
Height="150"
|
||||||
TextWrapping="Wrap"
|
TextWrapping="Wrap"
|
||||||
AcceptsReturn="True"
|
AcceptsReturn="True"
|
||||||
VerticalScrollBarVisibility="Auto"
|
VerticalScrollBarVisibility="Auto"
|
||||||
Margin="0,0,0,10"/>
|
Margin="0,0,0,15"/>
|
||||||
|
|
||||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,15">
|
|
||||||
<Button Content="Importa dal Browser"
|
|
||||||
Background="#007ACC"
|
|
||||||
Style="{StaticResource ModernButton}"
|
|
||||||
Margin="0,0,10,0"
|
|
||||||
Click="ImportCookieFromBrowserButton_Click"/>
|
|
||||||
|
|
||||||
<Button Content="Cancella"
|
|
||||||
Background="#3E3E42"
|
|
||||||
Style="{StaticResource ModernButton}"
|
|
||||||
Click="CancelCookieButton_Click"/>
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<!-- Info Box -->
|
<!-- Info Box -->
|
||||||
<Border Style="{StaticResource InfoBox}">
|
<Border Style="{StaticResource InfoBox}">
|
||||||
@@ -237,7 +224,7 @@
|
|||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="200"/>
|
<ColumnDefinition Width="250"/>
|
||||||
<ColumnDefinition Width="150"/>
|
<ColumnDefinition Width="150"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
@@ -248,11 +235,11 @@
|
|||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Timer Click (secondi)" Foreground="#CCCCCC" Margin="0,10" VerticalAlignment="Center"/>
|
<TextBlock Grid.Row="0" Grid.Column="0" Text="Anticipo Puntata (millisecondi)" Foreground="#CCCCCC" Margin="0,10" VerticalAlignment="Center" ToolTip="Millisecondi prima della scadenza per puntare"/>
|
||||||
<TextBox Grid.Row="0" Grid.Column="1" x:Name="DefaultTimerClick" Text="0" Margin="10,10"/>
|
<TextBox Grid.Row="0" Grid.Column="1" x:Name="DefaultBidBeforeDeadlineMs" Text="200" Margin="10,10"/>
|
||||||
|
|
||||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Delay (millisecondi)" Foreground="#CCCCCC" Margin="0,10" VerticalAlignment="Center"/>
|
<TextBlock Grid.Row="1" Grid.Column="0" Text="Verifica Stato Prima di Puntare" Foreground="#CCCCCC" Margin="0,10" VerticalAlignment="Center" ToolTip="Controlla che l'asta sia ancora aperta prima di puntare"/>
|
||||||
<TextBox Grid.Row="1" Grid.Column="1" x:Name="DefaultDelayMs" Text="50" Margin="10,10"/>
|
<CheckBox Grid.Row="1" Grid.Column="1" x:Name="DefaultCheckAuctionOpen" Margin="10,10" VerticalAlignment="Center"/>
|
||||||
|
|
||||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Prezzo Minimo (€)" Foreground="#CCCCCC" Margin="0,10" VerticalAlignment="Center"/>
|
<TextBlock Grid.Row="2" Grid.Column="0" Text="Prezzo Minimo (€)" Foreground="#CCCCCC" Margin="0,10" VerticalAlignment="Center"/>
|
||||||
<TextBox Grid.Row="2" Grid.Column="1" x:Name="DefaultMinPrice" Text="0" Margin="10,10"/>
|
<TextBox Grid.Row="2" Grid.Column="1" x:Name="DefaultMinPrice" Text="0" Margin="10,10"/>
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ namespace AutoBidder.Controls
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Proprietà pubbliche per accesso da MainWindow
|
// Proprietà pubbliche per accesso da MainWindow (AGGIORNATE)
|
||||||
public TextBox DefaultTimerClickTextBox => DefaultTimerClick;
|
public TextBox DefaultBidBeforeDeadlineMsTextBox => DefaultBidBeforeDeadlineMs;
|
||||||
public TextBox DefaultDelayMsTextBox => DefaultDelayMs;
|
public CheckBox DefaultCheckAuctionOpenCheckBox => DefaultCheckAuctionOpen;
|
||||||
public TextBox DefaultMinPriceTextBox => DefaultMinPrice;
|
public TextBox DefaultMinPriceTextBox => DefaultMinPrice;
|
||||||
public TextBox DefaultMaxPriceTextBox => DefaultMaxPrice;
|
public TextBox DefaultMaxPriceTextBox => DefaultMaxPrice;
|
||||||
public TextBox DefaultMaxClicksTextBox => DefaultMaxClicks;
|
public TextBox DefaultMaxClicksTextBox => DefaultMaxClicks;
|
||||||
@@ -61,13 +61,37 @@ namespace AutoBidder.Controls
|
|||||||
RaiseEvent(new RoutedEventArgs(CancelDefaultsClickedEvent, this));
|
RaiseEvent(new RoutedEventArgs(CancelDefaultsClickedEvent, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nuovi eventi unificati
|
// Nuovo evento unificato - SALVA TUTTE LE IMPOSTAZIONI
|
||||||
private void SaveAllSettings_Click(object sender, RoutedEventArgs e)
|
private void SaveAllSettings_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
// Salva tutte le impostazioni in sequenza
|
try
|
||||||
|
{
|
||||||
|
// 1. Salva cookie (se presente)
|
||||||
RaiseEvent(new RoutedEventArgs(SaveCookieClickedEvent, this));
|
RaiseEvent(new RoutedEventArgs(SaveCookieClickedEvent, this));
|
||||||
|
|
||||||
|
// 2. Salva impostazioni export
|
||||||
RaiseEvent(new RoutedEventArgs(SaveSettingsClickedEvent, this));
|
RaiseEvent(new RoutedEventArgs(SaveSettingsClickedEvent, this));
|
||||||
|
|
||||||
|
// 3. Salva impostazioni predefinite aste
|
||||||
RaiseEvent(new RoutedEventArgs(SaveDefaultsClickedEvent, this));
|
RaiseEvent(new RoutedEventArgs(SaveDefaultsClickedEvent, this));
|
||||||
|
|
||||||
|
// UNICO MessageBox di conferma
|
||||||
|
MessageBox.Show(
|
||||||
|
"Tutte le impostazioni sono state salvate con successo.\n\nLe nuove impostazioni verranno applicate alle aste future.",
|
||||||
|
"Impostazioni Salvate",
|
||||||
|
MessageBoxButton.OK,
|
||||||
|
MessageBoxImage.Information
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show(
|
||||||
|
"Errore durante il salvataggio: " + ex.Message,
|
||||||
|
"Errore",
|
||||||
|
MessageBoxButton.OK,
|
||||||
|
MessageBoxImage.Error
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CancelAllSettings_Click(object sender, RoutedEventArgs e)
|
private void CancelAllSettings_Click(object sender, RoutedEventArgs e)
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ namespace AutoBidder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
// Note: Progress bar rimosso con refactoring Statistics
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void ExportAllButton_Click(object sender, RoutedEventArgs e)
|
private async void ExportAllButton_Click(object sender, RoutedEventArgs e)
|
||||||
@@ -182,6 +181,7 @@ namespace AutoBidder
|
|||||||
|
|
||||||
if (chosenExt.Equals(".json", StringComparison.OrdinalIgnoreCase))
|
if (chosenExt.Equals(".json", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
// JSON EXPORT - AGGIORNATO
|
||||||
var obj = new
|
var obj = new
|
||||||
{
|
{
|
||||||
AuctionId = a.AuctionId,
|
AuctionId = a.AuctionId,
|
||||||
@@ -189,8 +189,8 @@ namespace AutoBidder
|
|||||||
OriginalUrl = a.OriginalUrl,
|
OriginalUrl = a.OriginalUrl,
|
||||||
MinPrice = a.MinPrice,
|
MinPrice = a.MinPrice,
|
||||||
MaxPrice = a.MaxPrice,
|
MaxPrice = a.MaxPrice,
|
||||||
TimerClick = a.TimerClick,
|
BidBeforeDeadlineMs = a.BidBeforeDeadlineMs,
|
||||||
DelayMs = a.DelayMs,
|
CheckAuctionOpenBeforeBid = a.CheckAuctionOpenBeforeBid,
|
||||||
IsActive = a.IsActive,
|
IsActive = a.IsActive,
|
||||||
IsPaused = a.IsPaused,
|
IsPaused = a.IsPaused,
|
||||||
BidHistory = a.BidHistory,
|
BidHistory = a.BidHistory,
|
||||||
@@ -202,6 +202,7 @@ namespace AutoBidder
|
|||||||
}
|
}
|
||||||
else if (chosenExt.Equals(".xml", StringComparison.OrdinalIgnoreCase))
|
else if (chosenExt.Equals(".xml", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
// XML EXPORT - AGGIORNATO
|
||||||
var doc = new XDocument(
|
var doc = new XDocument(
|
||||||
new XElement("AuctionExport",
|
new XElement("AuctionExport",
|
||||||
new XElement("Metadata",
|
new XElement("Metadata",
|
||||||
@@ -210,8 +211,8 @@ namespace AutoBidder
|
|||||||
new XElement("OriginalUrl", a.OriginalUrl ?? string.Empty),
|
new XElement("OriginalUrl", a.OriginalUrl ?? string.Empty),
|
||||||
new XElement("MinPrice", a.MinPrice),
|
new XElement("MinPrice", a.MinPrice),
|
||||||
new XElement("MaxPrice", a.MaxPrice),
|
new XElement("MaxPrice", a.MaxPrice),
|
||||||
new XElement("TimerClick", a.TimerClick),
|
new XElement("BidBeforeDeadlineMs", a.BidBeforeDeadlineMs),
|
||||||
new XElement("DelayMs", a.DelayMs),
|
new XElement("CheckAuctionOpenBeforeBid", a.CheckAuctionOpenBeforeBid),
|
||||||
new XElement("IsActive", a.IsActive),
|
new XElement("IsActive", a.IsActive),
|
||||||
new XElement("IsPaused", a.IsPaused)
|
new XElement("IsPaused", a.IsPaused)
|
||||||
),
|
),
|
||||||
@@ -248,6 +249,7 @@ namespace AutoBidder
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// CSV EXPORT - AGGIORNATO
|
||||||
using var sw = new StreamWriter(path, false, Encoding.UTF8);
|
using var sw = new StreamWriter(path, false, Encoding.UTF8);
|
||||||
sw.WriteLine("Field,Value");
|
sw.WriteLine("Field,Value");
|
||||||
sw.WriteLine($"AuctionId,{a.AuctionId}");
|
sw.WriteLine($"AuctionId,{a.AuctionId}");
|
||||||
@@ -255,8 +257,8 @@ namespace AutoBidder
|
|||||||
sw.WriteLine($"OriginalUrl,\"{EscapeCsv(a.OriginalUrl)}\"");
|
sw.WriteLine($"OriginalUrl,\"{EscapeCsv(a.OriginalUrl)}\"");
|
||||||
sw.WriteLine($"MinPrice,{a.MinPrice}");
|
sw.WriteLine($"MinPrice,{a.MinPrice}");
|
||||||
sw.WriteLine($"MaxPrice,{a.MaxPrice}");
|
sw.WriteLine($"MaxPrice,{a.MaxPrice}");
|
||||||
sw.WriteLine($"TimerClick,{a.TimerClick}");
|
sw.WriteLine($"BidBeforeDeadlineMs,{a.BidBeforeDeadlineMs}");
|
||||||
sw.WriteLine($"DelayMs,{a.DelayMs}");
|
sw.WriteLine($"CheckAuctionOpenBeforeBid,{a.CheckAuctionOpenBeforeBid}");
|
||||||
sw.WriteLine($"IsActive,{a.IsActive}");
|
sw.WriteLine($"IsActive,{a.IsActive}");
|
||||||
sw.WriteLine($"IsPaused,{a.IsPaused}");
|
sw.WriteLine($"IsPaused,{a.IsPaused}");
|
||||||
sw.WriteLine();
|
sw.WriteLine();
|
||||||
|
|||||||
@@ -10,44 +10,42 @@ namespace AutoBidder
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class MainWindow
|
public partial class MainWindow
|
||||||
{
|
{
|
||||||
private void SaveCookieButton_Click(object sender, RoutedEventArgs e)
|
private async void SaveCookieButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var cookie = SettingsCookieTextBox.Text?.Trim();
|
var cookie = SettingsCookieTextBox.Text?.Trim();
|
||||||
if (string.IsNullOrEmpty(cookie))
|
if (string.IsNullOrEmpty(cookie))
|
||||||
{
|
{
|
||||||
MessageBox.Show(this, "Inserisci un cookie valido", "Errore", MessageBoxButton.OK, MessageBoxImage.Warning);
|
// Silenzioso - nessun MessageBox
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_auctionMonitor.InitializeSessionWithCookie(cookie, string.Empty);
|
_auctionMonitor.InitializeSessionWithCookie(cookie, string.Empty);
|
||||||
var success = _auctionMonitor.UpdateUserInfoAsync().GetAwaiter().GetResult();
|
var success = await _auctionMonitor.UpdateUserInfoAsync();
|
||||||
var session = _auctionMonitor.GetSession();
|
var session = _auctionMonitor.GetSession();
|
||||||
|
|
||||||
if (success && session != null)
|
if (success && session != null)
|
||||||
{
|
{
|
||||||
Services.SessionManager.SaveSession(session);
|
Services.SessionManager.SaveSession(session);
|
||||||
SetUserBanner(session.Username ?? string.Empty, session.RemainingBids);
|
SetUserBanner(session.Username ?? string.Empty, session.RemainingBids);
|
||||||
UsernameText.Text = session.Username ?? string.Empty;
|
|
||||||
StartButton.IsEnabled = true;
|
StartButton.IsEnabled = true;
|
||||||
Log($"[OK] Sessione salvata per: {session.Username}");
|
Log($"[OK] Sessione salvata per: {session.Username}");
|
||||||
MessageBox.Show(this, $"Cookie valido!\nUtente: {session.Username}\nPuntate disponibili: {session.RemainingBids}", "Sessione Salvata", MessageBoxButton.OK, MessageBoxImage.Information);
|
// Rimosso MessageBox - verrà mostrato dal chiamante
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log($"[WARN] Cookie non valido o scaduto", LogLevel.Warn);
|
Log($"[WARN] Cookie non valido o scaduto", LogLevel.Warn);
|
||||||
MessageBox.Show(this, "Cookie non valido o scaduto.\nVerifica che il cookie sia corretto.", "Errore Cookie", MessageBoxButton.OK, MessageBoxImage.Warning);
|
// Rimosso MessageBox - verrà mostrato dal chiamante se necessario
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log($"[ERRORE] Salvataggio cookie: {ex.Message}", LogLevel.Error);
|
Log($"[ERRORE] Salvataggio cookie: {ex.Message}", LogLevel.Error);
|
||||||
MessageBox.Show(this, "Errore durante salvataggio cookie: " + ex.Message, "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ImportCookieFromBrowserButton_Click(object sender, RoutedEventArgs e)
|
private async void ImportCookieFromBrowserButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -57,14 +55,14 @@ namespace AutoBidder
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var cookies = EmbeddedWebView.CoreWebView2.CookieManager.GetCookiesAsync("https://it.bidoo.com").GetAwaiter().GetResult();
|
var cookies = await EmbeddedWebView.CoreWebView2.CookieManager.GetCookiesAsync("https://it.bidoo.com");
|
||||||
var stattrb = cookies.FirstOrDefault(c => c.Name == "__stattrb");
|
var stattrb = cookies.FirstOrDefault(c => c.Name == "__stattrb");
|
||||||
|
|
||||||
if (stattrb != null)
|
if (stattrb != null)
|
||||||
{
|
{
|
||||||
SettingsCookieTextBox.Text = stattrb.Value;
|
SettingsCookieTextBox.Text = stattrb.Value;
|
||||||
Log("[OK] Cookie importato dal browser");
|
Log("[OK] Cookie importato dal browser");
|
||||||
MessageBox.Show(this, "Cookie importato con successo!\nClicca 'Salva Cookie' per confermare.", "Importa Cookie", MessageBoxButton.OK, MessageBoxImage.Information);
|
MessageBox.Show(this, "Cookie importato con successo!\nClicca 'Salva' per confermare.", "Importa Cookie", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -110,27 +108,118 @@ namespace AutoBidder
|
|||||||
|
|
||||||
SettingsManager.Save(s);
|
SettingsManager.Save(s);
|
||||||
ExportPreferences.SaveLastExportExtension(s.LastExportExt);
|
ExportPreferences.SaveLastExportExtension(s.LastExportExt);
|
||||||
MessageBox.Show(this, "Impostazioni salvate.", "Salva", MessageBoxButton.OK, MessageBoxImage.Information);
|
Log("[OK] Impostazioni export salvate", LogLevel.Success);
|
||||||
|
// Rimosso MessageBox - verrà mostrato dal chiamante
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
MessageBox.Show(this, "Errore salvataggio impostazioni: " + ex.Message, "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
|
Log($"[ERRORE] Salvataggio impostazioni export: {ex.Message}", LogLevel.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CancelSettingsButton_Click(object sender, RoutedEventArgs e)
|
private void CancelSettingsButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Ricarica impostazioni export
|
||||||
LoadExportSettings();
|
LoadExportSettings();
|
||||||
|
|
||||||
|
// Ricarica cookie salvato
|
||||||
|
var session = Services.SessionManager.LoadSession();
|
||||||
|
if (session != null && !string.IsNullOrEmpty(session.CookieString))
|
||||||
|
{
|
||||||
|
SettingsCookieTextBox.Text = session.CookieString;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SettingsCookieTextBox.Text = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log("[INFO] Impostazioni ripristinate", LogLevel.Info);
|
||||||
|
MessageBox.Show(this, "Impostazioni ripristinate alle ultime salvate.", "Annulla", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log($"[ERRORE] Ripristino impostazioni: {ex.Message}", LogLevel.Error);
|
||||||
|
MessageBox.Show(this, "Errore durante ripristino: " + ex.Message, "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SaveDefaultsButton_Click(object sender, RoutedEventArgs e)
|
private void SaveDefaultsButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
MessageBox.Show(this, "Funzionalità non ancora implementata", "Info", MessageBoxButton.OK, MessageBoxImage.Information);
|
try
|
||||||
|
{
|
||||||
|
// Salva impostazioni predefinite aste
|
||||||
|
if (int.TryParse(DefaultBidBeforeDeadlineMs.Text, out var bidMs) && bidMs >= 0 && bidMs <= 5000)
|
||||||
|
{
|
||||||
|
var settings = Utilities.SettingsManager.Load() ?? new Utilities.AppSettings();
|
||||||
|
settings.DefaultBidBeforeDeadlineMs = bidMs;
|
||||||
|
settings.DefaultCheckAuctionOpenBeforeBid = DefaultCheckAuctionOpen.IsChecked ?? false;
|
||||||
|
|
||||||
|
if (double.TryParse(DefaultMinPrice.Text.Replace(',', '.'), System.Globalization.NumberStyles.Any,
|
||||||
|
System.Globalization.CultureInfo.InvariantCulture, out var minPrice))
|
||||||
|
{
|
||||||
|
settings.DefaultMinPrice = minPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (double.TryParse(DefaultMaxPrice.Text.Replace(',', '.'), System.Globalization.NumberStyles.Any,
|
||||||
|
System.Globalization.CultureInfo.InvariantCulture, out var maxPrice))
|
||||||
|
{
|
||||||
|
settings.DefaultMaxPrice = maxPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (int.TryParse(DefaultMaxClicks.Text, out var maxClicks))
|
||||||
|
{
|
||||||
|
settings.DefaultMaxClicks = maxClicks;
|
||||||
|
}
|
||||||
|
|
||||||
|
Utilities.SettingsManager.Save(settings);
|
||||||
|
Log("[OK] Impostazioni predefinite aste salvate", LogLevel.Success);
|
||||||
|
// Rimosso MessageBox - verrà mostrato dal chiamante
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log("[WARN] Valore anticipo puntata non valido", LogLevel.Warn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log($"[ERRORE] Salvataggio defaults: {ex.Message}", LogLevel.Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CancelDefaultsButton_Click(object sender, RoutedEventArgs e)
|
private void CancelDefaultsButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
MessageBox.Show(this, "Funzionalità non ancora implementata", "Info", MessageBoxButton.OK, MessageBoxImage.Information);
|
try
|
||||||
|
{
|
||||||
|
// Ricarica defaults salvati
|
||||||
|
var settings = Utilities.SettingsManager.Load();
|
||||||
|
if (settings != null)
|
||||||
|
{
|
||||||
|
DefaultBidBeforeDeadlineMs.Text = settings.DefaultBidBeforeDeadlineMs.ToString();
|
||||||
|
DefaultCheckAuctionOpen.IsChecked = settings.DefaultCheckAuctionOpenBeforeBid;
|
||||||
|
DefaultMinPrice.Text = settings.DefaultMinPrice.ToString("F2");
|
||||||
|
DefaultMaxPrice.Text = settings.DefaultMaxPrice.ToString("F2");
|
||||||
|
DefaultMaxClicks.Text = settings.DefaultMaxClicks.ToString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Valori di default se non ci sono impostazioni salvate
|
||||||
|
DefaultBidBeforeDeadlineMs.Text = "200";
|
||||||
|
DefaultCheckAuctionOpen.IsChecked = false;
|
||||||
|
DefaultMinPrice.Text = "0";
|
||||||
|
DefaultMaxPrice.Text = "0";
|
||||||
|
DefaultMaxClicks.Text = "0";
|
||||||
|
}
|
||||||
|
|
||||||
|
Log("[INFO] Impostazioni predefinite ripristinate", LogLevel.Info);
|
||||||
|
MessageBox.Show(this, "Impostazioni predefinite ripristinate.", "Annulla", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log($"[ERRORE] Ripristino defaults: {ex.Message}", LogLevel.Error);
|
||||||
|
MessageBox.Show(this, "Errore durante ripristino: " + ex.Message, "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,15 +60,16 @@ namespace AutoBidder
|
|||||||
? $"Asta {auctionId}"
|
? $"Asta {auctionId}"
|
||||||
: $"{System.Net.WebUtility.HtmlDecode(productName)} ({auctionId})";
|
: $"{System.Net.WebUtility.HtmlDecode(productName)} ({auctionId})";
|
||||||
|
|
||||||
// Crea model
|
// Crea model con NUOVI CAMPI - ASTA STOPPATA ALL'INIZIO
|
||||||
var auction = new AuctionInfo
|
var auction = new AuctionInfo
|
||||||
{
|
{
|
||||||
AuctionId = auctionId,
|
AuctionId = auctionId,
|
||||||
Name = System.Net.WebUtility.HtmlDecode(displayName),
|
Name = System.Net.WebUtility.HtmlDecode(displayName),
|
||||||
OriginalUrl = originalUrl,
|
OriginalUrl = originalUrl,
|
||||||
TimerClick = 0,
|
BidBeforeDeadlineMs = 200,
|
||||||
DelayMs = 50,
|
CheckAuctionOpenBeforeBid = false,
|
||||||
IsActive = true
|
IsActive = false, // STOPPATA
|
||||||
|
IsPaused = false
|
||||||
};
|
};
|
||||||
|
|
||||||
// Aggiungi al monitor
|
// Aggiungi al monitor
|
||||||
@@ -80,6 +81,7 @@ namespace AutoBidder
|
|||||||
|
|
||||||
SaveAuctions();
|
SaveAuctions();
|
||||||
UpdateTotalCount();
|
UpdateTotalCount();
|
||||||
|
UpdateGlobalControlButtons(); // Aggiorna stato pulsanti globali
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -126,14 +128,16 @@ namespace AutoBidder
|
|||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
|
|
||||||
// Crea model
|
// Crea model con NUOVI CAMPI - ASTA STOPPATA ALL'INIZIO
|
||||||
var auction = new AuctionInfo
|
var auction = new AuctionInfo
|
||||||
{
|
{
|
||||||
AuctionId = auctionId,
|
AuctionId = auctionId,
|
||||||
Name = System.Net.WebUtility.HtmlDecode(name),
|
Name = System.Net.WebUtility.HtmlDecode(name),
|
||||||
TimerClick = 0,
|
OriginalUrl = url,
|
||||||
DelayMs = 50,
|
BidBeforeDeadlineMs = 200,
|
||||||
IsActive = true
|
CheckAuctionOpenBeforeBid = false,
|
||||||
|
IsActive = false, // STOPPATA
|
||||||
|
IsPaused = false
|
||||||
};
|
};
|
||||||
|
|
||||||
// Aggiungi al monitor
|
// Aggiungi al monitor
|
||||||
@@ -145,6 +149,7 @@ namespace AutoBidder
|
|||||||
|
|
||||||
SaveAuctions();
|
SaveAuctions();
|
||||||
UpdateTotalCount();
|
UpdateTotalCount();
|
||||||
|
UpdateGlobalControlButtons(); // Aggiorna stato pulsanti globali
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -192,6 +197,8 @@ namespace AutoBidder
|
|||||||
}
|
}
|
||||||
|
|
||||||
UpdateTotalCount();
|
UpdateTotalCount();
|
||||||
|
UpdateGlobalControlButtons(); // Aggiorna stato pulsanti dopo caricamento
|
||||||
|
|
||||||
if (auctions.Count > 0)
|
if (auctions.Count > 0)
|
||||||
{
|
{
|
||||||
Log($"[OK] Caricate {auctions.Count} aste salvate");
|
Log($"[OK] Caricate {auctions.Count} aste salvate");
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
@@ -20,18 +20,13 @@ namespace AutoBidder
|
|||||||
{
|
{
|
||||||
if (!_isAutomationActive)
|
if (!_isAutomationActive)
|
||||||
{
|
{
|
||||||
foreach (var vm in _auctionViewModels)
|
// Avvia il monitoraggio globale
|
||||||
{
|
|
||||||
vm.IsActive = true;
|
|
||||||
vm.IsPaused = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_auctionMonitor.Start();
|
_auctionMonitor.Start();
|
||||||
_isAutomationActive = true;
|
_isAutomationActive = true;
|
||||||
Log("Monitoraggio avviato!", LogLevel.Success);
|
Log("[START] Monitoraggio avviato!", LogLevel.Success);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
// Attiva e riprendi tutte le aste
|
||||||
foreach (var vm in _auctionViewModels)
|
foreach (var vm in _auctionViewModels)
|
||||||
{
|
{
|
||||||
if (!vm.IsActive)
|
if (!vm.IsActive)
|
||||||
@@ -40,14 +35,17 @@ namespace AutoBidder
|
|||||||
}
|
}
|
||||||
vm.IsPaused = false;
|
vm.IsPaused = false;
|
||||||
}
|
}
|
||||||
Log("Avviate/riprese le aste (tutte)", LogLevel.Info);
|
|
||||||
|
if (sender != null) // Solo se chiamato dall'utente, non internamente
|
||||||
|
{
|
||||||
|
Log("[START ALL] Tutte le aste avviate/riprese", LogLevel.Info);
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateGlobalControlButtons();
|
UpdateGlobalControlButtons();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log($"Errore avvio: {ex.Message}", LogLevel.Error);
|
Log($"[ERRORE] Avvio: {ex.Message}", LogLevel.Error);
|
||||||
MessageBox.Show($"Errore: {ex.Message}", "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show($"Errore: {ex.Message}", "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,23 +54,29 @@ namespace AutoBidder
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Ferma tutte le aste
|
||||||
|
foreach (var vm in _auctionViewModels)
|
||||||
|
{
|
||||||
|
vm.IsActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ferma il monitoraggio globale
|
||||||
if (_isAutomationActive)
|
if (_isAutomationActive)
|
||||||
{
|
{
|
||||||
_auctionMonitor.Stop();
|
_auctionMonitor.Stop();
|
||||||
_isAutomationActive = false;
|
_isAutomationActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var vm in _auctionViewModels)
|
|
||||||
{
|
|
||||||
vm.IsActive = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateGlobalControlButtons();
|
UpdateGlobalControlButtons();
|
||||||
Log("[STOP] Automazione fermata e aste arrestate", LogLevel.Warn);
|
|
||||||
|
if (sender != null) // Solo se chiamato dall'utente
|
||||||
|
{
|
||||||
|
Log("[STOP ALL] Monitoraggio fermato e tutte le aste arrestate", LogLevel.Warn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log($"[STOP ERROR] {ex.Message}", LogLevel.Error);
|
Log($"[ERRORE STOP] {ex.Message}", LogLevel.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +89,11 @@ namespace AutoBidder
|
|||||||
vm.IsPaused = true;
|
vm.IsPaused = true;
|
||||||
}
|
}
|
||||||
UpdateGlobalControlButtons();
|
UpdateGlobalControlButtons();
|
||||||
Log("[PAUSA] Tutte le aste in pausa", LogLevel.Warn);
|
|
||||||
|
if (sender != null) // Solo se chiamato dall'utente
|
||||||
|
{
|
||||||
|
Log("[PAUSE ALL] Tutte le aste in pausa", LogLevel.Warn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -161,15 +169,45 @@ namespace AutoBidder
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// La conferma è già gestita in AuctionMonitorControl per il tasto Canc
|
var auctionName = _selectedAuction.Name;
|
||||||
// Qui facciamo la rimozione diretta per il bottone
|
var auctionId = _selectedAuction.AuctionId;
|
||||||
_auctionMonitor.RemoveAuction(_selectedAuction.AuctionId);
|
|
||||||
|
// Conferma rimozione
|
||||||
|
var result = MessageBox.Show(
|
||||||
|
$"Rimuovere l'asta dal monitoraggio?\n\n{auctionName}\n(ID: {auctionId})\n\nL'asta verrà eliminata dalla lista e non sarà più monitorata.",
|
||||||
|
"Conferma Rimozione",
|
||||||
|
MessageBoxButton.YesNo,
|
||||||
|
MessageBoxImage.Question);
|
||||||
|
|
||||||
|
if (result != MessageBoxResult.Yes)
|
||||||
|
{
|
||||||
|
Log($"[REMOVE] Rimozione annullata: {auctionName}", LogLevel.Info);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Rimuove dal monitor
|
||||||
|
_auctionMonitor.RemoveAuction(auctionId);
|
||||||
|
|
||||||
|
// Rimuove dal ViewModel
|
||||||
_auctionViewModels.Remove(_selectedAuction);
|
_auctionViewModels.Remove(_selectedAuction);
|
||||||
|
|
||||||
|
// Reset selezione
|
||||||
_selectedAuction = null;
|
_selectedAuction = null;
|
||||||
|
|
||||||
|
// Salva modifiche
|
||||||
SaveAuctions();
|
SaveAuctions();
|
||||||
UpdateTotalCount();
|
UpdateTotalCount();
|
||||||
Log("Asta rimossa", LogLevel.Info);
|
UpdateGlobalControlButtons();
|
||||||
|
|
||||||
|
Log($"[REMOVE] Asta rimossa: {auctionName} (ID: {auctionId})", LogLevel.Success);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log($"[ERROR] Errore rimozione asta: {ex.Message}", LogLevel.Error);
|
||||||
|
MessageBox.Show($"Errore durante la rimozione:\n{ex.Message}", "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ResetSettingsButton_Click(object sender, RoutedEventArgs e)
|
private void ResetSettingsButton_Click(object sender, RoutedEventArgs e)
|
||||||
@@ -184,8 +222,8 @@ namespace AutoBidder
|
|||||||
|
|
||||||
if (result == MessageBoxResult.Yes)
|
if (result == MessageBoxResult.Yes)
|
||||||
{
|
{
|
||||||
_selectedAuction.TimerClick = 0;
|
_selectedAuction.AuctionInfo.BidBeforeDeadlineMs = 200;
|
||||||
_selectedAuction.AuctionInfo.DelayMs = 50;
|
_selectedAuction.AuctionInfo.CheckAuctionOpenBeforeBid = false;
|
||||||
_selectedAuction.MinPrice = 0;
|
_selectedAuction.MinPrice = 0;
|
||||||
_selectedAuction.MaxPrice = 0;
|
_selectedAuction.MaxPrice = 0;
|
||||||
_selectedAuction.MaxClicks = 0;
|
_selectedAuction.MaxClicks = 0;
|
||||||
@@ -249,61 +287,108 @@ namespace AutoBidder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SelectedTimerClick_TextChanged(object sender, TextChangedEventArgs e)
|
private void SelectedBidBeforeDeadlineMs_TextChanged(object sender, TextChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (_selectedAuction != null && sender is TextBox tb)
|
if (_selectedAuction == null) return;
|
||||||
|
|
||||||
|
if (sender is TextBox tb && int.TryParse(tb.Text, out var value) && value >= 0 && value <= 5000)
|
||||||
{
|
{
|
||||||
if (int.TryParse(tb.Text, out var value) && value >= 0 && value <= 8)
|
var oldValue = _selectedAuction.AuctionInfo.BidBeforeDeadlineMs;
|
||||||
|
_selectedAuction.AuctionInfo.BidBeforeDeadlineMs = value;
|
||||||
|
|
||||||
|
// Log solo se non stiamo caricando E il valore è cambiato
|
||||||
|
if (!_isUpdatingSelection && oldValue != value)
|
||||||
{
|
{
|
||||||
_selectedAuction.TimerClick = value;
|
_selectedAuction.AuctionInfo.AddLog($"[SETTINGS] Anticipo puntata: {oldValue}ms → {value}ms");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Salva sempre (anche durante caricamento iniziale non fa male)
|
||||||
|
SaveAuctions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SelectedDelayMs_TextChanged(object sender, TextChangedEventArgs e)
|
private void SelectedCheckAuctionOpen_Changed(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (_selectedAuction != null && sender is TextBox tb)
|
if (_selectedAuction == null) return;
|
||||||
|
|
||||||
|
if (sender is System.Windows.Controls.Primitives.ToggleButton cb)
|
||||||
{
|
{
|
||||||
if (int.TryParse(tb.Text, out var value) && value >= 0)
|
var oldValue = _selectedAuction.AuctionInfo.CheckAuctionOpenBeforeBid;
|
||||||
|
var newValue = cb.IsChecked ?? false;
|
||||||
|
_selectedAuction.AuctionInfo.CheckAuctionOpenBeforeBid = newValue;
|
||||||
|
|
||||||
|
// Log solo se non stiamo caricando E il valore è cambiato
|
||||||
|
if (!_isUpdatingSelection && oldValue != newValue)
|
||||||
{
|
{
|
||||||
_selectedAuction.AuctionInfo.DelayMs = value;
|
_selectedAuction.AuctionInfo.AddLog($"[SETTINGS] Verifica stato asta: {(newValue ? "ON" : "OFF")}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SaveAuctions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SelectedMinPrice_TextChanged(object sender, TextChangedEventArgs e)
|
private void SelectedMinPrice_TextChanged(object sender, TextChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (_selectedAuction != null && sender is TextBox tb)
|
if (_selectedAuction == null) return;
|
||||||
|
|
||||||
|
if (sender is TextBox tb)
|
||||||
{
|
{
|
||||||
if (double.TryParse(tb.Text.Replace(',', '.'), System.Globalization.NumberStyles.Any,
|
if (double.TryParse(tb.Text.Replace(',', '.'), System.Globalization.NumberStyles.Any,
|
||||||
System.Globalization.CultureInfo.InvariantCulture, out var value))
|
System.Globalization.CultureInfo.InvariantCulture, out var value))
|
||||||
{
|
{
|
||||||
|
var oldValue = _selectedAuction.MinPrice;
|
||||||
_selectedAuction.MinPrice = value;
|
_selectedAuction.MinPrice = value;
|
||||||
|
|
||||||
|
// Log solo se non stiamo caricando E il valore è cambiato
|
||||||
|
if (!_isUpdatingSelection && Math.Abs(oldValue - value) > 0.01)
|
||||||
|
{
|
||||||
|
_selectedAuction.AuctionInfo.AddLog($"[SETTINGS] Prezzo minimo: €{oldValue:F2} → €{value:F2}");
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveAuctions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SelectedMaxPrice_TextChanged(object sender, TextChangedEventArgs e)
|
private void SelectedMaxPrice_TextChanged(object sender, TextChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (_selectedAuction != null && sender is TextBox tb)
|
if (_selectedAuction == null) return;
|
||||||
|
|
||||||
|
if (sender is TextBox tb)
|
||||||
{
|
{
|
||||||
if (double.TryParse(tb.Text.Replace(',', '.'), System.Globalization.NumberStyles.Any,
|
if (double.TryParse(tb.Text.Replace(',', '.'), System.Globalization.NumberStyles.Any,
|
||||||
System.Globalization.CultureInfo.InvariantCulture, out var value))
|
System.Globalization.CultureInfo.InvariantCulture, out var value))
|
||||||
{
|
{
|
||||||
|
var oldValue = _selectedAuction.MaxPrice;
|
||||||
_selectedAuction.MaxPrice = value;
|
_selectedAuction.MaxPrice = value;
|
||||||
|
|
||||||
|
// Log solo se non stiamo caricando E il valore è cambiato
|
||||||
|
if (!_isUpdatingSelection && Math.Abs(oldValue - value) > 0.01)
|
||||||
|
{
|
||||||
|
_selectedAuction.AuctionInfo.AddLog($"[SETTINGS] Prezzo massimo: €{oldValue:F2} → €{value:F2}");
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveAuctions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SelectedMaxClicks_TextChanged(object sender, TextChangedEventArgs e)
|
private void SelectedMaxClicks_TextChanged(object sender, TextChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (_selectedAuction != null && sender is TextBox tb)
|
if (_selectedAuction == null) return;
|
||||||
{
|
|
||||||
if (int.TryParse(tb.Text, out var value) && value >= 0)
|
if (sender is TextBox tb && int.TryParse(tb.Text, out var value) && value >= 0)
|
||||||
{
|
{
|
||||||
|
var oldValue = _selectedAuction.MaxClicks;
|
||||||
_selectedAuction.MaxClicks = value;
|
_selectedAuction.MaxClicks = value;
|
||||||
SaveAuctions();
|
|
||||||
|
// Log solo se non stiamo caricando E il valore è cambiato
|
||||||
|
if (!_isUpdatingSelection && oldValue != value)
|
||||||
|
{
|
||||||
|
_selectedAuction.AuctionInfo.AddLog($"[SETTINGS] Max clicks: {oldValue} → {value}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SaveAuctions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,12 +405,12 @@ namespace AutoBidder
|
|||||||
MessageBox.Show(
|
MessageBox.Show(
|
||||||
$"Export Massivo di {_auctionViewModels.Count} aste.\n\n" +
|
$"Export Massivo di {_auctionViewModels.Count} aste.\n\n" +
|
||||||
"Per configurare le opzioni di export, vai nella scheda Impostazioni.\n\n" +
|
"Per configurare le opzioni di export, vai nella scheda Impostazioni.\n\n" +
|
||||||
"Nota: Questa funzionalità verrà completata nelle prossime versioni.",
|
"Nota: Questa funzionalità verrà completata nelle prossime versioni.",
|
||||||
"Export Aste",
|
"Export Aste",
|
||||||
MessageBoxButton.OK,
|
MessageBoxButton.OK,
|
||||||
MessageBoxImage.Information);
|
MessageBoxImage.Information);
|
||||||
|
|
||||||
Log($"[EXPORT] Richiesto export per {_auctionViewModels.Count} aste (funzionalità in sviluppo)", LogLevel.Info);
|
Log($"[EXPORT] Richiesto export per {_auctionViewModels.Count} aste (funzionalità in sviluppo)", LogLevel.Info);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using AutoBidder.Utilities;
|
||||||
using AutoBidder.ViewModels;
|
using AutoBidder.ViewModels;
|
||||||
|
|
||||||
namespace AutoBidder
|
namespace AutoBidder
|
||||||
@@ -11,14 +13,14 @@ namespace AutoBidder
|
|||||||
{
|
{
|
||||||
private void InitializeCommands()
|
private void InitializeCommands()
|
||||||
{
|
{
|
||||||
StartAllCommand = new Utilities.RelayCommand(_ => ExecuteStartAll());
|
StartAllCommand = new RelayCommand(_ => ExecuteStartAll());
|
||||||
StopAllCommand = new Utilities.RelayCommand(_ => ExecuteStopAll());
|
StopAllCommand = new RelayCommand(_ => ExecuteStopAll());
|
||||||
PauseAllCommand = new Utilities.RelayCommand(_ => ExecutePauseAll());
|
PauseAllCommand = new RelayCommand(_ => ExecutePauseAll());
|
||||||
|
|
||||||
GridStartCommand = new Utilities.RelayCommand(param => ExecuteGridStart(param as AuctionViewModel));
|
GridStartCommand = new RelayCommand(param => ExecuteGridStart(param as AuctionViewModel));
|
||||||
GridPauseCommand = new Utilities.RelayCommand(param => ExecuteGridPause(param as AuctionViewModel));
|
GridPauseCommand = new RelayCommand(param => ExecuteGridPause(param as AuctionViewModel));
|
||||||
GridStopCommand = new Utilities.RelayCommand(param => ExecuteGridStop(param as AuctionViewModel));
|
GridStopCommand = new RelayCommand(param => ExecuteGridStop(param as AuctionViewModel));
|
||||||
GridBidCommand = new Utilities.RelayCommand(async param => await ExecuteGridBidAsync(param as AuctionViewModel));
|
GridBidCommand = new RelayCommand(async param => await ExecuteGridBidAsync(param as AuctionViewModel));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExecuteStartAll()
|
private void ExecuteStartAll()
|
||||||
@@ -39,9 +41,23 @@ namespace AutoBidder
|
|||||||
private void ExecuteGridStart(AuctionViewModel? vm)
|
private void ExecuteGridStart(AuctionViewModel? vm)
|
||||||
{
|
{
|
||||||
if (vm == null) return;
|
if (vm == null) return;
|
||||||
|
|
||||||
|
// Attiva l'asta
|
||||||
vm.IsActive = true;
|
vm.IsActive = true;
|
||||||
vm.IsPaused = false;
|
vm.IsPaused = false;
|
||||||
Log($"[START] Asta avviata: {vm.Name}");
|
|
||||||
|
// Se il monitoraggio globale non è attivo, avvialo automaticamente
|
||||||
|
if (!_isAutomationActive)
|
||||||
|
{
|
||||||
|
_auctionMonitor.Start();
|
||||||
|
_isAutomationActive = true;
|
||||||
|
Log($"[AUTO-START] Monitoraggio avviato automaticamente per asta: {vm.Name}", LogLevel.Info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log($"[START] Asta avviata: {vm.Name}", LogLevel.Info);
|
||||||
|
}
|
||||||
|
|
||||||
UpdateGlobalControlButtons();
|
UpdateGlobalControlButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +65,7 @@ namespace AutoBidder
|
|||||||
{
|
{
|
||||||
if (vm == null) return;
|
if (vm == null) return;
|
||||||
vm.IsPaused = true;
|
vm.IsPaused = true;
|
||||||
Log($"[PAUSA] Asta in pausa: {vm.Name}");
|
Log($"[PAUSA] Asta in pausa: {vm.Name}", LogLevel.Info);
|
||||||
UpdateGlobalControlButtons();
|
UpdateGlobalControlButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +73,20 @@ namespace AutoBidder
|
|||||||
{
|
{
|
||||||
if (vm == null) return;
|
if (vm == null) return;
|
||||||
vm.IsActive = false;
|
vm.IsActive = false;
|
||||||
Log($"[STOP] Asta fermata: {vm.Name}");
|
|
||||||
|
// Se tutte le aste sono fermate, ferma anche il monitoraggio globale
|
||||||
|
bool hasActiveAuctions = _auctionViewModels.Any(a => a.IsActive);
|
||||||
|
if (!hasActiveAuctions && _isAutomationActive)
|
||||||
|
{
|
||||||
|
_auctionMonitor.Stop();
|
||||||
|
_isAutomationActive = false;
|
||||||
|
Log($"[AUTO-STOP] Monitoraggio fermato: nessuna asta attiva", LogLevel.Info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log($"[STOP] Asta fermata: {vm.Name}", LogLevel.Info);
|
||||||
|
}
|
||||||
|
|
||||||
UpdateGlobalControlButtons();
|
UpdateGlobalControlButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,16 +95,16 @@ namespace AutoBidder
|
|||||||
if (vm == null) return;
|
if (vm == null) return;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Log($"[BID] Puntata manuale richiesta su: {vm.Name}");
|
Log($"[BID] Puntata manuale richiesta su: {vm.Name}", LogLevel.Info);
|
||||||
var result = await _auctionMonitor.PlaceManualBidAsync(vm.AuctionInfo);
|
var result = await _auctionMonitor.PlaceManualBidAsync(vm.AuctionInfo);
|
||||||
if (result.Success)
|
if (result.Success)
|
||||||
Log($"[OK] Puntata manuale su {vm.Name}: {result.LatencyMs}ms");
|
Log($"[OK] Puntata manuale su {vm.Name}: {result.LatencyMs}ms", LogLevel.Success);
|
||||||
else
|
else
|
||||||
Log($"[FAIL] Puntata manuale su {vm.Name}: {result.Error}");
|
Log($"[FAIL] Puntata manuale su {vm.Name}: {result.Error}", LogLevel.Error);
|
||||||
}
|
}
|
||||||
catch (System.Exception ex)
|
catch (System.Exception ex)
|
||||||
{
|
{
|
||||||
Log($"[ERRORE] Puntata manuale: {ex.Message}");
|
Log($"[ERRORE] Puntata manuale: {ex.Message}", LogLevel.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,6 +118,33 @@ namespace AutoBidder
|
|||||||
ClearGlobalLogButton_Click(sender, e);
|
ClearGlobalLogButton_Click(sender, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== AUCTION SETTINGS EVENTS =====
|
||||||
|
|
||||||
|
private void AuctionMonitor_BidBeforeDeadlineMsChanged(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
SelectedBidBeforeDeadlineMs_TextChanged(AuctionMonitor.SelectedBidBeforeDeadlineMs, new System.Windows.Controls.TextChangedEventArgs(e.RoutedEvent, System.Windows.Controls.UndoAction.None));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AuctionMonitor_CheckAuctionOpenChanged(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
SelectedCheckAuctionOpen_Changed(AuctionMonitor.SelectedCheckAuctionOpen, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AuctionMonitor_MinPriceChanged(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
SelectedMinPrice_TextChanged(AuctionMonitor.SelectedMinPrice, new System.Windows.Controls.TextChangedEventArgs(e.RoutedEvent, System.Windows.Controls.UndoAction.None));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AuctionMonitor_MaxPriceChanged(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
SelectedMaxPrice_TextChanged(AuctionMonitor.SelectedMaxPrice, new System.Windows.Controls.TextChangedEventArgs(e.RoutedEvent, System.Windows.Controls.UndoAction.None));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AuctionMonitor_MaxClicksChanged(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
SelectedMaxClicks_TextChanged(AuctionMonitor.SelectedMaxClicks, new System.Windows.Controls.TextChangedEventArgs(e.RoutedEvent, System.Windows.Controls.UndoAction.None));
|
||||||
|
}
|
||||||
|
|
||||||
// ===== BROWSER CONTROL EVENTS =====
|
// ===== BROWSER CONTROL EVENTS =====
|
||||||
|
|
||||||
private void Browser_BrowserBackClicked(object sender, RoutedEventArgs e)
|
private void Browser_BrowserBackClicked(object sender, RoutedEventArgs e)
|
||||||
|
|||||||
@@ -17,11 +17,14 @@ namespace AutoBidder
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Blocca temporaneamente i TextChanged per evitare loop di aggiornamento
|
||||||
|
_isUpdatingSelection = true;
|
||||||
|
|
||||||
SelectedAuctionName.Text = auction.Name;
|
SelectedAuctionName.Text = auction.Name;
|
||||||
SelectedTimerClick.Text = auction.TimerClick.ToString();
|
SelectedBidBeforeDeadlineMs.Text = auction.AuctionInfo.BidBeforeDeadlineMs.ToString();
|
||||||
SelectedDelayMs.Text = auction.AuctionInfo.DelayMs.ToString();
|
SelectedCheckAuctionOpen.IsChecked = auction.AuctionInfo.CheckAuctionOpenBeforeBid;
|
||||||
SelectedMinPrice.Text = auction.MinPrice.ToString();
|
SelectedMinPrice.Text = auction.MinPrice.ToString("F2", System.Globalization.CultureInfo.InvariantCulture);
|
||||||
SelectedMaxPrice.Text = auction.MaxPrice.ToString();
|
SelectedMaxPrice.Text = auction.MaxPrice.ToString("F2", System.Globalization.CultureInfo.InvariantCulture);
|
||||||
SelectedMaxClicks.Text = auction.MaxClicks.ToString();
|
SelectedMaxClicks.Text = auction.MaxClicks.ToString();
|
||||||
|
|
||||||
var url = auction.AuctionInfo.OriginalUrl;
|
var url = auction.AuctionInfo.OriginalUrl;
|
||||||
@@ -35,8 +38,13 @@ namespace AutoBidder
|
|||||||
|
|
||||||
UpdateAuctionLog(auction);
|
UpdateAuctionLog(auction);
|
||||||
RefreshBiddersGrid(auction);
|
RefreshBiddersGrid(auction);
|
||||||
|
|
||||||
|
_isUpdatingSelection = false;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_isUpdatingSelection = false;
|
||||||
}
|
}
|
||||||
catch { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateAuctionLog(AuctionViewModel auction)
|
private void UpdateAuctionLog(AuctionViewModel auction)
|
||||||
@@ -106,12 +114,38 @@ namespace AutoBidder
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var hasAuctions = _auctionViewModels.Count > 0;
|
var hasAuctions = _auctionViewModels.Count > 0;
|
||||||
var anyActive = _auctionViewModels.Any(a => a.IsActive);
|
|
||||||
var anyPaused = _auctionViewModels.Any(a => a.IsPaused);
|
|
||||||
|
|
||||||
StartButton.IsEnabled = hasAuctions;
|
if (!hasAuctions)
|
||||||
StopButton.IsEnabled = hasAuctions && (_isAutomationActive || anyActive);
|
{
|
||||||
PauseAllButton.IsEnabled = hasAuctions && anyActive && !anyPaused;
|
// Nessuna asta: tutti disabilitati
|
||||||
|
StartButton.IsEnabled = false;
|
||||||
|
StartButton.Opacity = 0.4;
|
||||||
|
PauseAllButton.IsEnabled = false;
|
||||||
|
PauseAllButton.Opacity = 0.4;
|
||||||
|
StopButton.IsEnabled = false;
|
||||||
|
StopButton.Opacity = 0.4;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conta quante aste possono eseguire ogni azione
|
||||||
|
int canStartCount = _auctionViewModels.Count(a => a.CanStart);
|
||||||
|
int canPauseCount = _auctionViewModels.Count(a => a.CanPause);
|
||||||
|
int canStopCount = _auctionViewModels.Count(a => a.CanStop);
|
||||||
|
|
||||||
|
// AVVIA TUTTI: abilitato se ALMENO UNA asta può essere avviata
|
||||||
|
// Scuro se NESSUNA asta può essere avviata (tutte già avviate)
|
||||||
|
StartButton.IsEnabled = canStartCount > 0;
|
||||||
|
StartButton.Opacity = canStartCount > 0 ? 1.0 : 0.4;
|
||||||
|
|
||||||
|
// PAUSA TUTTI: abilitato se ALMENO UNA asta può essere messa in pausa
|
||||||
|
// Scuro se NESSUNA asta può essere messa in pausa (tutte già in pausa o ferme)
|
||||||
|
PauseAllButton.IsEnabled = canPauseCount > 0;
|
||||||
|
PauseAllButton.Opacity = canPauseCount > 0 ? 1.0 : 0.4;
|
||||||
|
|
||||||
|
// FERMA TUTTI: abilitato se ALMENO UNA asta può essere fermata
|
||||||
|
// Scuro se NESSUNA asta può essere fermata (tutte già ferme)
|
||||||
|
StopButton.IsEnabled = canStopCount > 0;
|
||||||
|
StopButton.Opacity = canStopCount > 0 ? 1.0 : 0.4;
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Linq;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using AutoBidder.Services;
|
using AutoBidder.Services;
|
||||||
|
using AutoBidder.Utilities;
|
||||||
|
|
||||||
namespace AutoBidder
|
namespace AutoBidder
|
||||||
{
|
{
|
||||||
@@ -16,30 +17,83 @@ namespace AutoBidder
|
|||||||
|
|
||||||
private void InitializeUserInfoTimers()
|
private void InitializeUserInfoTimers()
|
||||||
{
|
{
|
||||||
// Timer per aggiornamento banner utente ogni minuto
|
// Timer per aggiornamento dati utente da HTML ogni 5 minuti (PRINCIPALE)
|
||||||
|
_userHtmlTimer = new System.Windows.Threading.DispatcherTimer();
|
||||||
|
_userHtmlTimer.Interval = TimeSpan.FromMinutes(5);
|
||||||
|
_userHtmlTimer.Tick += UserHtmlTimer_Tick;
|
||||||
|
_userHtmlTimer.Start();
|
||||||
|
|
||||||
|
// Timer per aggiornamento banner API ogni 10 minuti (SECONDARIO - fallback)
|
||||||
_userBannerTimer = new System.Windows.Threading.DispatcherTimer();
|
_userBannerTimer = new System.Windows.Threading.DispatcherTimer();
|
||||||
_userBannerTimer.Interval = TimeSpan.FromMinutes(1);
|
_userBannerTimer.Interval = TimeSpan.FromMinutes(10);
|
||||||
_userBannerTimer.Tick += UserBannerTimer_Tick;
|
_userBannerTimer.Tick += UserBannerTimer_Tick;
|
||||||
_userBannerTimer.Start();
|
_userBannerTimer.Start();
|
||||||
|
|
||||||
// Timer per aggiornamento dati utente da HTML ogni 3 minuti
|
Log("[INFO] Timer info utente avviati (5min HTML principale, 10min API fallback)", LogLevel.Info);
|
||||||
_userHtmlTimer = new System.Windows.Threading.DispatcherTimer();
|
|
||||||
_userHtmlTimer.Interval = TimeSpan.FromMinutes(3);
|
|
||||||
_userHtmlTimer.Tick += UserHtmlTimer_Tick;
|
|
||||||
_userHtmlTimer.Start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetUserBanner(string username, int? remainingBids)
|
private void SetUserBanner(string username, int? remainingBids)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var session = _auctionMonitor.GetSession();
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(username))
|
if (!string.IsNullOrEmpty(username))
|
||||||
{
|
{
|
||||||
UsernameText.Text = $"{username} ({remainingBids ?? 0} puntate)";
|
// === HEADER - 2 RIGHE ===
|
||||||
|
// Riga 1: Puntate + Credito
|
||||||
|
RemainingBidsText.Text = remainingBids?.ToString() ?? "0";
|
||||||
|
|
||||||
|
if (session?.ShopCredit > 0)
|
||||||
|
{
|
||||||
|
AuctionMonitor.ShopCreditText.Text = $"EUR {session.ShopCredit:F2}";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UsernameText.Text = "Non connesso";
|
AuctionMonitor.ShopCreditText.Text = "EUR 0.00";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Riga 2: Aste vinte (TODO: implementare)
|
||||||
|
BannerAsteDaRiscattare.Text = "0";
|
||||||
|
|
||||||
|
// === SIDEBAR - Pannello Utente ===
|
||||||
|
// Username
|
||||||
|
SidebarUsernameText.Text = username;
|
||||||
|
|
||||||
|
// ID Utente
|
||||||
|
if (session?.UserId > 0)
|
||||||
|
{
|
||||||
|
SidebarUserIdText.Text = $"ID: {session.UserId}";
|
||||||
|
SidebarUserIdText.Visibility = System.Windows.Visibility.Visible;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SidebarUserIdText.Visibility = System.Windows.Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Email
|
||||||
|
if (!string.IsNullOrEmpty(session?.Email))
|
||||||
|
{
|
||||||
|
SidebarUserEmailText.Text = session.Email;
|
||||||
|
SidebarUserEmailText.Visibility = System.Windows.Visibility.Visible;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SidebarUserEmailText.Visibility = System.Windows.Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mostra il pannello sidebar
|
||||||
|
SidebarUserInfoPanel.Visibility = System.Windows.Visibility.Visible;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Nascondi pannello sidebar
|
||||||
|
SidebarUserInfoPanel.Visibility = System.Windows.Visibility.Collapsed;
|
||||||
|
|
||||||
|
// Reset header
|
||||||
|
RemainingBidsText.Text = "0";
|
||||||
|
AuctionMonitor.ShopCreditText.Text = "EUR 0.00";
|
||||||
|
BannerAsteDaRiscattare.Text = "0";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
@@ -47,11 +101,13 @@ namespace AutoBidder
|
|||||||
|
|
||||||
private async void UserBannerTimer_Tick(object? sender, EventArgs e)
|
private async void UserBannerTimer_Tick(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
// Questo è ora il fallback secondario
|
||||||
await UpdateUserBannerInfoAsync();
|
await UpdateUserBannerInfoAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void UserHtmlTimer_Tick(object? sender, EventArgs e)
|
private async void UserHtmlTimer_Tick(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
// Questo è ora il metodo principale
|
||||||
await UpdateUserHtmlInfoAsync();
|
await UpdateUserHtmlInfoAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,16 +115,48 @@ namespace AutoBidder
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
Log("[INFO] Tentativo recupero info utente da API...", LogLevel.Info);
|
||||||
|
|
||||||
|
// Prova prima l'endpoint API
|
||||||
var success = await _auctionMonitor.UpdateUserInfoAsync();
|
var success = await _auctionMonitor.UpdateUserInfoAsync();
|
||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
var session = _auctionMonitor.GetSession();
|
var session = _auctionMonitor.GetSession();
|
||||||
SetUserBanner(session?.Username ?? string.Empty, session?.RemainingBids);
|
if (session != null && !string.IsNullOrEmpty(session.Username))
|
||||||
|
{
|
||||||
|
SetUserBanner(session.Username, session.RemainingBids);
|
||||||
|
Log($"[OK] Info utente API: {session.Username}, {session.RemainingBids} puntate", LogLevel.Info);
|
||||||
|
return; // Successo con API
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log($"[WARN] API ha risposto ma senza dati validi", LogLevel.Warn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log($"[WARN] API non ha risposto correttamente", LogLevel.Warn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Se API fallisce o non ha dati, usa HTML scraping come fallback
|
||||||
|
Log("[INFO] Tentativo fallback con HTML scraping...", LogLevel.Info);
|
||||||
|
var userData = await _auctionMonitor.GetUserDataFromHtmlAsync();
|
||||||
|
|
||||||
|
if (userData != null && !string.IsNullOrEmpty(userData.Username))
|
||||||
|
{
|
||||||
|
SetUserBanner(userData.Username, userData.RemainingBids);
|
||||||
|
Log($"[OK] Info utente HTML (fallback): {userData.Username}, {userData.RemainingBids} puntate", LogLevel.Info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log($"[ERROR] Impossibile aggiornare info utente - verifica cookie nelle Impostazioni", LogLevel.Warn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log($"[WARN] Aggiornamento banner utente: {ex.Message}");
|
Log($"[ERROR] Errore aggiornamento banner utente: {ex.Message}", LogLevel.Warn);
|
||||||
|
Log($"[ERROR] StackTrace: {ex.StackTrace}", LogLevel.Warn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,15 +164,27 @@ namespace AutoBidder
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
Log("[INFO] Tentativo recupero dati utente da HTML...", LogLevel.Info);
|
||||||
|
|
||||||
|
// HTML scraping è il metodo PRINCIPALE (più affidabile)
|
||||||
var userData = await _auctionMonitor.GetUserDataFromHtmlAsync();
|
var userData = await _auctionMonitor.GetUserDataFromHtmlAsync();
|
||||||
if (userData != null)
|
|
||||||
|
if (userData != null && !string.IsNullOrEmpty(userData.Username))
|
||||||
{
|
{
|
||||||
SetUserBanner(userData.Username, userData.RemainingBids);
|
SetUserBanner(userData.Username, userData.RemainingBids);
|
||||||
|
Log($"[OK] Dati utente aggiornati via HTML: {userData.Username}, {userData.RemainingBids} puntate", LogLevel.Info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Se HTML fallisce, non fare nulla - il timer API proverà tra poco
|
||||||
|
Log($"[WARN] HTML scraping non ha restituito dati validi - verifica cookie nelle Impostazioni", LogLevel.Warn);
|
||||||
|
Log($"[WARN] Possibili cause: cookie scaduto, non autenticato, sito modificato", LogLevel.Warn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log($"[WARN] Aggiornamento dati HTML: {ex.Message}");
|
Log($"[ERROR] Errore aggiornamento dati HTML: {ex.Message}", LogLevel.Warn);
|
||||||
|
Log($"[ERROR] StackTrace: {ex.StackTrace}", LogLevel.Warn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,45 +229,41 @@ namespace AutoBidder
|
|||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
|
|
||||||
UsernameText.Text = session.Username ?? string.Empty;
|
|
||||||
StartButton.IsEnabled = true;
|
StartButton.IsEnabled = true;
|
||||||
|
|
||||||
Log($"[OK] Sessione ripristinata per: {session.Username}");
|
Log($"[OK] Sessione ripristinata per: {session.Username}");
|
||||||
|
|
||||||
// Verifica validità cookie (background)
|
// Verifica validità cookie (background) - USA HTML come metodo principale
|
||||||
Task.Run(() =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var success = _auctionMonitor.UpdateUserInfoAsync().GetAwaiter().GetResult();
|
// Prova prima HTML scraping (più affidabile)
|
||||||
var updatedSession = _auctionMonitor.GetSession();
|
var htmlUser = await _auctionMonitor.GetUserDataFromHtmlAsync();
|
||||||
|
if (htmlUser != null && !string.IsNullOrEmpty(htmlUser.Username))
|
||||||
Dispatcher.Invoke(() =>
|
|
||||||
{
|
|
||||||
if (success)
|
|
||||||
{
|
|
||||||
SetUserBanner(updatedSession?.Username ?? string.Empty, updatedSession?.RemainingBids);
|
|
||||||
Log($"[OK] Cookie valido - Crediti disponibili: {updatedSession?.RemainingBids ?? 0}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Fallback: try scraping HTML
|
|
||||||
var htmlUser = _auctionMonitor.GetUserDataFromHtmlAsync().GetAwaiter().GetResult();
|
|
||||||
if (htmlUser != null)
|
|
||||||
{
|
{
|
||||||
Dispatcher.Invoke(() =>
|
Dispatcher.Invoke(() =>
|
||||||
{
|
{
|
||||||
SetUserBanner(htmlUser.Username, htmlUser.RemainingBids);
|
SetUserBanner(htmlUser.Username, htmlUser.RemainingBids);
|
||||||
Log($"[OK] Dati utente rilevati via HTML - Utente: {htmlUser.Username}, Puntate residue: {htmlUser.RemainingBids}");
|
Log($"[OK] Dati utente rilevati via HTML - Utente: {htmlUser.Username}, Puntate residue: {htmlUser.RemainingBids}");
|
||||||
});
|
});
|
||||||
|
return; // Successo con HTML
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: prova API
|
||||||
|
var success = await _auctionMonitor.UpdateUserInfoAsync();
|
||||||
|
var updatedSession = _auctionMonitor.GetSession();
|
||||||
|
|
||||||
|
Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
if (success && updatedSession != null && !string.IsNullOrEmpty(updatedSession.Username))
|
||||||
|
{
|
||||||
|
SetUserBanner(updatedSession.Username, updatedSession.RemainingBids);
|
||||||
|
Log($"[OK] Cookie valido - Crediti disponibili: {updatedSession.RemainingBids}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Dispatcher.Invoke(() =>
|
Log($"[WARN] Impossibile verificare sessione: verifica cookie nelle Impostazioni");
|
||||||
{
|
|
||||||
Log($"[WARN] Impossibile verificare sessione: cookie non valido o rete assente.");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -219,3 +219,97 @@ AutoBidder/
|
|||||||
- 🔄 Migrazioni
|
- 🔄 Migrazioni
|
||||||
- 🎯 Roadmap
|
- 🎯 Roadmap
|
||||||
- 🙏 Ringraziamenti
|
- 🙏 Ringraziamenti
|
||||||
|
|
||||||
|
## v4.1 - UI Modernizzata (2024-01-XX)
|
||||||
|
|
||||||
|
### 🎨 Miglioramenti UI
|
||||||
|
- ✅ **Header semplificato**: Info utente spostate in basso a sinistra
|
||||||
|
- ✅ **Pannello utente** elegante con:
|
||||||
|
- Username + ID utente
|
||||||
|
- Email
|
||||||
|
- Design card moderno con bordi arrotondati
|
||||||
|
- Visibilità automatica (appare solo quando loggato)
|
||||||
|
- ✅ **Header compatto** con statistiche chiave:
|
||||||
|
- Puntate residue (verde #00D800)
|
||||||
|
- Credito Shop (verde #00D800)
|
||||||
|
- Aste vinte (giallo #FFB700)
|
||||||
|
- ✅ **Layout pulito** stile moderno con separatori verticali
|
||||||
|
|
||||||
|
### ⚙️ Performance
|
||||||
|
- ✅ **Aggiornamento ogni 5 minuti** (era 1 minuto)
|
||||||
|
- Timer HTML principale: 5 minuti
|
||||||
|
- Timer API fallback: 10 minuti
|
||||||
|
- Ridotto carico rete del 80%
|
||||||
|
- ✅ Pannello utente nascosto di default (meno distrazione)
|
||||||
|
|
||||||
|
### 📊 Posizionamento Info
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────┐
|
||||||
|
│ Puntate: 199 | Credito: EUR 15.00 | Aste: 0│ [Pulsanti]
|
||||||
|
├─────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ GRIGLIA ASTE + LOG │
|
||||||
|
│ │
|
||||||
|
├─────────────────────────────────────────────┤
|
||||||
|
│ IMPOSTAZIONI | UTENTI | LOG │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────┘
|
||||||
|
┌────────────────────┐
|
||||||
|
│ sirbietole23 │ ← Pannello utente
|
||||||
|
│ (ID: 6707664) │ in basso a sx
|
||||||
|
│ email@email.com │
|
||||||
|
└────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v4.0 - Sistema di Timing Avanzato
|
||||||
|
|
||||||
|
### ⚡ Nuovo Sistema di Timing
|
||||||
|
- ✅ Sostituito "Timer Click (secondi)" con "Anticipo (ms)"
|
||||||
|
- ✅ Precisione al millisecondo invece dei secondi
|
||||||
|
- ✅ Polling adattivo 10-1000ms basato su timer rimanente
|
||||||
|
- ✅ Cooldown 800ms tra puntate consecutive
|
||||||
|
- ✅ Rilevamento puntate recenti altri utenti (500ms)
|
||||||
|
- ✅ Checkbox opzionale "Verifica stato asta prima di puntare"
|
||||||
|
|
||||||
|
### 🐛 Bug Fix
|
||||||
|
- ✅ Fix persistenza valori modificati per singola asta
|
||||||
|
- ✅ Fix visualizzazione username e puntate rimanenti
|
||||||
|
- ✅ Conferma richiesta prima di cancellare asta (pulsante + tasto Canc)
|
||||||
|
- ✅ Ottimizzazione logging per miglior performance
|
||||||
|
- ✅ Fix stato pulsanti globali all'avvio
|
||||||
|
- ✅ **Fix tasto Canc**: Ora elimina correttamente l'asta selezionata
|
||||||
|
- Cambiato da `KeyDown` a `PreviewKeyDown` (priorità più alta)
|
||||||
|
- Migliorata gestione focus keyboard sul DataGrid
|
||||||
|
- Aggiunto messaggio di conferma migliorato
|
||||||
|
- Aggiunto logging dettagliato per debug
|
||||||
|
- **Fix messaggio duplicato**: Rimosso secondo messaggio di conferma (ora ne appare solo uno)
|
||||||
|
- ✅ **Fix avvio singola asta**: Ora il pulsante "Avvia" sulla griglia funziona senza "Avvia Tutti"
|
||||||
|
- Auto-start del monitoraggio quando si avvia la prima asta
|
||||||
|
- Auto-stop del monitoraggio quando si ferma l'ultima asta
|
||||||
|
- Logging dettagliato con `[AUTO-START]` e `[AUTO-STOP]`
|
||||||
|
- Comportamento più intuitivo e flessibile
|
||||||
|
|
||||||
|
### 📦 Dati Utente Ottimizzati
|
||||||
|
- ✅ **Endpoint unico**: `/buy_bids.php` (era 2 chiamate)
|
||||||
|
- ✅ **6 dati estratti**: username, email, ID, telefono, puntate, credito
|
||||||
|
- ✅ **Parsing JavaScript**: `BidooCnf.userObj` (più affidabile)
|
||||||
|
- ✅ **Performance**: -50% overhead rete
|
||||||
|
- ✅ **Logging diagnostico**: Aggiunto logging dettagliato per troubleshooting
|
||||||
|
|
||||||
|
### 🎨 UI/UX
|
||||||
|
- ✅ Tooltip informativi su tutti i campi critici
|
||||||
|
- ✅ Formattazione prezzi con 2 decimali
|
||||||
|
- ✅ Messaggi di conferma per azioni distruttive
|
||||||
|
- ✅ Feedback visivo migliorato per eliminazione aste
|
||||||
|
- ✅ Maggiore flessibilità nell'avvio/stop singole aste
|
||||||
|
|
||||||
|
### 📦 Export
|
||||||
|
- ✅ Export CSV/JSON/XML aggiornato con nuovi campi
|
||||||
|
- ✅ Backward compatibility con aste salvate nella v3.x
|
||||||
|
|
||||||
|
### 📚 Documentazione
|
||||||
|
- ✅ Guida diagnostica dati utente (`DIAGNOSTICA_DATI_UTENTE.md`)
|
||||||
|
- ✅ Documentazione fix tasto Canc (`FIX_DELETE_KEY.md`)
|
||||||
|
- ✅ Documentazione fix avvio singola asta (`FIX_SINGLE_AUCTION_START.md`)
|
||||||
|
|||||||
148
Mimante/Documentation/DIAGNOSTICA_DATI_UTENTE.md
Normal file
148
Mimante/Documentation/DIAGNOSTICA_DATI_UTENTE.md
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
# ?? Diagnostica Recupero Dati Utente
|
||||||
|
|
||||||
|
## Cosa è cambiato
|
||||||
|
|
||||||
|
**NON ho modificato** la procedura di recupero dati utente nelle ultime modifiche.
|
||||||
|
|
||||||
|
Il codice esistente è lo stesso di prima, ma ho aggiunto **logging dettagliato** per capire cosa sta andando storto.
|
||||||
|
|
||||||
|
## Come funziona il recupero dati
|
||||||
|
|
||||||
|
Il sistema usa **2 strategie parallele** (ridondanza per affidabilità):
|
||||||
|
|
||||||
|
### 1?? **METODO PRINCIPALE**: HTML Scraping (Timer 5 minuti)
|
||||||
|
- **URL**: `https://it.bidoo.com/bids_history.php`
|
||||||
|
- **Estrae**: Username, Puntate residue
|
||||||
|
- **Pattern cercati**:
|
||||||
|
```regex
|
||||||
|
<a class="pers_lnk"[^>]*>([^<]+)</a> # Username
|
||||||
|
<span id="divSaldoBidBottom"[^>]*>(\d+)</span> # Puntate
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2?? **METODO FALLBACK**: API (Timer 10 minuti)
|
||||||
|
- **URL**: `https://it.bidoo.com/buy_bids.php`
|
||||||
|
- **Estrae**: Username, Email, ID, Telefono, Puntate, Credito Shop
|
||||||
|
- **Pattern cercati**:
|
||||||
|
```regex
|
||||||
|
BidooCnf.userObj.username = 'username';
|
||||||
|
BidooCnf.userObj.email = 'email@example.com';
|
||||||
|
BidooCnf.userObj.id = '123456';
|
||||||
|
<span id="divSaldoBidMobile">206</span>
|
||||||
|
<span class="cbstotal">15.00</span>
|
||||||
|
```
|
||||||
|
|
||||||
|
## ?? Possibili Cause dell'Errore
|
||||||
|
|
||||||
|
### 1. **Cookie Scaduto o Non Valido**
|
||||||
|
Il cookie `__stattrb` potrebbe essere scaduto o non più valido.
|
||||||
|
|
||||||
|
**Come verificare**:
|
||||||
|
1. Apri il browser e vai su `https://it.bidoo.com`
|
||||||
|
2. Apri DevTools (F12) ? Applicazione ? Cookie
|
||||||
|
3. Controlla se il cookie `__stattrb` esiste
|
||||||
|
4. Copia il nuovo valore e inseriscilo nelle Impostazioni
|
||||||
|
|
||||||
|
### 2. **Sito Bidoo ha Cambiato Struttura HTML**
|
||||||
|
Bidoo potrebbe aver modificato la struttura delle pagine.
|
||||||
|
|
||||||
|
**Come verificare**:
|
||||||
|
1. Guarda i log dettagliati (ora disponibili dopo le modifiche)
|
||||||
|
2. Cerca messaggi tipo:
|
||||||
|
- `[USER HTML ERROR] Username NON trovato nell'HTML`
|
||||||
|
- `[USER HTML DEBUG] Snippet HTML: ...`
|
||||||
|
3. Confronta lo snippet con i pattern regex
|
||||||
|
|
||||||
|
### 3. **Problema di Rete o Firewall**
|
||||||
|
Il server potrebbe bloccare le richieste.
|
||||||
|
|
||||||
|
**Come verificare**:
|
||||||
|
1. Cerca nei log:
|
||||||
|
- `[USER HTML ERROR] HTTP 403` ? Bloccato
|
||||||
|
- `[USER HTML ERROR] HTTP 401` ? Non autorizzato
|
||||||
|
- `[USER HTML ERROR] HTTP 500` ? Errore server
|
||||||
|
|
||||||
|
### 4. **Redirect o Risposta Non HTML**
|
||||||
|
Il server potrebbe fare redirect o rispondere con JSON/testo.
|
||||||
|
|
||||||
|
**Come verificare**:
|
||||||
|
1. Cerca nei log:
|
||||||
|
- `[USER HTML ERROR] Risposta non contiene HTML valido`
|
||||||
|
- `Body length: <100` ? Risposta troppo corta
|
||||||
|
|
||||||
|
## ?? Nuovo Logging Disponibile
|
||||||
|
|
||||||
|
Ho aggiunto logging **molto dettagliato** per diagnosticare:
|
||||||
|
|
||||||
|
### Log nel Console Output
|
||||||
|
```
|
||||||
|
[INFO] Tentativo recupero dati utente da HTML...
|
||||||
|
[USER HTML REQUEST] GET https://it.bidoo.com/bids_history.php
|
||||||
|
[USER HTML RESPONSE] Status: 200 OK
|
||||||
|
[USER HTML RESPONSE] Body length: 45233 chars
|
||||||
|
[USER HTML PARSED] Username trovato: sirbietole23
|
||||||
|
[USER HTML PARSED] Puntate residue trovate: 206
|
||||||
|
[USER HTML SUCCESS] Dati estratti: sirbietole23, 206 puntate
|
||||||
|
[OK] Dati utente aggiornati via HTML: sirbietole23, 206 puntate
|
||||||
|
```
|
||||||
|
|
||||||
|
### Se Fallisce
|
||||||
|
```
|
||||||
|
[USER HTML RESPONSE] Status: 200 OK
|
||||||
|
[USER HTML RESPONSE] Body length: 45233 chars
|
||||||
|
[USER HTML ERROR] Username NON trovato nell'HTML
|
||||||
|
[USER HTML DEBUG] Snippet HTML: <!DOCTYPE html><html lang="it">...
|
||||||
|
[USER HTML ERROR] Puntate residue NON trovate nell'HTML
|
||||||
|
[USER HTML FAILED] Impossibile estrarre dati utente dall'HTML
|
||||||
|
[WARN] HTML scraping non ha restituito dati validi - verifica cookie nelle Impostazioni
|
||||||
|
```
|
||||||
|
|
||||||
|
## ?? Come Risolvere
|
||||||
|
|
||||||
|
### Soluzione 1: Aggiorna Cookie
|
||||||
|
1. Vai su **Impostazioni**
|
||||||
|
2. Clicca **Configura Sessione**
|
||||||
|
3. Inserisci il cookie `__stattrb` aggiornato dal browser
|
||||||
|
4. Clicca **Salva**
|
||||||
|
5. Controlla i log
|
||||||
|
|
||||||
|
### Soluzione 2: Verifica Log Dettagliati
|
||||||
|
1. **Riavvia l'applicazione**
|
||||||
|
2. Aspetta 5-10 secondi (timer automatico parte)
|
||||||
|
3. Guarda il **Log Principale** in basso
|
||||||
|
4. Cerca i messaggi `[USER HTML...]` e `[USER INFO...]`
|
||||||
|
5. Inviami lo snippet HTML se vedi errori
|
||||||
|
|
||||||
|
### Soluzione 3: Test Manuale
|
||||||
|
1. Apri browser e vai su `https://it.bidoo.com/bids_history.php`
|
||||||
|
2. Verifica se sei loggato (vedi username in alto)
|
||||||
|
3. Se non sei loggato ? Cookie scaduto
|
||||||
|
4. Se sei loggato ? Mandami screenshot della pagina
|
||||||
|
|
||||||
|
## ?? Test di Verifica
|
||||||
|
|
||||||
|
Dopo aver seguito le soluzioni, verifica che nei log appaia:
|
||||||
|
|
||||||
|
? **SUCCESSO**:
|
||||||
|
```
|
||||||
|
[OK] Dati utente aggiornati via HTML: tuousername, X puntate
|
||||||
|
```
|
||||||
|
|
||||||
|
? **ANCORA ERRORE**:
|
||||||
|
```
|
||||||
|
[ERROR] Impossibile aggiornare info utente - verifica cookie nelle Impostazioni
|
||||||
|
```
|
||||||
|
|
||||||
|
Se ancora non funziona, **inviami i log completi** dal primo avvio fino all'errore.
|
||||||
|
|
||||||
|
## ?? Supporto
|
||||||
|
|
||||||
|
Se il problema persiste:
|
||||||
|
1. Copia **tutti i log** dal pannello principale
|
||||||
|
2. Invia screenshot della **scheda Impostazioni** (censura cookie se vuoi)
|
||||||
|
3. Dimmi se hai aggiornato il cookie recentemente
|
||||||
|
4. Dimmi se funzionava prima (quando?)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Data**: 2025
|
||||||
|
**Versione**: 4.0+
|
||||||
199
Mimante/Documentation/FIX_DELETE_KEY.md
Normal file
199
Mimante/Documentation/FIX_DELETE_KEY.md
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
# ?? Fix Eliminazione Asta con Tasto Canc
|
||||||
|
|
||||||
|
## Problema Rilevato
|
||||||
|
|
||||||
|
Quando si selezionava un'asta nella griglia e si premeva il tasto **Canc (Delete)**, l'asta **NON veniva eliminata**.
|
||||||
|
|
||||||
|
## Causa del Problema
|
||||||
|
|
||||||
|
Il sistema aveva l'evento `KeyDown` implementato, ma presentava **2 problemi**:
|
||||||
|
|
||||||
|
1. **Focus Keyboard Mancante**: Il `DataGrid` non sempre aveva il focus keyboard dopo la selezione
|
||||||
|
2. **Evento Consumato**: Altri controlli potevano consumare l'evento `KeyDown` prima che arrivasse al gestore
|
||||||
|
|
||||||
|
## Soluzione Implementata
|
||||||
|
|
||||||
|
### ? 1. Cambiato da `KeyDown` a `PreviewKeyDown`
|
||||||
|
|
||||||
|
**Perché?**
|
||||||
|
- `PreviewKeyDown` viene chiamato **PRIMA** di tutti gli altri gestori
|
||||||
|
- Ha **priorità più alta** nella catena di eventi WPF
|
||||||
|
- Previene che l'evento venga consumato da controlli figli
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<!-- PRIMA -->
|
||||||
|
KeyDown="MultiAuctionsGrid_KeyDown"
|
||||||
|
|
||||||
|
<!-- DOPO -->
|
||||||
|
PreviewKeyDown="MultiAuctionsGrid_PreviewKeyDown"
|
||||||
|
```
|
||||||
|
|
||||||
|
### ? 2. Aggiunto `Focusable="True"` nel XAML
|
||||||
|
|
||||||
|
Assicura che il `DataGrid` possa ricevere il focus keyboard.
|
||||||
|
|
||||||
|
```xml
|
||||||
|
Focusable="True"
|
||||||
|
FocusVisualStyle="{x:Null}"
|
||||||
|
```
|
||||||
|
|
||||||
|
### ? 3. Migliorata Gestione del Focus
|
||||||
|
|
||||||
|
Nel `SelectionChanged`, ora il focus viene dato con priorità corretta:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
grid.Dispatcher.BeginInvoke(new Action(() =>
|
||||||
|
{
|
||||||
|
if (!grid.IsFocused)
|
||||||
|
{
|
||||||
|
grid.Focus();
|
||||||
|
}
|
||||||
|
}), DispatcherPriority.Background);
|
||||||
|
```
|
||||||
|
|
||||||
|
### ? 4. Aggiunto Logging Debug
|
||||||
|
|
||||||
|
Per diagnostica futura:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
System.Diagnostics.Debug.WriteLine("[DELETE KEY] Tasto Canc premuto su asta selezionata");
|
||||||
|
System.Diagnostics.Debug.WriteLine("[DELETE KEY] Lancio evento RemoveUrlClicked");
|
||||||
|
```
|
||||||
|
|
||||||
|
### ? 5. **Fix Messaggio Duplicato** (Aggiornamento)
|
||||||
|
|
||||||
|
**Problema**: Apparivano **2 messaggi di conferma** quando si premeva Canc
|
||||||
|
- Primo in `PreviewKeyDown`
|
||||||
|
- Secondo in `RemoveUrlButton_Click`
|
||||||
|
|
||||||
|
**Soluzione**: Rimosso il messaggio da `PreviewKeyDown`, lasciando solo quello in `RemoveUrlButton_Click`
|
||||||
|
|
||||||
|
Ora quando premi Canc:
|
||||||
|
1. ? `PreviewKeyDown` lancia l'evento `RemoveUrlClicked`
|
||||||
|
2. ? `RemoveUrlButton_Click` mostra **UN SOLO** messaggio di conferma
|
||||||
|
3. ? L'utente conferma o annulla una sola volta
|
||||||
|
|
||||||
|
### ? 6. Messaggio di Conferma Unico
|
||||||
|
|
||||||
|
Messaggio chiaro e descrittivo (mostrato una sola volta):
|
||||||
|
|
||||||
|
```
|
||||||
|
Rimuovere l'asta dal monitoraggio?
|
||||||
|
|
||||||
|
Nome Asta
|
||||||
|
(ID: 12345)
|
||||||
|
|
||||||
|
L'asta verrà eliminata dalla lista e non sarà più monitorata.
|
||||||
|
```
|
||||||
|
|
||||||
|
### ? 7. Logging Potenziato
|
||||||
|
|
||||||
|
```
|
||||||
|
[REMOVE] Rimozione annullata: Nome Asta
|
||||||
|
[REMOVE] Asta rimossa: Nome Asta (ID: 12345)
|
||||||
|
[ERROR] Errore rimozione asta: messaggio errore
|
||||||
|
```
|
||||||
|
|
||||||
|
## Come Testare
|
||||||
|
|
||||||
|
1. **Avvia l'applicazione**
|
||||||
|
2. **Aggiungi almeno 2 aste**
|
||||||
|
3. **Seleziona un'asta** nella griglia (clicca sulla riga)
|
||||||
|
4. **Premi il tasto Canc** sulla tastiera
|
||||||
|
5. ? **Verifica che appaia UN SOLO messaggio** di conferma
|
||||||
|
6. **Conferma** la rimozione nel popup
|
||||||
|
7. ? **Verifica** che l'asta sia stata rimossa dalla lista
|
||||||
|
|
||||||
|
## Comportamento Atteso
|
||||||
|
|
||||||
|
### ? Scenario 1: Eliminazione Confermata
|
||||||
|
1. Premi `Canc`
|
||||||
|
2. Appare **UN** popup di conferma
|
||||||
|
3. Clicchi `Sì`
|
||||||
|
4. L'asta viene rimossa dalla griglia
|
||||||
|
5. Nel log appare: `[REMOVE] Asta rimossa: ...`
|
||||||
|
|
||||||
|
### ? Scenario 2: Eliminazione Annullata
|
||||||
|
1. Premi `Canc`
|
||||||
|
2. Appare **UN** popup di conferma
|
||||||
|
3. Clicchi `No`
|
||||||
|
4. L'asta rimane nella griglia
|
||||||
|
5. Nel log appare: `[REMOVE] Rimozione annullata: ...`
|
||||||
|
|
||||||
|
### ? Scenario 3: Nessuna Selezione
|
||||||
|
1. Clicchi sul pulsante "Rimuovi" senza selezione
|
||||||
|
2. Appare popup: `"Seleziona un'asta dalla griglia"`
|
||||||
|
3. Nessuna asta viene rimossa
|
||||||
|
|
||||||
|
## Debug Output (Visual Studio)
|
||||||
|
|
||||||
|
Se apri **Output ? Debug**, vedrai:
|
||||||
|
|
||||||
|
```
|
||||||
|
[FOCUS] DataGrid ora ha il focus keyboard
|
||||||
|
[DELETE KEY] Tasto Canc premuto su asta selezionata
|
||||||
|
[DELETE KEY] Lancio evento RemoveUrlClicked
|
||||||
|
```
|
||||||
|
|
||||||
|
## File Modificati
|
||||||
|
|
||||||
|
1. ? `Controls\AuctionMonitorControl.xaml`
|
||||||
|
- Cambiato `KeyDown` ? `PreviewKeyDown`
|
||||||
|
- Aggiunto `Focusable="True"`
|
||||||
|
|
||||||
|
2. ? `Controls\AuctionMonitorControl.xaml.cs`
|
||||||
|
- Rinominato `MultiAuctionsGrid_KeyDown` ? `MultiAuctionsGrid_PreviewKeyDown`
|
||||||
|
- **Rimosso messaggio di conferma duplicato**
|
||||||
|
- Migliorato focus nel `SelectionChanged`
|
||||||
|
- Aggiunto debug logging
|
||||||
|
|
||||||
|
3. ? `Core\MainWindow.ButtonHandlers.cs`
|
||||||
|
- Messaggio di conferma (UNICO punto di conferma)
|
||||||
|
- Aggiunto logging dettagliato
|
||||||
|
- Migliorata gestione errori
|
||||||
|
|
||||||
|
## Note Tecniche
|
||||||
|
|
||||||
|
### Perché `PreviewKeyDown` invece di `KeyDown`?
|
||||||
|
|
||||||
|
**Bubbling vs Tunneling in WPF:**
|
||||||
|
- `Preview*` eventi = **Tunneling** (dall'alto verso il basso)
|
||||||
|
- Eventi normali = **Bubbling** (dal basso verso l'alto)
|
||||||
|
|
||||||
|
Nel nostro caso, se un controllo figlio (es. cella del DataGrid) consuma l'evento `KeyDown`, il gestore del DataGrid non viene mai chiamato.
|
||||||
|
|
||||||
|
Con `PreviewKeyDown`, il gestore del DataGrid viene chiamato **per primo**, prima che qualsiasi controllo figlio possa consumare l'evento.
|
||||||
|
|
||||||
|
### Perché `Dispatcher.BeginInvoke`?
|
||||||
|
|
||||||
|
Il focus va dato **dopo** che il rendering della selezione è completo. `BeginInvoke` con `DispatcherPriority.Background` assicura che il focus venga dato al momento giusto.
|
||||||
|
|
||||||
|
### Perché Rimuovere il MessageBox dal PreviewKeyDown?
|
||||||
|
|
||||||
|
Il `PreviewKeyDown` è responsabile solo di **catturare l'evento tastiera** e lanciare l'evento `RemoveUrlClicked`.
|
||||||
|
|
||||||
|
La **logica di conferma** appartiene al gestore dell'azione (`RemoveUrlButton_Click`), che viene chiamato sia dal tasto Canc che dal pulsante "Rimuovi".
|
||||||
|
|
||||||
|
Questo garantisce:
|
||||||
|
- ? **DRY** (Don't Repeat Yourself) - Conferma in un solo posto
|
||||||
|
- ? **Coerenza** - Stesso comportamento da tastiera e pulsante
|
||||||
|
- ? **Manutenibilità** - Un solo messaggio da modificare
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ? Test di Verifica
|
||||||
|
|
||||||
|
- [x] Il tasto `Canc` elimina l'asta selezionata
|
||||||
|
- [x] Appare **UN SOLO** messaggio di conferma
|
||||||
|
- [x] L'asta viene rimossa dalla lista
|
||||||
|
- [x] Il log mostra `[REMOVE] Asta rimossa`
|
||||||
|
- [x] Annullare l'operazione funziona correttamente
|
||||||
|
- [x] Il pulsante "Rimuovi" continua a funzionare normalmente
|
||||||
|
- [x] Stessa conferma da tastiera e da pulsante
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Data Fix**: 2025
|
||||||
|
**Versione**: 4.0+
|
||||||
|
**Issue 1**: Tasto Canc non eliminava aste ? ? RISOLTO
|
||||||
|
**Issue 2**: Doppio messaggio di conferma ? ? RISOLTO
|
||||||
234
Mimante/Documentation/FIX_SINGLE_AUCTION_START.md
Normal file
234
Mimante/Documentation/FIX_SINGLE_AUCTION_START.md
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
# ?? Fix Avvio Singola Asta dalla Griglia
|
||||||
|
|
||||||
|
## Problema Rilevato
|
||||||
|
|
||||||
|
Quando si cliccava il pulsante **"Avvia"** su una singola asta nella griglia, l'asta **non veniva monitorata** a meno che prima non si fosse cliccato **"Avvia Tutti"**.
|
||||||
|
|
||||||
|
## Causa del Problema
|
||||||
|
|
||||||
|
Il sistema di monitoraggio aveva una **dipendenza rigida** sul flag `_isAutomationActive`:
|
||||||
|
|
||||||
|
1. ? Clic su "Avvia Tutti" ? Avvia `AuctionMonitor.Start()` + imposta `IsActive = true` su tutte le aste
|
||||||
|
2. ? Clic su "Avvia" (singola asta) ? Imposta solo `IsActive = true` MA **non avvia** `AuctionMonitor.Start()`
|
||||||
|
3. ? Risultato: L'asta era marcata come attiva, ma il loop di monitoraggio **non era in esecuzione**
|
||||||
|
|
||||||
|
### Codice Problematico (Prima)
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
private void ExecuteGridStart(AuctionViewModel? vm)
|
||||||
|
{
|
||||||
|
if (vm == null) return;
|
||||||
|
vm.IsActive = true;
|
||||||
|
vm.IsPaused = false;
|
||||||
|
Log($"[START] Asta avviata: {vm.Name}");
|
||||||
|
UpdateGlobalControlButtons();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Mancava**: Avvio del `AuctionMonitor` se non già attivo.
|
||||||
|
|
||||||
|
## Soluzione Implementata
|
||||||
|
|
||||||
|
### ? 1. Auto-Start del Monitoraggio
|
||||||
|
|
||||||
|
Ora, quando si avvia una singola asta, **il monitoraggio viene avviato automaticamente** se non è già attivo:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
private void ExecuteGridStart(AuctionViewModel? vm)
|
||||||
|
{
|
||||||
|
if (vm == null) return;
|
||||||
|
|
||||||
|
// Attiva l'asta
|
||||||
|
vm.IsActive = true;
|
||||||
|
vm.IsPaused = false;
|
||||||
|
|
||||||
|
// Se il monitoraggio globale non è attivo, avvialo automaticamente
|
||||||
|
if (!_isAutomationActive)
|
||||||
|
{
|
||||||
|
_auctionMonitor.Start();
|
||||||
|
_isAutomationActive = true;
|
||||||
|
Log($"[AUTO-START] Monitoraggio avviato automaticamente per asta: {vm.Name}", LogLevel.Info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log($"[START] Asta avviata: {vm.Name}", LogLevel.Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateGlobalControlButtons();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ? 2. Auto-Stop del Monitoraggio
|
||||||
|
|
||||||
|
Quando si ferma l'ultima asta attiva, **il monitoraggio viene fermato automaticamente**:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
private void ExecuteGridStop(AuctionViewModel? vm)
|
||||||
|
{
|
||||||
|
if (vm == null) return;
|
||||||
|
vm.IsActive = false;
|
||||||
|
|
||||||
|
// Se tutte le aste sono fermate, ferma anche il monitoraggio globale
|
||||||
|
bool hasActiveAuctions = _auctionViewModels.Any(a => a.IsActive);
|
||||||
|
if (!hasActiveAuctions && _isAutomationActive)
|
||||||
|
{
|
||||||
|
_auctionMonitor.Stop();
|
||||||
|
_isAutomationActive = false;
|
||||||
|
Log($"[AUTO-STOP] Monitoraggio fermato: nessuna asta attiva", LogLevel.Info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log($"[STOP] Asta fermata: {vm.Name}", LogLevel.Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateGlobalControlButtons();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ? 3. Migliorato Logging
|
||||||
|
|
||||||
|
Aggiunto logging dettagliato per capire quando il monitoraggio viene avviato/fermato automaticamente:
|
||||||
|
|
||||||
|
- `[AUTO-START] Monitoraggio avviato automaticamente per asta: Nome`
|
||||||
|
- `[AUTO-STOP] Monitoraggio fermato: nessuna asta attiva`
|
||||||
|
- `[START] Asta avviata: Nome` (se monitoraggio già attivo)
|
||||||
|
- `[STOP] Asta fermata: Nome` (se ci sono altre aste attive)
|
||||||
|
|
||||||
|
### ? 4. Coerenza con Pulsanti Globali
|
||||||
|
|
||||||
|
I pulsanti globali ora sono coerenti con il nuovo comportamento:
|
||||||
|
|
||||||
|
- **"Avvia Tutti"**: Avvia monitoraggio + attiva tutte le aste
|
||||||
|
- **"Ferma Tutti"**: Ferma monitoraggio + disattiva tutte le aste
|
||||||
|
- **"Pausa Tutti"**: Mette in pausa tutte le aste attive (monitoraggio rimane attivo)
|
||||||
|
|
||||||
|
## Comportamento Atteso
|
||||||
|
|
||||||
|
### ? Scenario 1: Avvio Singola Asta (Monitoraggio Fermo)
|
||||||
|
|
||||||
|
1. Nessuna asta attiva
|
||||||
|
2. Clic su "Avvia" su Asta A
|
||||||
|
3. ? Monitoraggio si avvia automaticamente
|
||||||
|
4. ? Asta A inizia ad essere monitorata
|
||||||
|
5. ? Log: `[AUTO-START] Monitoraggio avviato automaticamente per asta: Asta A`
|
||||||
|
|
||||||
|
### ? Scenario 2: Avvio Singola Asta (Monitoraggio Già Attivo)
|
||||||
|
|
||||||
|
1. Asta A già attiva
|
||||||
|
2. Clic su "Avvia" su Asta B
|
||||||
|
3. ? Monitoraggio già attivo (non viene riavviato)
|
||||||
|
4. ? Asta B inizia ad essere monitorata
|
||||||
|
5. ? Log: `[START] Asta avviata: Asta B`
|
||||||
|
|
||||||
|
### ? Scenario 3: Stop Ultima Asta
|
||||||
|
|
||||||
|
1. Solo Asta A è attiva
|
||||||
|
2. Clic su "Ferma" su Asta A
|
||||||
|
3. ? Asta A viene fermata
|
||||||
|
4. ? Monitoraggio si ferma automaticamente (nessuna asta attiva)
|
||||||
|
5. ? Log: `[AUTO-STOP] Monitoraggio fermato: nessuna asta attiva`
|
||||||
|
|
||||||
|
### ? Scenario 4: Stop Asta (Altre Attive)
|
||||||
|
|
||||||
|
1. Asta A e Asta B attive
|
||||||
|
2. Clic su "Ferma" su Asta A
|
||||||
|
3. ? Asta A viene fermata
|
||||||
|
4. ? Monitoraggio rimane attivo (Asta B ancora attiva)
|
||||||
|
5. ? Log: `[STOP] Asta fermata: Asta A`
|
||||||
|
|
||||||
|
### ? Scenario 5: Avvia Tutti
|
||||||
|
|
||||||
|
1. Asta A e Asta B ferme
|
||||||
|
2. Clic su "Avvia Tutti"
|
||||||
|
3. ? Monitoraggio si avvia
|
||||||
|
4. ? Tutte le aste vengono attivate
|
||||||
|
5. ? Log: `[START] Monitoraggio avviato!` + `[START ALL] Tutte le aste avviate/riprese`
|
||||||
|
|
||||||
|
### ? Scenario 6: Ferma Tutti
|
||||||
|
|
||||||
|
1. Alcune aste attive
|
||||||
|
2. Clic su "Ferma Tutti"
|
||||||
|
3. ? Tutte le aste vengono fermate
|
||||||
|
4. ? Monitoraggio si ferma
|
||||||
|
5. ? Log: `[STOP ALL] Monitoraggio fermato e tutte le aste arrestate`
|
||||||
|
|
||||||
|
## Vantaggi della Soluzione
|
||||||
|
|
||||||
|
### ?? 1. Maggiore Flessibilità
|
||||||
|
- Puoi avviare solo le aste che ti interessano
|
||||||
|
- Non serve più avviare tutte le aste per monitorarne una
|
||||||
|
|
||||||
|
### ?? 2. Risparmio Risorse
|
||||||
|
- Il monitoraggio si ferma automaticamente quando non serve
|
||||||
|
- Polling solo sulle aste effettivamente attive
|
||||||
|
|
||||||
|
### ?? 3. UX Migliorata
|
||||||
|
- Comportamento più intuitivo
|
||||||
|
- Non serve capire la differenza tra "Avvia Tutti" e "Avvia" singolo
|
||||||
|
|
||||||
|
### ?? 4. Logging Chiaro
|
||||||
|
- Si vede esattamente quando il monitoraggio parte/si ferma
|
||||||
|
- Distingue tra start manuale e automatico
|
||||||
|
|
||||||
|
## File Modificati
|
||||||
|
|
||||||
|
1. ? `Core\MainWindow.Commands.cs`
|
||||||
|
- Aggiunto auto-start in `ExecuteGridStart`
|
||||||
|
- Aggiunto auto-stop in `ExecuteGridStop`
|
||||||
|
- Aggiunta importazione `System.Linq` e `AutoBidder.Utilities`
|
||||||
|
- Migliorato logging con `LogLevel`
|
||||||
|
|
||||||
|
2. ? `Core\MainWindow.ButtonHandlers.cs`
|
||||||
|
- Migliorato logging in `StartButton_Click`
|
||||||
|
- Migliorato logging in `StopButton_Click`
|
||||||
|
- Migliorato logging in `PauseAllButton_Click`
|
||||||
|
|
||||||
|
## Note Tecniche
|
||||||
|
|
||||||
|
### Perché Auto-Start è Sicuro?
|
||||||
|
|
||||||
|
1. **Idempotente**: `AuctionMonitor.Start()` controlla se è già attivo
|
||||||
|
2. **Thread-safe**: Il lock interno previene race conditions
|
||||||
|
3. **Logging**: Si vede esattamente cosa succede
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
if (_monitoringTask != null && !_monitoringTask.IsCompleted)
|
||||||
|
{
|
||||||
|
OnLog?.Invoke("[WARN] Monitoraggio gia' attivo");
|
||||||
|
return; // Non fa nulla se già attivo
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Perché Auto-Stop è Sicuro?
|
||||||
|
|
||||||
|
1. **Controlla tutte le aste**: Verifica se ci sono altre aste attive prima di fermare
|
||||||
|
2. **Non forza**: Se ci sono altre aste attive, non ferma il monitoraggio
|
||||||
|
3. **Graceful**: Usa `Stop()` che fa cleanup corretto
|
||||||
|
|
||||||
|
## Test di Verifica
|
||||||
|
|
||||||
|
- [x] Avviare singola asta da griglia (monitoraggio fermo)
|
||||||
|
- [x] Avviare seconda asta (monitoraggio già attivo)
|
||||||
|
- [x] Fermare asta (altre attive) ? Monitoraggio continua
|
||||||
|
- [x] Fermare ultima asta ? Monitoraggio si ferma
|
||||||
|
- [x] "Avvia Tutti" continua a funzionare
|
||||||
|
- [x] "Ferma Tutti" continua a funzionare
|
||||||
|
- [x] "Pausa Tutti" continua a funzionare
|
||||||
|
- [x] Pulsanti di griglia abilitati/disabilitati correttamente
|
||||||
|
- [x] Log mostra AUTO-START/AUTO-STOP
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Data Fix**: 2025
|
||||||
|
**Versione**: 4.0+
|
||||||
|
**Issue**: Pulsante "Avvia" singolo non funzionava senza "Avvia Tutti"
|
||||||
|
**Status**: ? RISOLTO
|
||||||
|
|
||||||
|
## Riepilogo
|
||||||
|
|
||||||
|
Prima: **Dovevi cliccare "Avvia Tutti" per monitorare anche una sola asta**
|
||||||
|
Dopo: **Clicchi "Avvia" su un'asta e parte automaticamente il monitoraggio** ??
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:controls="clr-namespace:AutoBidder.Controls"
|
xmlns:controls="clr-namespace:AutoBidder.Controls"
|
||||||
Title="AutoBidder v4.0"
|
Title="AutoBidder 4.0"
|
||||||
Height="800"
|
Height="800"
|
||||||
Width="1400"
|
Width="1400"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
@@ -84,6 +84,7 @@
|
|||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="*"/>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<!-- Navigation Tabs -->
|
<!-- Navigation Tabs -->
|
||||||
@@ -120,11 +121,46 @@
|
|||||||
Checked="TabImpostazioni_Checked"/>
|
Checked="TabImpostazioni_Checked"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- User Info Panel - SOPRA AutoBidder -->
|
||||||
|
<Border x:Name="SidebarUserInfoPanel"
|
||||||
|
Grid.Row="1"
|
||||||
|
Background="#2D2D30"
|
||||||
|
BorderBrush="#3E3E42"
|
||||||
|
BorderThickness="0,1,0,1"
|
||||||
|
Padding="15,12"
|
||||||
|
Visibility="Collapsed">
|
||||||
|
<StackPanel>
|
||||||
|
<!-- Riga 1: Username + ID -->
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,0,0,4">
|
||||||
|
<TextBlock x:Name="SidebarUsernameText"
|
||||||
|
Text="sirbietole23"
|
||||||
|
Foreground="#00D800"
|
||||||
|
FontSize="11"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
TextTrimming="CharacterEllipsis"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Riga 2: ID + Email -->
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<TextBlock x:Name="SidebarUserIdText"
|
||||||
|
Text="ID: 6707664"
|
||||||
|
Foreground="#666666"
|
||||||
|
FontSize="9"
|
||||||
|
Margin="0,0,0,2"/>
|
||||||
|
<TextBlock x:Name="SidebarUserEmailText"
|
||||||
|
Text="email@email.com"
|
||||||
|
Foreground="#999999"
|
||||||
|
FontSize="9"
|
||||||
|
TextTrimming="CharacterEllipsis"/>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
<!-- App Title & Logo (Bottom) -->
|
<!-- App Title & Logo (Bottom) -->
|
||||||
<Border Grid.Row="1" Background="#007ACC" Padding="15,20" BorderBrush="#3E3E42" BorderThickness="0,1,0,0">
|
<Border Grid.Row="2" Background="#007ACC" Padding="15,20" BorderBrush="#3E3E42" BorderThickness="0,1,0,0">
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
|
||||||
<Image Source="Icon/favicon.ico" Width="24" Height="24" Margin="0,0,10,0"/>
|
<Image Source="Icon/favicon.ico" Width="24" Height="24" Margin="0,0,10,0"/>
|
||||||
<TextBlock Text="AutoBidder v4.0"
|
<TextBlock Text="AutoBidder"
|
||||||
FontSize="16"
|
FontSize="16"
|
||||||
FontWeight="Bold"
|
FontWeight="Bold"
|
||||||
Foreground="White"
|
Foreground="White"
|
||||||
@@ -139,6 +175,10 @@
|
|||||||
<!-- Aste Attive Panel -->
|
<!-- Aste Attive Panel -->
|
||||||
<controls:AuctionMonitorControl x:Name="AuctionMonitor"
|
<controls:AuctionMonitorControl x:Name="AuctionMonitor"
|
||||||
Visibility="Visible"
|
Visibility="Visible"
|
||||||
|
GridStartCommand="{Binding GridStartCommand}"
|
||||||
|
GridPauseCommand="{Binding GridPauseCommand}"
|
||||||
|
GridStopCommand="{Binding GridStopCommand}"
|
||||||
|
GridBidCommand="{Binding GridBidCommand}"
|
||||||
StartClicked="AuctionMonitor_StartClicked"
|
StartClicked="AuctionMonitor_StartClicked"
|
||||||
PauseAllClicked="AuctionMonitor_PauseAllClicked"
|
PauseAllClicked="AuctionMonitor_PauseAllClicked"
|
||||||
StopClicked="AuctionMonitor_StopClicked"
|
StopClicked="AuctionMonitor_StopClicked"
|
||||||
@@ -150,7 +190,12 @@
|
|||||||
ResetSettingsClicked="AuctionMonitor_ResetSettingsClicked"
|
ResetSettingsClicked="AuctionMonitor_ResetSettingsClicked"
|
||||||
ClearBiddersClicked="AuctionMonitor_ClearBiddersClicked"
|
ClearBiddersClicked="AuctionMonitor_ClearBiddersClicked"
|
||||||
ClearLogClicked="AuctionMonitor_ClearLogClicked"
|
ClearLogClicked="AuctionMonitor_ClearLogClicked"
|
||||||
ClearGlobalLogClicked="AuctionMonitor_ClearGlobalLogClicked"/>
|
ClearGlobalLogClicked="AuctionMonitor_ClearGlobalLogClicked"
|
||||||
|
BidBeforeDeadlineMsChanged="AuctionMonitor_BidBeforeDeadlineMsChanged"
|
||||||
|
CheckAuctionOpenChanged="AuctionMonitor_CheckAuctionOpenChanged"
|
||||||
|
MinPriceChanged="AuctionMonitor_MinPriceChanged"
|
||||||
|
MaxPriceChanged="AuctionMonitor_MaxPriceChanged"
|
||||||
|
MaxClicksChanged="AuctionMonitor_MaxClicksChanged"/>
|
||||||
|
|
||||||
<!-- Browser Panel -->
|
<!-- Browser Panel -->
|
||||||
<controls:BrowserControl x:Name="Browser"
|
<controls:BrowserControl x:Name="Browser"
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ namespace AutoBidder
|
|||||||
// UI State
|
// UI State
|
||||||
private AuctionViewModel? _selectedAuction;
|
private AuctionViewModel? _selectedAuction;
|
||||||
private bool _isAutomationActive = false;
|
private bool _isAutomationActive = false;
|
||||||
|
private bool _isUpdatingSelection = false; // Previene loop di salvataggio
|
||||||
|
|
||||||
// Commands (initialized in MainWindow.Commands.cs)
|
// Commands (initialized in MainWindow.Commands.cs)
|
||||||
public RelayCommand? StartAllCommand { get; private set; }
|
public RelayCommand? StartAllCommand { get; private set; }
|
||||||
@@ -47,9 +48,8 @@ namespace AutoBidder
|
|||||||
public DataGrid SelectedAuctionBiddersGrid => AuctionMonitor.SelectedAuctionBiddersGrid;
|
public DataGrid SelectedAuctionBiddersGrid => AuctionMonitor.SelectedAuctionBiddersGrid;
|
||||||
public Button ClearBiddersButton => AuctionMonitor.ClearBiddersButton;
|
public Button ClearBiddersButton => AuctionMonitor.ClearBiddersButton;
|
||||||
public TextBlock MonitorateTitle => AuctionMonitor.MonitorateTitle;
|
public TextBlock MonitorateTitle => AuctionMonitor.MonitorateTitle;
|
||||||
public TextBlock UsernameText => AuctionMonitor.UsernameText;
|
public TextBox SelectedBidBeforeDeadlineMs => AuctionMonitor.SelectedBidBeforeDeadlineMs;
|
||||||
public TextBox SelectedTimerClick => AuctionMonitor.SelectedTimerClick;
|
public CheckBox SelectedCheckAuctionOpen => AuctionMonitor.SelectedCheckAuctionOpen;
|
||||||
public TextBox SelectedDelayMs => AuctionMonitor.SelectedDelayMs;
|
|
||||||
public TextBox SelectedMinPrice => AuctionMonitor.SelectedMinPrice;
|
public TextBox SelectedMinPrice => AuctionMonitor.SelectedMinPrice;
|
||||||
public TextBox SelectedMaxPrice => AuctionMonitor.SelectedMaxPrice;
|
public TextBox SelectedMaxPrice => AuctionMonitor.SelectedMaxPrice;
|
||||||
public TextBox SelectedMaxClicks => AuctionMonitor.SelectedMaxClicks;
|
public TextBox SelectedMaxClicks => AuctionMonitor.SelectedMaxClicks;
|
||||||
@@ -78,12 +78,12 @@ namespace AutoBidder
|
|||||||
public CheckBox RemoveAfterExport => Settings.RemoveAfterExport;
|
public CheckBox RemoveAfterExport => Settings.RemoveAfterExport;
|
||||||
public CheckBox OverwriteExisting => Settings.OverwriteExisting;
|
public CheckBox OverwriteExisting => Settings.OverwriteExisting;
|
||||||
|
|
||||||
// Impostazioni predefinite aste
|
// Impostazioni predefinite aste - AGGIORNATO
|
||||||
public TextBox DefaultTimerClick => Settings.DefaultTimerClick;
|
public TextBox DefaultBidBeforeDeadlineMs => Settings.DefaultBidBeforeDeadlineMsTextBox;
|
||||||
public TextBox DefaultDelayMs => Settings.DefaultDelayMs;
|
public CheckBox DefaultCheckAuctionOpen => Settings.DefaultCheckAuctionOpenCheckBox;
|
||||||
public TextBox DefaultMinPrice => Settings.DefaultMinPrice;
|
public TextBox DefaultMinPrice => Settings.DefaultMinPriceTextBox;
|
||||||
public TextBox DefaultMaxPrice => Settings.DefaultMaxPrice;
|
public TextBox DefaultMaxPrice => Settings.DefaultMaxPriceTextBox;
|
||||||
public TextBox DefaultMaxClicks => Settings.DefaultMaxClicks;
|
public TextBox DefaultMaxClicks => Settings.DefaultMaxClicksTextBox;
|
||||||
|
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,8 +16,18 @@ namespace AutoBidder.Models
|
|||||||
public string OriginalUrl { get; set; } = ""; // URL completo dell'asta (per referer)
|
public string OriginalUrl { get; set; } = ""; // URL completo dell'asta (per referer)
|
||||||
|
|
||||||
// Configurazione asta
|
// Configurazione asta
|
||||||
public int TimerClick { get; set; } = 0; // Secondo del timer per click (default 0)
|
/// <summary>
|
||||||
public int DelayMs { get; set; } = 50; // Ritardo aggiuntivo in ms (per compensare latenza)
|
/// Millisecondi prima della scadenza (deadline) per inviare la puntata.
|
||||||
|
/// Es: 200ms = punta 200ms prima che il timer raggiunga 0.
|
||||||
|
/// </summary>
|
||||||
|
public int BidBeforeDeadlineMs { get; set; } = 200;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Se true, verifica che l'asta sia ancora aperta prima di piazzare la puntata.
|
||||||
|
/// Aggiunge una chiamata API extra ma aumenta la sicurezza.
|
||||||
|
/// </summary>
|
||||||
|
public bool CheckAuctionOpenBeforeBid { get; set; } = false;
|
||||||
|
|
||||||
public double MinPrice { get; set; } = 0;
|
public double MinPrice { get; set; } = 0;
|
||||||
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
|
||||||
@@ -40,8 +50,6 @@ namespace AutoBidder.Models
|
|||||||
public List<BidHistory> BidHistory { get; set; } = new List<BidHistory>();
|
public List<BidHistory> BidHistory { get; set; } = new List<BidHistory>();
|
||||||
public Dictionary<string, BidderInfo> BidderStats { get; set; } = new(StringComparer.OrdinalIgnoreCase);
|
public Dictionary<string, BidderInfo> BidderStats { get; set; } = new(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
// Legacy (deprecato) - removed `Bidders` dictionary; use `BidderStats` instead
|
|
||||||
|
|
||||||
// Log per-asta (non serializzato)
|
// Log per-asta (non serializzato)
|
||||||
[System.Text.Json.Serialization.JsonIgnore]
|
[System.Text.Json.Serialization.JsonIgnore]
|
||||||
public List<string> AuctionLog { get; set; } = new();
|
public List<string> AuctionLog { get; set; } = new();
|
||||||
@@ -50,17 +58,12 @@ 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>
|
||||||
public void AddLog(string message)
|
public void AddLog(string message)
|
||||||
{
|
{
|
||||||
var entry = $"{DateTime.Now:HH:mm:ss} - {message}";
|
var entry = $"{DateTime.Now:HH:mm:ss.fff} - {message}";
|
||||||
AuctionLog.Add(entry);
|
AuctionLog.Add(entry);
|
||||||
|
|
||||||
// Mantieni solo ultimi 500 log
|
// Mantieni solo ultimi 500 log
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using System;
|
|||||||
namespace AutoBidder.Models
|
namespace AutoBidder.Models
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sessione Bidoo con token di autenticazione
|
/// Sessione Bidoo con token di autenticazione e dati utente completi
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class BidooSession
|
public class BidooSession
|
||||||
{
|
{
|
||||||
@@ -24,11 +24,31 @@ namespace AutoBidder.Models
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string Username { get; set; } = "";
|
public string Username { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Email dell'utente
|
||||||
|
/// </summary>
|
||||||
|
public string Email { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Numero di telefono dell'utente
|
||||||
|
/// </summary>
|
||||||
|
public string Phone { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ID numerico univoco dell'utente
|
||||||
|
/// </summary>
|
||||||
|
public int UserId { get; set; } = 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Puntate rimanenti sull'account
|
/// Puntate rimanenti sull'account
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int RemainingBids { get; set; } = 0;
|
public int RemainingBids { get; set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Credito disponibile nel Bidoo Shop (€)
|
||||||
|
/// </summary>
|
||||||
|
public double ShopCredit { get; set; } = 0.0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Timestamp ultimo aggiornamento info account
|
/// Timestamp ultimo aggiornamento info account
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -46,3 +66,4 @@ namespace AutoBidder.Models
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace AutoBidder.Services
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Servizio centrale per monitoraggio aste
|
/// Servizio centrale per monitoraggio aste
|
||||||
/// Solo HTTP, nessuna modalità, browser o multi-click
|
/// Sistema di timing ottimizzato: punta solo se necessario, poco prima della scadenza
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AuctionMonitor
|
public class AuctionMonitor
|
||||||
{
|
{
|
||||||
@@ -21,13 +21,12 @@ namespace AutoBidder.Services
|
|||||||
public event Action<AuctionState>? OnAuctionUpdated;
|
public event Action<AuctionState>? OnAuctionUpdated;
|
||||||
public event Action<AuctionInfo, BidResult>? OnBidExecuted;
|
public event Action<AuctionInfo, BidResult>? OnBidExecuted;
|
||||||
public event Action<string>? OnLog;
|
public event Action<string>? OnLog;
|
||||||
public event Action<string>? OnResetCountChanged; // Notifica cambio contatore reset
|
public event Action<string>? OnResetCountChanged;
|
||||||
|
|
||||||
public AuctionMonitor()
|
public AuctionMonitor()
|
||||||
{
|
{
|
||||||
_apiClient = new BidooApiClient();
|
_apiClient = new BidooApiClient();
|
||||||
|
|
||||||
// Subscribe to detailed per-auction logs from API client
|
|
||||||
_apiClient.OnAuctionLog += (auctionId, message) =>
|
_apiClient.OnAuctionLog += (auctionId, message) =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -45,59 +44,38 @@ namespace AutoBidder.Services
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Inizializza sessione con token di autenticazione
|
|
||||||
/// </summary>
|
|
||||||
public void InitializeSession(string authToken, string username)
|
public void InitializeSession(string authToken, string username)
|
||||||
{
|
{
|
||||||
_apiClient.InitializeSession(authToken, username);
|
_apiClient.InitializeSession(authToken, username);
|
||||||
OnLog?.Invoke($"[OK] Sessione configurata per: {username}");
|
OnLog?.Invoke($"[OK] Sessione configurata per: {username}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Inizializza sessione con cookie (fallback legacy)
|
|
||||||
/// </summary>
|
|
||||||
public void InitializeSessionWithCookie(string cookieString, string username)
|
public void InitializeSessionWithCookie(string cookieString, string username)
|
||||||
{
|
{
|
||||||
_apiClient.InitializeSessionWithCookie(cookieString, username);
|
_apiClient.InitializeSessionWithCookie(cookieString, username);
|
||||||
OnLog?.Invoke($"[OK] Sessione configurata (cookie) per: {username}");
|
OnLog?.Invoke($"[OK] Sessione configurata (cookie) per: {username}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Aggiorna info utente (puntate rimanenti)
|
|
||||||
/// </summary>
|
|
||||||
public async Task<bool> UpdateUserInfoAsync()
|
public async Task<bool> UpdateUserInfoAsync()
|
||||||
{
|
{
|
||||||
return await _apiClient.UpdateUserInfoAsync();
|
return await _apiClient.UpdateUserInfoAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ottieni sessione corrente
|
|
||||||
/// </summary>
|
|
||||||
public BidooSession GetSession()
|
public BidooSession GetSession()
|
||||||
{
|
{
|
||||||
return _apiClient.GetSession();
|
return _apiClient.GetSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ottiene dati utente (nome, puntate residue, saldo, id) tramite chiamata AJAX leggera
|
|
||||||
/// </summary>
|
|
||||||
public async Task<UserData?> GetUserDataAsync()
|
public async Task<UserData?> GetUserDataAsync()
|
||||||
{
|
{
|
||||||
return await _apiClient.GetUserDataAsync();
|
return await _apiClient.GetUserDataAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ottiene info banner utente (aste vinte, bonus, ecc.) tramite chiamata AJAX
|
|
||||||
/// </summary>
|
|
||||||
public async Task<UserBannerInfo?> GetUserBannerInfoAsync()
|
public async Task<UserBannerInfo?> GetUserBannerInfoAsync()
|
||||||
{
|
{
|
||||||
return await _apiClient.GetUserBannerInfoAsync();
|
return await _apiClient.GetUserBannerInfoAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Estrae nome utente e puntate residue dall'HTML di bids_history.php
|
|
||||||
/// </summary>
|
|
||||||
public async Task<UserData?> GetUserDataFromHtmlAsync()
|
public async Task<UserData?> GetUserDataFromHtmlAsync()
|
||||||
{
|
{
|
||||||
return await _apiClient.GetUserDataFromHtmlAsync();
|
return await _apiClient.GetUserDataFromHtmlAsync();
|
||||||
@@ -167,9 +145,6 @@ namespace AutoBidder.Services
|
|||||||
List<AuctionInfo> activeAuctions;
|
List<AuctionInfo> activeAuctions;
|
||||||
lock (_auctions)
|
lock (_auctions)
|
||||||
{
|
{
|
||||||
// Filtra aste che devono ancora essere monitorate
|
|
||||||
// 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 &&
|
||||||
!IsAuctionTerminated(a)
|
!IsAuctionTerminated(a)
|
||||||
@@ -189,16 +164,14 @@ namespace AutoBidder.Services
|
|||||||
// Ottimizzazione polling per 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;
|
||||||
foreach (var a in activeAuctions)
|
foreach (var a in activeAuctions)
|
||||||
{
|
{
|
||||||
if (a.IsPaused)
|
if (a.IsPaused)
|
||||||
{
|
{
|
||||||
anyPaused = true;
|
anyPaused = true;
|
||||||
// Se tra le 00:00 e le 09:55 polling ogni 60s
|
|
||||||
if (now.Hour < 9 || (now.Hour == 9 && now.Minute < 55))
|
if (now.Hour < 9 || (now.Hour == 9 && now.Minute < 55))
|
||||||
pauseDelayMs = 60000;
|
pauseDelayMs = 60000;
|
||||||
// Negli ultimi 5 minuti prima delle 10 polling ogni 5s
|
|
||||||
else if (now.Hour == 9 && now.Minute >= 55)
|
else if (now.Hour == 9 && now.Minute >= 55)
|
||||||
pauseDelayMs = 5000;
|
pauseDelayMs = 5000;
|
||||||
}
|
}
|
||||||
@@ -218,13 +191,14 @@ namespace AutoBidder.Services
|
|||||||
|
|
||||||
int delayMs = lowestTimer switch
|
int delayMs = lowestTimer switch
|
||||||
{
|
{
|
||||||
< 1 => 5, // Iper-veloce: polling ogni 5ms (0-1s rimanenti)
|
< 0.5 => 10, // Ultra-critico: polling ogni 10ms
|
||||||
< 2 => 20, // Ultra-veloce: polling ogni 20ms (1-2s)
|
< 1 => 20, // Iper-veloce: polling ogni 20ms
|
||||||
< 3 => 50, // Molto veloce: polling ogni 50ms (2-3s)
|
< 2 => 50, // Ultra-veloce: polling ogni 50ms
|
||||||
< 5 => 100, // Veloce: polling ogni 100ms (3-5s)
|
< 3 => 100, // Molto veloce: polling ogni 100ms
|
||||||
< 10 => 200, // Medio: polling ogni 200ms (5-10s)
|
< 5 => 150, // Veloce: polling ogni 150ms
|
||||||
< 30 => 500, // Lento: polling ogni 500ms (10-30s)
|
< 10 => 300, // Medio: polling ogni 300ms
|
||||||
_ => 1000 // Molto lento: polling ogni 1s (>30s)
|
< 30 => 500, // Lento: polling ogni 500ms
|
||||||
|
_ => 1000 // Molto lento: polling ogni 1s
|
||||||
};
|
};
|
||||||
|
|
||||||
await Task.Delay(delayMs, token);
|
await Task.Delay(delayMs, token);
|
||||||
@@ -241,16 +215,11 @@ namespace AutoBidder.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Verifica se un'asta è terminata e non deve più essere monitorata
|
|
||||||
/// </summary>
|
|
||||||
private bool IsAuctionTerminated(AuctionInfo auction)
|
private bool IsAuctionTerminated(AuctionInfo auction)
|
||||||
{
|
{
|
||||||
// Se l'ultima entry nello storico indica uno stato finale, ferma polling
|
|
||||||
var lastHistory = auction.BidHistory.LastOrDefault();
|
var lastHistory = auction.BidHistory.LastOrDefault();
|
||||||
if (lastHistory != null)
|
if (lastHistory != null)
|
||||||
{
|
{
|
||||||
// Controlla se c'è una nota che indica fine asta
|
|
||||||
if (lastHistory.Notes != null &&
|
if (lastHistory.Notes != null &&
|
||||||
(lastHistory.Notes.Contains("VINTA") ||
|
(lastHistory.Notes.Contains("VINTA") ||
|
||||||
lastHistory.Notes.Contains("Persa") ||
|
lastHistory.Notes.Contains("Persa") ||
|
||||||
@@ -267,7 +236,6 @@ namespace AutoBidder.Services
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Poll tramite API Bidoo (passa anche l'URL originale per referer corretto)
|
|
||||||
var state = await _apiClient.PollAuctionStateAsync(auction.AuctionId, auction.OriginalUrl, token);
|
var state = await _apiClient.PollAuctionStateAsync(auction.AuctionId, auction.OriginalUrl, token);
|
||||||
|
|
||||||
if (state == null)
|
if (state == null)
|
||||||
@@ -277,10 +245,8 @@ namespace AutoBidder.Services
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aggiorna la latenza per la DataGrid
|
|
||||||
auction.PollingLatencyMs = state.PollingLatencyMs;
|
auction.PollingLatencyMs = state.PollingLatencyMs;
|
||||||
|
|
||||||
// Se l'asta è terminata, segnala e disattiva polling
|
|
||||||
if (state.Status == AuctionStatus.EndedWon ||
|
if (state.Status == AuctionStatus.EndedWon ||
|
||||||
state.Status == AuctionStatus.EndedLost ||
|
state.Status == AuctionStatus.EndedLost ||
|
||||||
state.Status == AuctionStatus.Closed)
|
state.Status == AuctionStatus.Closed)
|
||||||
@@ -288,13 +254,10 @@ namespace AutoBidder.Services
|
|||||||
string statusMsg = state.Status == AuctionStatus.EndedWon ? "VINTA" :
|
string statusMsg = state.Status == AuctionStatus.EndedWon ? "VINTA" :
|
||||||
state.Status == AuctionStatus.EndedLost ? "Persa" : "Chiusa";
|
state.Status == AuctionStatus.EndedLost ? "Persa" : "Chiusa";
|
||||||
|
|
||||||
// Mark auction inactive immediately to stop further polling
|
|
||||||
auction.IsActive = false;
|
auction.IsActive = false;
|
||||||
|
|
||||||
auction.AddLog($"[ASTA TERMINATA] {statusMsg}");
|
auction.AddLog($"[ASTA TERMINATA] {statusMsg}");
|
||||||
OnLog?.Invoke($"[FINE] [{auction.AuctionId}] Asta {statusMsg} - Polling fermato");
|
OnLog?.Invoke($"[FINE] [{auction.AuctionId}] Asta {statusMsg} - Polling fermato");
|
||||||
|
|
||||||
// Aggiungi entry nello storico per marcare come terminata
|
|
||||||
auction.BidHistory.Add(new BidHistory
|
auction.BidHistory.Add(new BidHistory
|
||||||
{
|
{
|
||||||
Timestamp = DateTime.UtcNow,
|
Timestamp = DateTime.UtcNow,
|
||||||
@@ -305,222 +268,148 @@ namespace AutoBidder.Services
|
|||||||
Notes = $"Asta {statusMsg}"
|
Notes = $"Asta {statusMsg}"
|
||||||
});
|
});
|
||||||
|
|
||||||
// Notifica UI e fermati
|
|
||||||
OnAuctionUpdated?.Invoke(state);
|
OnAuctionUpdated?.Invoke(state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log stato solo per aste attive (riduci spam) - keep detailed per-auction log
|
|
||||||
if (state.Status == AuctionStatus.Running)
|
if (state.Status == AuctionStatus.Running)
|
||||||
{
|
{
|
||||||
// Detailed info stays in auction log only
|
// Log RIMOSSO per ridurre verbosità - polling continuo non necessita log
|
||||||
auction.AddLog($"API OK - Timer: {state.Timer:F2}s, EUR{state.Price:F2}, {state.LastBidder}, {state.PollingLatencyMs}ms");
|
// Solo eventi importanti (bid, reset, errori) vengono loggati
|
||||||
}
|
}
|
||||||
else if (state.Status == AuctionStatus.Paused)
|
else if (state.Status == AuctionStatus.Paused)
|
||||||
{
|
{
|
||||||
auction.AddLog($"[PAUSA] Asta in pausa - Timer: {state.Timer:F2}s, EUR{state.Price:F2}");
|
// Log solo primo cambio stato, non ad ogni polling
|
||||||
|
var lastLog = auction.AuctionLog.LastOrDefault();
|
||||||
|
if (lastLog == null || !lastLog.Contains("[PAUSA]"))
|
||||||
|
{
|
||||||
|
auction.AddLog($"[PAUSA] Asta in pausa - Timer: {state.Timer:F3}s, EUR{state.Price:F2}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notifica aggiornamento UI
|
|
||||||
OnAuctionUpdated?.Invoke(state);
|
OnAuctionUpdated?.Invoke(state);
|
||||||
|
|
||||||
// Aggiorna storico e bidders
|
|
||||||
UpdateAuctionHistory(auction, state);
|
UpdateAuctionHistory(auction, state);
|
||||||
|
|
||||||
// FINAL-ATTACK PROTOCOL: when the remaining timer is below our latency threshold (<= 0.5s)
|
// NUOVA LOGICA: Punta solo se siamo vicini alla deadline E nessun altro ha appena puntato
|
||||||
// we stop the normal polling loop for this auction and send a single minimal bid request.
|
if (state.Status == AuctionStatus.Running && !auction.IsPaused && !auction.IsAttackInProgress)
|
||||||
if (state.Status == AuctionStatus.Running && !auction.IsPaused && ShouldBid(auction, state))
|
|
||||||
{
|
{
|
||||||
// Use latency threshold (0.5s default) - treat as critical window
|
if (ShouldBid(auction, state))
|
||||||
var latencyThreshold = 0.5; // seconds
|
|
||||||
if (!auction.IsAttackInProgress && state.Timer <= latencyThreshold)
|
|
||||||
{
|
{
|
||||||
// Quick re-poll strategy: perform a couple of fast re-polls to confirm that the timer
|
await ExecuteBidStrategy(auction, state, token);
|
||||||
// is still in the critical window and that the lastBidder did not change.
|
|
||||||
auction.IsAttackInProgress = true;
|
|
||||||
AuctionState? lastConfirmedState = state;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auction.AddLog($"[FAIL] Final bid failed: {finalResult.Error}");
|
|
||||||
OnLog?.Invoke($"[FAIL] Puntata fallita su {auction.Name} ({auction.AuctionId}): {finalResult.Error}");
|
|
||||||
}
|
|
||||||
|
|
||||||
auction.BidHistory.Add(new BidHistory
|
|
||||||
{
|
|
||||||
Timestamp = finalResult.Timestamp,
|
|
||||||
EventType = finalResult.Success ? BidEventType.MyBid : BidEventType.OpponentBid,
|
|
||||||
Bidder = "Tu",
|
|
||||||
Price = state.Price,
|
|
||||||
Timer = state.Timer,
|
|
||||||
LatencyMs = finalResult.LatencyMs,
|
|
||||||
Success = finalResult.Success,
|
|
||||||
Notes = finalResult.Success ? $"EUR{finalResult.NewPrice:F2}" : finalResult.Error
|
|
||||||
});
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
auction.IsAttackInProgress = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise fallback to normal early-bid behavior
|
|
||||||
if (Math.Abs(state.Timer) < 0.001)
|
|
||||||
{
|
|
||||||
// Put detailed info into auction log but avoid noisy global log lines
|
|
||||||
auction.AddLog($"[TRIGGER] Timer 0, attendo delay {auction.DelayMs}ms e invio puntata direttamente...");
|
|
||||||
|
|
||||||
if (auction.DelayMs > 0)
|
|
||||||
await Task.Delay(auction.DelayMs, token);
|
|
||||||
|
|
||||||
// Direct bid - API client already writes detailed request/response into auction.AddLog via subscription
|
|
||||||
var result = await _apiClient.PlaceBidAsync(auction.AuctionId);
|
|
||||||
auction.LastClickAt = DateTime.UtcNow;
|
|
||||||
OnBidExecuted?.Invoke(auction, result);
|
|
||||||
|
|
||||||
// Add concise global log (single line) and keep extended details inside auction log
|
|
||||||
if (result.Success)
|
|
||||||
{
|
|
||||||
auction.AddLog($"[OK] PUNTATA OK: {result.LatencyMs}ms -> EUR{result.NewPrice:F2}");
|
|
||||||
OnLog?.Invoke($"[OK] Puntata riuscita su {auction.Name} ({auction.AuctionId}): {result.LatencyMs}ms");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auction.AddLog($"[FAIL] PUNTATA FALLITA: {result.Error}");
|
|
||||||
OnLog?.Invoke($"[FAIL] Puntata fallita su {auction.Name} ({auction.AuctionId}): {result.Error}");
|
|
||||||
}
|
|
||||||
|
|
||||||
auction.BidHistory.Add(new BidHistory
|
|
||||||
{
|
|
||||||
Timestamp = result.Timestamp,
|
|
||||||
EventType = result.Success ? BidEventType.MyBid : BidEventType.OpponentBid,
|
|
||||||
Bidder = "Tu",
|
|
||||||
Price = state.Price,
|
|
||||||
Timer = state.Timer,
|
|
||||||
LatencyMs = result.LatencyMs,
|
|
||||||
Success = result.Success,
|
|
||||||
Notes = result.Success ? $"EUR{result.NewPrice:F2}" : result.Error
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Normal early-bid path: schedule immediate delay then place bid
|
|
||||||
auction.AddLog($"[TRIGGER] CONDIZIONI OK - Timer {state.Timer:F2}s <= {auction.TimerClick}s");
|
|
||||||
|
|
||||||
if (auction.DelayMs > 0)
|
|
||||||
{
|
|
||||||
await Task.Delay(auction.DelayMs, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = await _apiClient.PlaceBidAsync(auction.AuctionId);
|
|
||||||
auction.LastClickAt = DateTime.UtcNow;
|
|
||||||
OnBidExecuted?.Invoke(auction, result);
|
|
||||||
|
|
||||||
if (result.Success)
|
|
||||||
{
|
|
||||||
auction.AddLog($"[OK] PUNTATA OK: {result.LatencyMs}ms -> EUR{result.NewPrice:F2}");
|
|
||||||
OnLog?.Invoke($"[OK] Puntata riuscita su {auction.Name} ({auction.AuctionId}): {result.LatencyMs}ms");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auction.AddLog($"[FAIL] PUNTATA FALLITA: {result.Error}");
|
|
||||||
OnLog?.Invoke($"[FAIL] Puntata fallita su {auction.Name} ({auction.AuctionId}): {result.Error}");
|
|
||||||
}
|
|
||||||
|
|
||||||
auction.BidHistory.Add(new BidHistory
|
|
||||||
{
|
|
||||||
Timestamp = result.Timestamp,
|
|
||||||
EventType = result.Success ? BidEventType.MyBid : BidEventType.OpponentBid,
|
|
||||||
Bidder = "Tu",
|
|
||||||
Price = state.Price,
|
|
||||||
Timer = state.Timer,
|
|
||||||
LatencyMs = result.LatencyMs,
|
|
||||||
Success = result.Success,
|
|
||||||
Notes = result.Success ? $"EUR{result.NewPrice:F2}" : result.Error
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
auction.AddLog($"[EXCEPTION] ERRORE: {ex.Message}");
|
auction.AddLog($"[EXCEPTION] {ex.Message}");
|
||||||
OnLog?.Invoke($"[EXCEPTION] [{auction.AuctionId}] {ex.Message}");
|
OnLog?.Invoke($"[EXCEPTION] [{auction.AuctionId}] {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Strategia di puntata ottimizzata: punta solo quando necessario
|
||||||
|
/// </summary>
|
||||||
|
private async Task ExecuteBidStrategy(AuctionInfo auction, AuctionState state, CancellationToken token)
|
||||||
|
{
|
||||||
|
// Calcola il tempo rimanente in millisecondi
|
||||||
|
double timerMs = state.Timer * 1000;
|
||||||
|
|
||||||
|
// Se siamo nella finestra di puntata (timer <= BidBeforeDeadlineMs)
|
||||||
|
if (timerMs <= auction.BidBeforeDeadlineMs)
|
||||||
|
{
|
||||||
|
auction.IsAttackInProgress = true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auction.AddLog($"[STRATEGIA] Finestra di puntata raggiunta: {timerMs:F0}ms <= {auction.BidBeforeDeadlineMs}ms");
|
||||||
|
|
||||||
|
// Controlla se qualcun altro ha puntato di recente
|
||||||
|
var lastBidTime = GetLastBidTime(auction, state.LastBidder);
|
||||||
|
if (lastBidTime.HasValue)
|
||||||
|
{
|
||||||
|
var timeSinceLastBid = DateTime.UtcNow - lastBidTime.Value;
|
||||||
|
if (timeSinceLastBid.TotalMilliseconds < 500)
|
||||||
|
{
|
||||||
|
auction.AddLog($"[STRATEGIA] Puntata recente di {state.LastBidder} ({timeSinceLastBid.TotalMilliseconds:F0}ms fa), attendo...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Esegui la puntata
|
||||||
|
await ExecuteBid(auction, state, token);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
auction.IsAttackInProgress = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Esegue la puntata con verifica opzionale dello stato dell'asta
|
||||||
|
/// </summary>
|
||||||
|
private async Task ExecuteBid(AuctionInfo auction, AuctionState state, CancellationToken token)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Se richiesto, verifica prima che l'asta sia ancora aperta
|
||||||
|
if (auction.CheckAuctionOpenBeforeBid)
|
||||||
|
{
|
||||||
|
auction.AddLog("[PRE-CHECK] Verifica stato asta...");
|
||||||
|
var preCheckState = await _apiClient.PollAuctionStateAsync(auction.AuctionId, auction.OriginalUrl, token);
|
||||||
|
|
||||||
|
if (preCheckState == null)
|
||||||
|
{
|
||||||
|
auction.AddLog("[PRE-CHECK] FALLITO: Nessuna risposta");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preCheckState.Status != AuctionStatus.Running)
|
||||||
|
{
|
||||||
|
auction.AddLog($"[PRE-CHECK] ABORTITO: Asta non running (status: {preCheckState.Status})");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auction.AddLog($"[PRE-CHECK] OK - Timer: {preCheckState.Timer:F3}s");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Esegui la puntata
|
||||||
|
var result = await _apiClient.PlaceBidAsync(auction.AuctionId, auction.OriginalUrl);
|
||||||
|
auction.LastClickAt = DateTime.UtcNow;
|
||||||
|
OnBidExecuted?.Invoke(auction, result);
|
||||||
|
|
||||||
|
if (result.Success)
|
||||||
|
{
|
||||||
|
auction.AddLog($"[BID OK] Latenza: {result.LatencyMs}ms -> EUR{result.NewPrice:F2}");
|
||||||
|
OnLog?.Invoke($"[OK] Puntata riuscita su {auction.Name} ({auction.AuctionId}): {result.LatencyMs}ms");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auction.AddLog($"[BID FAIL] {result.Error}");
|
||||||
|
OnLog?.Invoke($"[FAIL] Puntata fallita su {auction.Name} ({auction.AuctionId}): {result.Error}");
|
||||||
|
}
|
||||||
|
|
||||||
|
auction.BidHistory.Add(new BidHistory
|
||||||
|
{
|
||||||
|
Timestamp = result.Timestamp,
|
||||||
|
EventType = result.Success ? BidEventType.MyBid : BidEventType.OpponentBid,
|
||||||
|
Bidder = "Tu",
|
||||||
|
Price = state.Price,
|
||||||
|
Timer = state.Timer,
|
||||||
|
LatencyMs = result.LatencyMs,
|
||||||
|
Success = result.Success,
|
||||||
|
Notes = result.Success ? $"EUR{result.NewPrice:F2}" : result.Error
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
auction.AddLog($"[BID EXCEPTION] {ex.Message}");
|
||||||
|
OnLog?.Invoke($"[BID EXCEPTION] [{auction.AuctionId}] {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool ShouldBid(AuctionInfo auction, AuctionState state)
|
private bool ShouldBid(AuctionInfo auction, AuctionState state)
|
||||||
{
|
{
|
||||||
// Timer check
|
|
||||||
if (state.Timer > auction.TimerClick)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Price check
|
// Price check
|
||||||
if (auction.MinPrice > 0 && state.Price < auction.MinPrice)
|
if (auction.MinPrice > 0 && state.Price < auction.MinPrice)
|
||||||
return false;
|
return false;
|
||||||
@@ -528,34 +417,57 @@ namespace AutoBidder.Services
|
|||||||
if (auction.MaxPrice > 0 && state.Price > auction.MaxPrice)
|
if (auction.MaxPrice > 0 && state.Price > auction.MaxPrice)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Cooldown check (evita click multipli ravvicinati)
|
// Reset count check
|
||||||
|
if (auction.MinResets > 0 && auction.ResetCount < auction.MinResets)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (auction.MaxResets > 0 && auction.ResetCount >= auction.MaxResets)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Max clicks check
|
||||||
|
int myBidsCount = auction.BidHistory.Count(b => b.EventType == BidEventType.MyBid);
|
||||||
|
if (auction.MaxClicks > 0 && myBidsCount >= auction.MaxClicks)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Cooldown check (evita puntate multiple ravvicinate)
|
||||||
if (auction.LastClickAt.HasValue)
|
if (auction.LastClickAt.HasValue)
|
||||||
{
|
{
|
||||||
var timeSinceLastClick = DateTime.UtcNow - auction.LastClickAt.Value;
|
var timeSinceLastClick = DateTime.UtcNow - auction.LastClickAt.Value;
|
||||||
if (timeSinceLastClick.TotalSeconds < 1)
|
if (timeSinceLastClick.TotalMilliseconds < 800)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DateTime? GetLastBidTime(AuctionInfo auction, string bidder)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(bidder))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (auction.BidderStats.TryGetValue(bidder, out var info))
|
||||||
|
{
|
||||||
|
return info.LastBidTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateAuctionHistory(AuctionInfo auction, AuctionState state)
|
private void UpdateAuctionHistory(AuctionInfo auction, AuctionState state)
|
||||||
{
|
{
|
||||||
// Traccia l'ultima puntata per rilevare cambi
|
|
||||||
var lastHistory = auction.BidHistory.LastOrDefault();
|
var lastHistory = auction.BidHistory.LastOrDefault();
|
||||||
var lastPrice = lastHistory?.Price ?? 0;
|
var lastPrice = lastHistory?.Price ?? 0;
|
||||||
var lastBidder = lastHistory?.Bidder;
|
var lastBidder = lastHistory?.Bidder;
|
||||||
|
|
||||||
bool isNewBid = false;
|
bool isNewBid = false;
|
||||||
|
|
||||||
// Nuova puntata = CAMBIO PREZZO (più affidabile)
|
// Nuova puntata = CAMBIO PREZZO
|
||||||
// Ogni incremento di prezzo significa che qualcuno ha puntato
|
|
||||||
if (state.Price > lastPrice && state.Price > 0)
|
if (state.Price > lastPrice && state.Price > 0)
|
||||||
{
|
{
|
||||||
isNewBid = true;
|
isNewBid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: cambio utente (se il prezzo è uguale ma l'utente cambia)
|
// Fallback: cambio utente
|
||||||
if (!isNewBid &&
|
if (!isNewBid &&
|
||||||
!string.IsNullOrEmpty(lastBidder) &&
|
!string.IsNullOrEmpty(lastBidder) &&
|
||||||
!string.IsNullOrEmpty(state.LastBidder) &&
|
!string.IsNullOrEmpty(state.LastBidder) &&
|
||||||
@@ -592,7 +504,6 @@ namespace AutoBidder.Services
|
|||||||
auction.BidderStats[state.LastBidder].LastBidTime = DateTime.UtcNow;
|
auction.BidderStats[state.LastBidder].LastBidTime = DateTime.UtcNow;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notifica cambio reset count per aggiornare UI
|
|
||||||
OnResetCountChanged?.Invoke(auction.AuctionId);
|
OnResetCountChanged?.Invoke(auction.AuctionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ namespace AutoBidder.Services
|
|||||||
if (!string.IsNullOrWhiteSpace(_session.CookieString))
|
if (!string.IsNullOrWhiteSpace(_session.CookieString))
|
||||||
{
|
{
|
||||||
request.Headers.Add("Cookie", _session.CookieString);
|
request.Headers.Add("Cookie", _session.CookieString);
|
||||||
Log("[AUTH] Using full cookie string", auctionId);
|
// Log rimosso per ridurre verbosità
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -98,7 +98,6 @@ namespace AutoBidder.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. HEADERS BROWSER-LIKE (anti-detection)
|
// 2. HEADERS BROWSER-LIKE (anti-detection)
|
||||||
|
|
||||||
// User-Agent realistico (Chrome su Windows)
|
// User-Agent realistico (Chrome su Windows)
|
||||||
request.Headers.Add("User-Agent",
|
request.Headers.Add("User-Agent",
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36");
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36");
|
||||||
@@ -131,7 +130,7 @@ namespace AutoBidder.Services
|
|||||||
request.Headers.Add("Referer", "https://it.bidoo.com/");
|
request.Headers.Add("Referer", "https://it.bidoo.com/");
|
||||||
}
|
}
|
||||||
|
|
||||||
Log("[HEADERS] Browser-like headers added (anti-bot)", auctionId);
|
// Log rimosso per ridurre verbosità - headers sempre aggiunti
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -321,7 +320,8 @@ namespace AutoBidder.Services
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var url = "https://it.bidoo.com/ajax/get_auction_bids_info_banner.php";
|
// OTTIMIZZATO: Usa buy_bids.php che contiene tutti i dati in un'unica chiamata
|
||||||
|
var url = "https://it.bidoo.com/buy_bids.php";
|
||||||
|
|
||||||
Log($"[USER INFO REQUEST] GET {url}");
|
Log($"[USER INFO REQUEST] GET {url}");
|
||||||
|
|
||||||
@@ -332,24 +332,133 @@ namespace AutoBidder.Services
|
|||||||
var response = await _httpClient.SendAsync(request);
|
var response = await _httpClient.SendAsync(request);
|
||||||
var latency = (int)(DateTime.UtcNow - startTime).TotalMilliseconds;
|
var latency = (int)(DateTime.UtcNow - startTime).TotalMilliseconds;
|
||||||
|
|
||||||
Log($"[USER INFO RESPONSE] Status: {(int)response.StatusCode} {response.StatusCode}");
|
Log($"[USER INFO RESPONSE] Status: {(int)response.StatusCode} {response.StatusCode}, Latency: {latency}ms");
|
||||||
Log($"[USER INFO RESPONSE] Latency: {latency}ms");
|
|
||||||
|
|
||||||
var responseText = await response.Content.ReadAsStringAsync();
|
|
||||||
Log($"[USER INFO RESPONSE] Body length: {responseText.Length}");
|
|
||||||
|
|
||||||
if (!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
Log($"[USER INFO ERROR] HTTP {response.StatusCode}");
|
Log($"[USER INFO ERROR] HTTP {response.StatusCode} - Cookie potrebbe essere scaduto o non valido");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var html = await response.Content.ReadAsStringAsync();
|
||||||
|
Log($"[USER INFO RESPONSE] Body length: {html.Length} chars");
|
||||||
|
|
||||||
|
// Verifica se la risposta contiene HTML valido
|
||||||
|
if (html.Length < 100)
|
||||||
|
{
|
||||||
|
Log($"[USER INFO ERROR] Risposta troppo corta ({html.Length} chars) - possibile errore server");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parsa l'oggetto JavaScript BidooCnf.userObj
|
||||||
|
bool foundData = false;
|
||||||
|
|
||||||
|
// Estrai ID utente: BidooCnf.userObj.id = '6707664';
|
||||||
|
var idMatch = System.Text.RegularExpressions.Regex.Match(html, @"BidooCnf\.userObj\.id\s*=\s*'([^']+)'");
|
||||||
|
if (idMatch.Success)
|
||||||
|
{
|
||||||
|
if (int.TryParse(idMatch.Groups[1].Value, out int userId))
|
||||||
|
{
|
||||||
|
_session.UserId = userId;
|
||||||
|
Log($"[USER INFO PARSED] User ID: {userId}");
|
||||||
|
foundData = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log($"[USER INFO WARN] User ID non trovato");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estrai email: BidooCnf.userObj.email = 'albertobalbo96@gmail.com';
|
||||||
|
var emailMatch = System.Text.RegularExpressions.Regex.Match(html, @"BidooCnf\.userObj\.email\s*=\s*'([^']+)'");
|
||||||
|
if (emailMatch.Success)
|
||||||
|
{
|
||||||
|
_session.Email = emailMatch.Groups[1].Value;
|
||||||
|
Log($"[USER INFO PARSED] Email: {_session.Email}");
|
||||||
|
foundData = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log($"[USER INFO WARN] Email non trovata");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estrai username: BidooCnf.userObj.username = 'sirbietole23';
|
||||||
|
var usernameMatch = System.Text.RegularExpressions.Regex.Match(html, @"BidooCnf\.userObj\.username\s*=\s*'([^']+)'");
|
||||||
|
if (usernameMatch.Success)
|
||||||
|
{
|
||||||
|
_session.Username = usernameMatch.Groups[1].Value;
|
||||||
|
Log($"[USER INFO PARSED] Username: {_session.Username}");
|
||||||
|
foundData = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log($"[USER INFO WARN] Username non trovato");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estrai telefono: BidooCnf.userObj.phone = '00393665920653';
|
||||||
|
var phoneMatch = System.Text.RegularExpressions.Regex.Match(html, @"BidooCnf\.userObj\.phone\s*=\s*'([^']+)'");
|
||||||
|
if (phoneMatch.Success)
|
||||||
|
{
|
||||||
|
_session.Phone = phoneMatch.Groups[1].Value;
|
||||||
|
Log($"[USER INFO PARSED] Phone: {_session.Phone}");
|
||||||
|
foundData = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estrai puntate residue dall'HTML: <span id="divSaldoBidMobile">206</span>
|
||||||
|
var bidsPatterns = new[]
|
||||||
|
{
|
||||||
|
@"<span[^>]*id=""divSaldoBidMobile""[^>]*>(\d+)</span>",
|
||||||
|
@"<span[^>]*id=""divSaldoBidBottom""[^>]*>(\d+)</span>",
|
||||||
|
@"<span[^>]*id=""divSaldoBid""[^>]*>(\d+)</span>"
|
||||||
|
};
|
||||||
|
|
||||||
|
bool foundBids = false;
|
||||||
|
foreach (var pattern in bidsPatterns)
|
||||||
|
{
|
||||||
|
var bidsMatch = System.Text.RegularExpressions.Regex.Match(html, pattern);
|
||||||
|
if (bidsMatch.Success && int.TryParse(bidsMatch.Groups[1].Value, out int bids))
|
||||||
|
{
|
||||||
|
_session.RemainingBids = bids;
|
||||||
|
Log($"[USER INFO PARSED] Remaining bids: {bids}");
|
||||||
|
foundData = true;
|
||||||
|
foundBids = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundBids)
|
||||||
|
{
|
||||||
|
Log($"[USER INFO WARN] Puntate residue non trovate");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estrai credito Bidoo Shop: <span class="cbstotal">15.00</span>
|
||||||
|
var creditMatch = System.Text.RegularExpressions.Regex.Match(html, @"<span[^>]*class=""cbstotal""[^>]*>([\d.]+)</span>");
|
||||||
|
if (creditMatch.Success && double.TryParse(creditMatch.Groups[1].Value, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out double credit))
|
||||||
|
{
|
||||||
|
_session.ShopCredit = credit;
|
||||||
|
Log($"[USER INFO PARSED] Shop credit: €{credit:F2}");
|
||||||
|
foundData = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundData)
|
||||||
|
{
|
||||||
_session.LastAccountUpdate = DateTime.UtcNow;
|
_session.LastAccountUpdate = DateTime.UtcNow;
|
||||||
|
Log($"[USER INFO SUCCESS] Dati estratti correttamente da buy_bids.php");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log($"[USER INFO ERROR] NESSUN dato trovato nell'HTML - cookie probabilmente non valido");
|
||||||
|
// Salva snippet per debug
|
||||||
|
var htmlSnippet = html.Substring(0, Math.Min(500, html.Length)).Replace("\n", " ").Replace("\r", "");
|
||||||
|
Log($"[USER INFO DEBUG] Snippet HTML: {htmlSnippet}...");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log($"[USER INFO EXCEPTION] {ex.GetType().Name}: {ex.Message}");
|
Log($"[USER INFO EXCEPTION] {ex.GetType().Name}: {ex.Message}");
|
||||||
|
Log($"[USER INFO EXCEPTION] StackTrace: {ex.StackTrace}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -363,10 +472,8 @@ namespace AutoBidder.Services
|
|||||||
};
|
};
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Log($"[BID] Placing bid via direct GET to bid.php", auctionId);
|
|
||||||
var url = "https://it.bidoo.com/bid.php";
|
var url = "https://it.bidoo.com/bid.php";
|
||||||
var payload = $"AID={WebUtility.UrlEncode(auctionId)}&sup=0&shock=0";
|
var payload = $"AID={WebUtility.UrlEncode(auctionId)}&sup=0&shock=0";
|
||||||
Log($"[BID REQUEST] GET {url}?{payload}", auctionId);
|
|
||||||
var getUrl = url + "?" + payload;
|
var getUrl = url + "?" + payload;
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, getUrl);
|
var request = new HttpRequestMessage(HttpMethod.Get, getUrl);
|
||||||
var referer = !string.IsNullOrEmpty(auctionUrl) ? auctionUrl : $"https://it.bidoo.com/asta/nome-prodotto-{auctionId}";
|
var referer = !string.IsNullOrEmpty(auctionUrl) ? auctionUrl : $"https://it.bidoo.com/asta/nome-prodotto-{auctionId}";
|
||||||
@@ -375,19 +482,14 @@ namespace AutoBidder.Services
|
|||||||
{
|
{
|
||||||
request.Headers.Add("Origin", "https://it.bidoo.com");
|
request.Headers.Add("Origin", "https://it.bidoo.com");
|
||||||
}
|
}
|
||||||
|
|
||||||
var startTime = DateTime.UtcNow;
|
var startTime = DateTime.UtcNow;
|
||||||
var response = await _httpClient.SendAsync(request);
|
var response = await _httpClient.SendAsync(request);
|
||||||
result.LatencyMs = (int)(DateTime.UtcNow - startTime).TotalMilliseconds;
|
result.LatencyMs = (int)(DateTime.UtcNow - startTime).TotalMilliseconds;
|
||||||
Log($"[BID RESPONSE] Status: {(int)response.StatusCode} {response.StatusCode}", auctionId);
|
|
||||||
Log($"[BID RESPONSE] Latency: {result.LatencyMs}ms", auctionId);
|
|
||||||
var responseText = await response.Content.ReadAsStringAsync();
|
var responseText = await response.Content.ReadAsStringAsync();
|
||||||
result.Response = responseText;
|
result.Response = responseText;
|
||||||
Log($"[BID RESPONSE] Body length: {responseText.Length} bytes", auctionId);
|
|
||||||
if (!string.IsNullOrEmpty(responseText))
|
|
||||||
{
|
|
||||||
var preview = responseText.Length > 80 ? responseText.Substring(0, 80) + "..." : responseText;
|
|
||||||
Log($"[BID RESPONSE] Preview: {preview}", auctionId);
|
|
||||||
}
|
|
||||||
if (responseText.StartsWith("ok", StringComparison.OrdinalIgnoreCase))
|
if (responseText.StartsWith("ok", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
result.Success = true;
|
result.Success = true;
|
||||||
@@ -397,50 +499,43 @@ namespace AutoBidder.Services
|
|||||||
result.NewPrice = priceIndex * 0.01;
|
result.NewPrice = priceIndex * 0.01;
|
||||||
}
|
}
|
||||||
// Parse remaining bids from response if present: ok|324|...
|
// Parse remaining bids from response if present: ok|324|...
|
||||||
var parts2 = responseText.Split('|');
|
if (parts.Length > 1 && int.TryParse(parts[1], out var remaining))
|
||||||
if (parts2.Length > 1 && int.TryParse(parts2[1], out var remaining))
|
|
||||||
{
|
{
|
||||||
_session.RemainingBids = remaining;
|
_session.RemainingBids = remaining;
|
||||||
Log($"[BID SUCCESS] ✓ Bid placed successfully - Remaining bids: {remaining}", auctionId);
|
Log($"[BID SUCCESS] Puntata piazzata - Crediti residui: {remaining}", auctionId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log("[BID SUCCESS] ✓ Bid placed successfully", auctionId);
|
Log("[BID SUCCESS] Puntata piazzata", 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))
|
||||||
{
|
{
|
||||||
result.Success = false;
|
result.Success = false;
|
||||||
var parts = responseText.Split('|');
|
var parts = responseText.Split('|');
|
||||||
result.Error = parts.Length > 1 ? parts[1] : responseText;
|
var errorMsg = parts.Length > 1 ? parts[1] : responseText;
|
||||||
Log($"[BID ERROR] Server returned error: {result.Error}", auctionId);
|
|
||||||
|
// Pulisci messaggio errore da HTML
|
||||||
|
if (errorMsg.Contains("<br>") || errorMsg.Contains("<a"))
|
||||||
|
{
|
||||||
|
var cleanMsg = System.Text.RegularExpressions.Regex.Replace(errorMsg, "<[^>]+>", "");
|
||||||
|
errorMsg = cleanMsg.Split(new[] { "<br>", "\n" }, StringSplitOptions.RemoveEmptyEntries)[0].Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Error = errorMsg;
|
||||||
|
Log($"[BID ERROR] {errorMsg}", auctionId);
|
||||||
}
|
}
|
||||||
else if (responseText.Contains("alive"))
|
else if (responseText.Contains("alive"))
|
||||||
{
|
{
|
||||||
result.Success = false;
|
result.Success = false;
|
||||||
result.Error = "Keep-alive response (not a bid response)";
|
result.Error = "Keep-alive response (not a bid response)";
|
||||||
Log($"[BID WARN] Received keep-alive instead of bid confirmation", auctionId);
|
Log($"[BID WARN] Ricevuto keep-alive invece di conferma bid", auctionId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result.Success = false;
|
result.Success = false;
|
||||||
result.Error = string.IsNullOrEmpty(responseText) ? $"HTTP {(int)response.StatusCode}" : responseText;
|
result.Error = string.IsNullOrEmpty(responseText) ? $"HTTP {(int)response.StatusCode}" : "Formato risposta inatteso";
|
||||||
Log($"[BID ERROR] Unexpected response format: {result.Error}", auctionId);
|
Log($"[BID ERROR] Formato risposta inatteso: HTTP {(int)response.StatusCode}", 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;
|
||||||
@@ -449,95 +544,7 @@ namespace AutoBidder.Services
|
|||||||
{
|
{
|
||||||
result.Success = false;
|
result.Success = false;
|
||||||
result.Error = ex.Message;
|
result.Error = ex.Message;
|
||||||
// Generic global-style hint (via auction log event, AuctionMonitor will emit concise global message)
|
Log($"[BID EXCEPTION] {ex.GetType().Name}: {ex.Message}", 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Place a minimal final bid using the simpler payload required by the final-attack protocol.
|
|
||||||
/// Uses: ?auctionID=[ID]&submit=1
|
|
||||||
/// </summary>
|
|
||||||
public async Task<BidResult> PlaceBidFinalAsync(string auctionId, string? auctionUrl = null)
|
|
||||||
{
|
|
||||||
var result = new BidResult
|
|
||||||
{
|
|
||||||
AuctionId = auctionId,
|
|
||||||
Timestamp = DateTime.UtcNow
|
|
||||||
};
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Log($"[BID FINAL] Placing final bid minimal payload", auctionId);
|
|
||||||
var url = "https://it.bidoo.com/bid.php";
|
|
||||||
var payload = $"auctionID={WebUtility.UrlEncode(auctionId)}&submit=1";
|
|
||||||
Log($"[BID REQUEST] GET {url}?{payload}", auctionId);
|
|
||||||
var getUrl = url + "?" + payload;
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, getUrl);
|
|
||||||
var referer = !string.IsNullOrEmpty(auctionUrl) ? auctionUrl : $"https://it.bidoo.com/asta/nome-prodotto-{auctionId}";
|
|
||||||
AddAuthHeaders(request, referer, auctionId);
|
|
||||||
if (!request.Headers.Contains("Origin"))
|
|
||||||
{
|
|
||||||
request.Headers.Add("Origin", "https://it.bidoo.com");
|
|
||||||
}
|
|
||||||
var startTime = DateTime.UtcNow;
|
|
||||||
var response = await _httpClient.SendAsync(request);
|
|
||||||
result.LatencyMs = (int)(DateTime.UtcNow - startTime).TotalMilliseconds;
|
|
||||||
Log($"[BID RESPONSE] Status: {(int)response.StatusCode} {response.StatusCode}", auctionId);
|
|
||||||
Log($"[BID RESPONSE] Latency: {result.LatencyMs}ms", auctionId);
|
|
||||||
var responseText = await response.Content.ReadAsStringAsync();
|
|
||||||
result.Response = responseText;
|
|
||||||
Log($"[BID RESPONSE] Body length: {responseText.Length} bytes", auctionId);
|
|
||||||
if (!string.IsNullOrEmpty(responseText))
|
|
||||||
{
|
|
||||||
var preview = responseText.Length > 80 ? responseText.Substring(0, 80) + "..." : responseText;
|
|
||||||
Log($"[BID RESPONSE] Preview: {preview}", auctionId);
|
|
||||||
}
|
|
||||||
if (responseText.StartsWith("ok", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
result.Success = true;
|
|
||||||
var parts = responseText.Split('|');
|
|
||||||
if (parts.Length > 1 && double.TryParse(parts[1], out var priceIndex))
|
|
||||||
{
|
|
||||||
result.NewPrice = priceIndex * 0.01;
|
|
||||||
}
|
|
||||||
Log("[BID SUCCESS] ✓ Final bid placed successfully", auctionId);
|
|
||||||
}
|
|
||||||
else if (responseText.StartsWith("error", StringComparison.OrdinalIgnoreCase) || responseText.StartsWith("no|", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
result.Success = false;
|
|
||||||
var parts = responseText.Split('|');
|
|
||||||
result.Error = parts.Length > 1 ? parts[1] : responseText;
|
|
||||||
Log($"[BID ERROR] Server returned error: {result.Error}", auctionId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result.Success = false;
|
|
||||||
result.Error = string.IsNullOrEmpty(responseText) ? $"HTTP {(int)response.StatusCode}" : responseText;
|
|
||||||
Log($"[BID ERROR] Unexpected response format: {result.Error}", auctionId);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
result.Success = false;
|
|
||||||
result.Error = ex.Message;
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -607,7 +614,7 @@ namespace AutoBidder.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ottiene dati utente (nome, puntate residue, saldo, id) tramite chiamata AJAX leggera
|
/// OTTIMIZZATO: Estrae ID utente, username e saldo disponibile tramite chiamata AJAX leggera
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<UserData?> GetUserDataAsync()
|
public async Task<UserData?> GetUserDataAsync()
|
||||||
{
|
{
|
||||||
@@ -708,31 +715,103 @@ namespace AutoBidder.Services
|
|||||||
{
|
{
|
||||||
var url = "https://it.bidoo.com/bids_history.php";
|
var url = "https://it.bidoo.com/bids_history.php";
|
||||||
Log($"[USER HTML REQUEST] GET {url}");
|
Log($"[USER HTML REQUEST] GET {url}");
|
||||||
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||||
AddAuthHeaders(request, "https://it.bidoo.com/");
|
AddAuthHeaders(request, "https://it.bidoo.com/");
|
||||||
|
|
||||||
var response = await _httpClient.SendAsync(request);
|
var response = await _httpClient.SendAsync(request);
|
||||||
|
|
||||||
|
Log($"[USER HTML RESPONSE] Status: {(int)response.StatusCode} {response.StatusCode}");
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
Log($"[USER HTML ERROR] HTTP {response.StatusCode} - Cookie potrebbe essere scaduto");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var html = await response.Content.ReadAsStringAsync();
|
var html = await response.Content.ReadAsStringAsync();
|
||||||
Log($"[USER HTML RESPONSE] Body length: {html.Length}");
|
Log($"[USER HTML RESPONSE] Body length: {html.Length} chars");
|
||||||
|
|
||||||
|
// Verifica se la risposta contiene HTML valido
|
||||||
|
if (html.Length < 100 || !html.Contains("<!DOCTYPE") && !html.Contains("<html"))
|
||||||
|
{
|
||||||
|
Log($"[USER HTML ERROR] Risposta non contiene HTML valido (possibile redirect o errore)");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var userData = new UserData();
|
var userData = new UserData();
|
||||||
// Estrai nome utente
|
bool foundUsername = false;
|
||||||
var userMatch = System.Text.RegularExpressions.Regex.Match(html, @"<a class=""pers_lnk""[^>]*>([^<]+)</a>");
|
bool foundBids = false;
|
||||||
|
|
||||||
|
// Estrai nome utente - pattern multipli per maggiore robustezza
|
||||||
|
var usernamePatterns = new[]
|
||||||
|
{
|
||||||
|
@"<a class=""pers_lnk""[^>]*>([^<]+)</a>",
|
||||||
|
@"<a[^>]*class=""pers_lnk""[^>]*>([^<]+)</a>",
|
||||||
|
@"<span[^>]*class=""username""[^>]*>([^<]+)</span>",
|
||||||
|
@"BidooCnf\.userObj\.username\s*=\s*'([^']+)'"
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var pattern in usernamePatterns)
|
||||||
|
{
|
||||||
|
var userMatch = System.Text.RegularExpressions.Regex.Match(html, pattern);
|
||||||
if (userMatch.Success)
|
if (userMatch.Success)
|
||||||
{
|
{
|
||||||
userData.Username = userMatch.Groups[1].Value.Trim();
|
userData.Username = userMatch.Groups[1].Value.Trim();
|
||||||
|
foundUsername = true;
|
||||||
|
Log($"[USER HTML PARSED] Username trovato: {userData.Username}");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
// Estrai puntate residue
|
}
|
||||||
var bidsMatch = System.Text.RegularExpressions.Regex.Match(html, @"<span id=""divSaldoBidBottom""[^>]*>(\d+)</span>");
|
|
||||||
|
if (!foundUsername)
|
||||||
|
{
|
||||||
|
Log($"[USER HTML ERROR] Username NON trovato nell'HTML");
|
||||||
|
// Salva un estratto dell'HTML per debug (primi 500 caratteri)
|
||||||
|
var htmlSnippet = html.Substring(0, Math.Min(500, html.Length)).Replace("\n", " ").Replace("\r", "");
|
||||||
|
Log($"[USER HTML DEBUG] Snippet HTML: {htmlSnippet}...");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estrai puntate residue - pattern multipli
|
||||||
|
var bidsPatterns = new[]
|
||||||
|
{
|
||||||
|
@"<span[^>]*id=""divSaldoBidBottom""[^>]*>(\d+)</span>",
|
||||||
|
@"<span[^>]*id=""divSaldoBidMobile""[^>]*>(\d+)</span>",
|
||||||
|
@"<span[^>]*id=""divSaldoBid""[^>]*>(\d+)</span>",
|
||||||
|
@"<div[^>]*class=""bids[_-]count""[^>]*>(\d+)</div>"
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var pattern in bidsPatterns)
|
||||||
|
{
|
||||||
|
var bidsMatch = System.Text.RegularExpressions.Regex.Match(html, pattern);
|
||||||
if (bidsMatch.Success && int.TryParse(bidsMatch.Groups[1].Value, out int bids))
|
if (bidsMatch.Success && int.TryParse(bidsMatch.Groups[1].Value, out int bids))
|
||||||
{
|
{
|
||||||
userData.RemainingBids = bids;
|
userData.RemainingBids = bids;
|
||||||
|
foundBids = true;
|
||||||
|
Log($"[USER HTML PARSED] Puntate residue trovate: {bids}");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (!string.IsNullOrEmpty(userData.Username) && userData.RemainingBids > 0)
|
}
|
||||||
|
|
||||||
|
if (!foundBids)
|
||||||
|
{
|
||||||
|
Log($"[USER HTML ERROR] Puntate residue NON trovate nell'HTML");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ritorna dati solo se almeno username è stato trovato
|
||||||
|
if (foundUsername)
|
||||||
|
{
|
||||||
|
Log($"[USER HTML SUCCESS] Dati estratti: {userData.Username}, {userData.RemainingBids} puntate");
|
||||||
return userData;
|
return userData;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log($"[USER HTML FAILED] Impossibile estrarre dati utente dall'HTML");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log($"[USER HTML EXCEPTION] {ex.GetType().Name}: {ex.Message}");
|
Log($"[USER HTML EXCEPTION] {ex.GetType().Name}: {ex.Message}");
|
||||||
|
Log($"[USER HTML EXCEPTION] StackTrace: {ex.StackTrace}");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
49
Mimante/Utilities/BooleanToOpacityConverter.cs
Normal file
49
Mimante/Utilities/BooleanToOpacityConverter.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Windows.Data;
|
||||||
|
|
||||||
|
namespace AutoBidder.Utilities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converter che trasforma un bool in opacità per visualizzare lo stato dei pulsanti
|
||||||
|
/// True = 1.0 (pulsante abilitato e luminoso)
|
||||||
|
/// False = 0.4 (pulsante disabilitato e scuro)
|
||||||
|
/// </summary>
|
||||||
|
public class BooleanToOpacityConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is bool boolValue)
|
||||||
|
{
|
||||||
|
return boolValue ? 1.0 : 0.4;
|
||||||
|
}
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converter inverso: True = 0.4, False = 1.0
|
||||||
|
/// Utile per pulsanti che devono essere scuri quando attivi (es: Stop quando già fermo)
|
||||||
|
/// </summary>
|
||||||
|
public class InverseBooleanToOpacityConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is bool boolValue)
|
||||||
|
{
|
||||||
|
return boolValue ? 0.4 : 1.0;
|
||||||
|
}
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -100,8 +100,8 @@ namespace AutoBidder.Utilities
|
|||||||
{
|
{
|
||||||
var csv = new StringBuilder();
|
var csv = new StringBuilder();
|
||||||
|
|
||||||
// Header
|
// Header AGGIORNATO
|
||||||
csv.AppendLine("Auction ID,Name,Timer Click,Delay (ms),Min Price,Max Price,My Bids,Resets,Total Bidders,Active,Paused,Added At,Last Click At");
|
csv.AppendLine("Auction ID,Name,Bid Before Deadline (ms),Check Before Bid,Min Price,Max Price,My Bids,Resets,Total Bidders,Active,Paused,Added At,Last Click At");
|
||||||
|
|
||||||
// Data
|
// Data
|
||||||
foreach (var auction in auctions)
|
foreach (var auction in auctions)
|
||||||
@@ -111,8 +111,8 @@ namespace AutoBidder.Utilities
|
|||||||
|
|
||||||
csv.AppendLine($"{auction.AuctionId}," +
|
csv.AppendLine($"{auction.AuctionId}," +
|
||||||
$"\"{auction.Name}\"," +
|
$"\"{auction.Name}\"," +
|
||||||
$"{auction.TimerClick}," +
|
$"{auction.BidBeforeDeadlineMs}," +
|
||||||
$"{auction.DelayMs}," +
|
$"{auction.CheckAuctionOpenBeforeBid}," +
|
||||||
$"{auction.MinPrice:F2}," +
|
$"{auction.MinPrice:F2}," +
|
||||||
$"{auction.MaxPrice:F2}," +
|
$"{auction.MaxPrice:F2}," +
|
||||||
$"{myBids}," +
|
$"{myBids}," +
|
||||||
|
|||||||
@@ -20,6 +20,13 @@ namespace AutoBidder.Utilities
|
|||||||
public bool IncludeMetadata { get; set; } = true;
|
public bool IncludeMetadata { get; set; } = true;
|
||||||
public bool RemoveAfterExport { get; set; } = false;
|
public bool RemoveAfterExport { get; set; } = false;
|
||||||
public bool OverwriteExisting { get; set; } = false;
|
public bool OverwriteExisting { get; set; } = false;
|
||||||
|
|
||||||
|
// NUOVE IMPOSTAZIONI PREDEFINITE PER LE ASTE
|
||||||
|
public int DefaultBidBeforeDeadlineMs { get; set; } = 200;
|
||||||
|
public bool DefaultCheckAuctionOpenBeforeBid { get; set; } = false;
|
||||||
|
public double DefaultMinPrice { get; set; } = 0;
|
||||||
|
public double DefaultMaxPrice { get; set; } = 0;
|
||||||
|
public int DefaultMaxClicks { get; set; } = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static class SettingsManager
|
internal static class SettingsManager
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace AutoBidder.ViewModels
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ViewModel per una riga della griglia aste (DataBinding)
|
/// ViewModel per una riga della griglia aste (DataBinding)
|
||||||
/// Solo HTTP, nessuna modalità, browser o multi-click
|
/// Sistema di timing ottimizzato con BidBeforeDeadlineMs
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AuctionViewModel : INotifyPropertyChanged
|
public class AuctionViewModel : INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
@@ -26,23 +26,23 @@ namespace AutoBidder.ViewModels
|
|||||||
public string Name => _auctionInfo.Name;
|
public string Name => _auctionInfo.Name;
|
||||||
|
|
||||||
// Configurazione
|
// Configurazione
|
||||||
public int TimerClick
|
public int BidBeforeDeadlineMs
|
||||||
{
|
{
|
||||||
get => _auctionInfo.TimerClick;
|
get => _auctionInfo.BidBeforeDeadlineMs;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_auctionInfo.TimerClick = value;
|
_auctionInfo.BidBeforeDeadlineMs = value;
|
||||||
OnPropertyChanged(nameof(TimerClick));
|
OnPropertyChanged(nameof(BidBeforeDeadlineMs));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int DelayMs
|
public bool CheckAuctionOpenBeforeBid
|
||||||
{
|
{
|
||||||
get => _auctionInfo.DelayMs;
|
get => _auctionInfo.CheckAuctionOpenBeforeBid;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_auctionInfo.DelayMs = value;
|
_auctionInfo.CheckAuctionOpenBeforeBid = value;
|
||||||
OnPropertyChanged(nameof(DelayMs));
|
OnPropertyChanged(nameof(CheckAuctionOpenBeforeBid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,6 +84,7 @@ namespace AutoBidder.ViewModels
|
|||||||
{
|
{
|
||||||
_auctionInfo.IsActive = value;
|
_auctionInfo.IsActive = value;
|
||||||
OnPropertyChanged(nameof(IsActive));
|
OnPropertyChanged(nameof(IsActive));
|
||||||
|
RefreshButtonStates();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public bool IsPaused
|
public bool IsPaused
|
||||||
@@ -93,6 +94,7 @@ namespace AutoBidder.ViewModels
|
|||||||
{
|
{
|
||||||
_auctionInfo.IsPaused = value;
|
_auctionInfo.IsPaused = value;
|
||||||
OnPropertyChanged(nameof(IsPaused));
|
OnPropertyChanged(nameof(IsPaused));
|
||||||
|
RefreshButtonStates();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public int ResetCount => _auctionInfo.ResetCount;
|
public int ResetCount => _auctionInfo.ResetCount;
|
||||||
@@ -202,11 +204,15 @@ namespace AutoBidder.ViewModels
|
|||||||
if (_lastState.Status == AuctionStatus.Scheduled)
|
if (_lastState.Status == AuctionStatus.Scheduled)
|
||||||
return $"Programmata: {_lastState.StartTime}";
|
return $"Programmata: {_lastState.StartTime}";
|
||||||
|
|
||||||
// Stati normali running
|
// Stati normali running - ora basati su millisecondi di anticipo
|
||||||
if (_lastState.Timer < 2) return "Active";
|
var msRemaining = _lastState.Timer * 1000;
|
||||||
if (_lastState.Timer < 10) return "Fast";
|
var anticipoMs = _auctionInfo.BidBeforeDeadlineMs;
|
||||||
if (_lastState.Timer < 30) return "HTTP";
|
|
||||||
return "Slow";
|
if (msRemaining <= anticipoMs) return "BID WINDOW";
|
||||||
|
if (msRemaining < 1000) return "Critical";
|
||||||
|
if (msRemaining < 3000) return "Ready";
|
||||||
|
if (msRemaining < 10000) return "Standby";
|
||||||
|
return "Waiting";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,11 +238,27 @@ namespace AutoBidder.ViewModels
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void RefreshCounters()
|
public void RefreshCounters()
|
||||||
{
|
{
|
||||||
// RIMOSSO: OnPropertyChanged(nameof(MyClicks));
|
|
||||||
OnPropertyChanged(nameof(ResetCount));
|
OnPropertyChanged(nameof(ResetCount));
|
||||||
OnPropertyChanged(nameof(MyClicks));
|
OnPropertyChanged(nameof(MyClicks));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Proprietà per abilitare/disabilitare pulsanti griglia
|
||||||
|
public bool CanStart => !IsActive || IsPaused;
|
||||||
|
public bool CanPause => IsActive && !IsPaused;
|
||||||
|
public bool CanStop => IsActive;
|
||||||
|
public bool CanBid => true; // Puntata manuale sempre disponibile
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Notifica cambio stato per aggiornare pulsanti
|
||||||
|
/// </summary>
|
||||||
|
public void RefreshButtonStates()
|
||||||
|
{
|
||||||
|
OnPropertyChanged(nameof(CanStart));
|
||||||
|
OnPropertyChanged(nameof(CanPause));
|
||||||
|
OnPropertyChanged(nameof(CanStop));
|
||||||
|
OnPropertyChanged(nameof(CanBid));
|
||||||
|
}
|
||||||
|
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
protected void OnPropertyChanged(string propertyName)
|
protected void OnPropertyChanged(string propertyName)
|
||||||
|
|||||||
562
README.md
562
README.md
@@ -1,10 +1,10 @@
|
|||||||
# AutoBidder v4.0 - Guida Completa
|
# AutoBidder v4.0 - Guida Completa
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
AutoBidder è un'applicazione desktop per Windows realizzata con WPF e .NET 8.0, progettata per il monitoraggio e la gestione automatizzata di aste online sulla piattaforma Bidoo. L'applicazione utilizza polling HTTP per monitorare in tempo reale lo stato delle aste e inviare offerte automatiche tramite richieste HTTP dirette, senza necessità di automazione browser.
|
AutoBidder è un'applicazione desktop per Windows realizzata con WPF e .NET 8.0, progettata per il monitoraggio e la gestione automatizzata di aste online sulla piattaforma Bidoo. L'applicazione utilizza polling HTTP per monitorare in tempo reale lo stato delle aste e inviare offerte automatiche tramite richieste HTTP dirette, senza necessità di automazione browser.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -22,13 +22,13 @@ AutoBidder
|
|||||||
10. [Export Dati](#export-dati)
|
10. [Export Dati](#export-dati)
|
||||||
11. [Dettagli Tecnici](#dettagli-tecnici)
|
11. [Dettagli Tecnici](#dettagli-tecnici)
|
||||||
12. [FAQ e Troubleshooting](#faq-e-troubleshooting)
|
12. [FAQ e Troubleshooting](#faq-e-troubleshooting)
|
||||||
13. [Sicurezza e Responsabilità](#sicurezza-e-responsabilit)
|
13. [Sicurezza e Responsabilità](#sicurezza-e-responsabilit)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Panoramica
|
## Panoramica
|
||||||
|
|
||||||
AutoBidder offre un'interfaccia dashboard moderna con layout modulare che permette di monitorare simultaneamente più aste, visualizzare statistiche in tempo reale e configurare strategie di bidding personalizzate. L'architettura dell'applicazione è stata progettata per massimizzare le prestazioni minimizzando l'utilizzo di risorse di sistema.
|
AutoBidder offre un'interfaccia dashboard moderna con layout modulare che permette di monitorare simultaneamente più aste, visualizzare statistiche in tempo reale e configurare strategie di bidding personalizzate. L'architettura dell'applicazione è stata progettata per massimizzare le prestazioni minimizzando l'utilizzo di risorse di sistema.
|
||||||
|
|
||||||
### Caratteristiche Principali
|
### Caratteristiche Principali
|
||||||
|
|
||||||
@@ -38,17 +38,20 @@ AutoBidder offre un'interfaccia dashboard moderna con layout modulare che permet
|
|||||||
- Tracking real-time di timer, prezzo corrente, ultimo offerente e numero di reset
|
- Tracking real-time di timer, prezzo corrente, ultimo offerente e numero di reset
|
||||||
- Statistiche dettagliate per ogni asta con log dedicato e lista utenti partecipanti
|
- Statistiche dettagliate per ogni asta con log dedicato e lista utenti partecipanti
|
||||||
|
|
||||||
**Invio Offerte Automatico**
|
**Invio Offerte Automatico con Timing Preciso**
|
||||||
- Puntate inviate tramite richieste HTTP GET dirette agli endpoint Bidoo
|
- Puntate inviate tramite richieste HTTP GET dirette agli endpoint Bidoo
|
||||||
- Configurazione precisa del timing con granularità al secondo (0-8 secondi countdown)
|
- **Nuovo Sistema di Timing**: Configurazione precisa in millisecondi (0-5000ms) prima della scadenza
|
||||||
- Delay configurabile in millisecondi per ottimizzare la precisione dell'invio
|
- Polling adattivo: frequenza dinamica basata sul tempo rimanente (10-1000ms)
|
||||||
|
- Cooldown intelligente di 800ms tra puntate consecutive per evitare spam
|
||||||
|
- Rilevamento puntate recenti di altri utenti (finestra 500ms)
|
||||||
|
- Verifica opzionale stato asta prima di puntare
|
||||||
- Limiti di prezzo minimo/massimo per controllo automatico delle spese
|
- Limiti di prezzo minimo/massimo per controllo automatico delle spese
|
||||||
- Limite massimo di click per asta per gestire il budget di puntate
|
- Limite massimo di click per asta per gestire il budget di puntate
|
||||||
|
|
||||||
**Dashboard Moderna**
|
**Dashboard Moderna**
|
||||||
- Layout a griglia con 6 pannelli ridimensionabili tramite GridSplitter
|
- Layout a griglia con 6 pannelli ridimensionabili tramite GridSplitter
|
||||||
- Dark theme professionale con palette colori consistente
|
- Dark theme professionale con palette colori consistente
|
||||||
- Log globale e log per singola asta con codifica colore per severità
|
- Log globale e log per singola asta con codifica colore per severità
|
||||||
- Lista utenti partecipanti con statistiche dettagliate
|
- Lista utenti partecipanti con statistiche dettagliate
|
||||||
- Pannello impostazioni dedicato per configurazione real-time parametri asta
|
- Pannello impostazioni dedicato per configurazione real-time parametri asta
|
||||||
|
|
||||||
@@ -69,7 +72,7 @@ AutoBidder offre un'interfaccia dashboard moderna con layout modulare che permet
|
|||||||
|
|
||||||
**Software Richiesto**
|
**Software Richiesto**
|
||||||
- .NET 8.0 Runtime o SDK
|
- .NET 8.0 Runtime o SDK
|
||||||
- WebView2 Runtime (solitamente già installato su Windows 11)
|
- WebView2 Runtime (solitamente già installato su Windows 11)
|
||||||
|
|
||||||
**Hardware Consigliato**
|
**Hardware Consigliato**
|
||||||
- CPU: Dual-core 2.0 GHz o superiore
|
- CPU: Dual-core 2.0 GHz o superiore
|
||||||
@@ -113,7 +116,7 @@ AutoBidder offre un'interfaccia dashboard moderna con layout modulare che permet
|
|||||||
|
|
||||||
### Installazione WebView2 Runtime
|
### Installazione WebView2 Runtime
|
||||||
|
|
||||||
Se WebView2 non è già installato:
|
Se WebView2 non è già installato:
|
||||||
1. Scaricare da [microsoft.com/edge/webview2](https://developer.microsoft.com/microsoft-edge/webview2/)
|
1. Scaricare da [microsoft.com/edge/webview2](https://developer.microsoft.com/microsoft-edge/webview2/)
|
||||||
2. Eseguire l'installer Evergreen Bootstrapper
|
2. Eseguire l'installer Evergreen Bootstrapper
|
||||||
3. Riavviare l'applicazione
|
3. Riavviare l'applicazione
|
||||||
@@ -122,7 +125,7 @@ Se WebView2 non
|
|||||||
|
|
||||||
## Interfaccia Utente
|
## Interfaccia Utente
|
||||||
|
|
||||||
L'interfaccia di AutoBidder è organizzata in una sidebar di navigazione verticale e un'area contenuto principale che mostra diverse schede.
|
L'interfaccia di AutoBidder è organizzata in una sidebar di navigazione verticale e un'area contenuto principale che mostra diverse schede.
|
||||||
|
|
||||||
### Sidebar Navigazione
|
### Sidebar Navigazione
|
||||||
|
|
||||||
@@ -130,13 +133,13 @@ La sidebar a sinistra contiene 5 tab principali:
|
|||||||
|
|
||||||
- **Aste Attive**: Dashboard principale con griglia aste monitorate e pannelli di controllo
|
- **Aste Attive**: Dashboard principale con griglia aste monitorate e pannelli di controllo
|
||||||
- **Browser**: Browser integrato WebView2 per navigazione su Bidoo
|
- **Browser**: Browser integrato WebView2 per navigazione su Bidoo
|
||||||
- **Puntate Gratis**: Placeholder per funzionalità di ricerca puntate gratuite (in sviluppo)
|
- **Puntate Gratis**: Placeholder per funzionalità di ricerca puntate gratuite (in sviluppo)
|
||||||
- **Dati Statistici**: Analisi aste chiuse e raccomandazioni strategiche (in sviluppo)
|
- **Dati Statistici**: Analisi aste chiuse e raccomandazioni strategiche (in sviluppo)
|
||||||
- **Impostazioni**: Configurazione sessione, export e impostazioni globali
|
- **Impostazioni**: Configurazione sessione, export e impostazioni globali
|
||||||
|
|
||||||
### Layout Dashboard Aste Attive
|
### Layout Dashboard Aste Attive
|
||||||
|
|
||||||
Il pannello Aste Attive è diviso in 6 sezioni ridimensionabili:
|
Il pannello Aste Attive è diviso in 6 sezioni ridimensionabili:
|
||||||
|
|
||||||
**Sezione Superiore**
|
**Sezione Superiore**
|
||||||
- Header con info utente (username, puntate disponibili, aste vinte)
|
- Header con info utente (username, puntate disponibili, aste vinte)
|
||||||
@@ -155,7 +158,7 @@ Tutti i pannelli sono ridimensionabili trascinando i separatori GridSplitter ver
|
|||||||
|
|
||||||
## Configurazione Sessione
|
## Configurazione Sessione
|
||||||
|
|
||||||
Per inviare puntate automatiche è necessario fornire ad AutoBidder il cookie di sessione autenticata del tuo account Bidoo. L'applicazione non gestisce login con username/password ma richiede l'inserimento manuale del cookie di sessione.
|
Per inviare puntate automatiche è necessario fornire ad AutoBidder il cookie di sessione autenticata del tuo account Bidoo. L'applicazione non gestisce login con username/password ma richiede l'inserimento manuale del cookie di sessione.
|
||||||
|
|
||||||
### Ottenere il Cookie di Sessione
|
### Ottenere il Cookie di Sessione
|
||||||
|
|
||||||
@@ -163,7 +166,7 @@ Per inviare puntate automatiche
|
|||||||
2. **Effettuare il login** con le proprie credenziali Bidoo
|
2. **Effettuare il login** con le proprie credenziali Bidoo
|
||||||
3. **Aprire Developer Tools** premendo `F12`
|
3. **Aprire Developer Tools** premendo `F12`
|
||||||
4. **Navigare alla tab "Application"**
|
4. **Navigare alla tab "Application"**
|
||||||
5. **Nel menu laterale** andare su Storage ? Cookies ? `https://it.bidoo.com`
|
5. **Nel menu laterale** andare su Storage → Cookies → `https://it.bidoo.com`
|
||||||
6. **Copiare la stringa completa** di tutti i cookie (formato: `cookie1=value1; cookie2=value2; ...`)
|
6. **Copiare la stringa completa** di tutti i cookie (formato: `cookie1=value1; cookie2=value2; ...`)
|
||||||
|
|
||||||
### Configurare il Cookie nell'Applicazione
|
### Configurare il Cookie nell'Applicazione
|
||||||
@@ -178,11 +181,11 @@ La sessione viene salvata in modo sicuro usando DPAPI (Data Protection API) di W
|
|||||||
### Verifica Sessione
|
### Verifica Sessione
|
||||||
|
|
||||||
Dopo aver configurato il cookie, l'applicazione:
|
Dopo aver configurato il cookie, l'applicazione:
|
||||||
- Mostrerà il nome utente nell'header della scheda Aste Attive
|
- Mostrerà il nome utente nell'header della scheda Aste Attive
|
||||||
- Aggiornerà automaticamente il numero di puntate disponibili
|
- Aggiornerà automaticamente il numero di puntate disponibili
|
||||||
- Abiliterà i pulsanti di controllo aste
|
- Abiliterà i pulsanti di controllo aste
|
||||||
|
|
||||||
Se il cookie scade o diventa invalido, sarà necessario ripetere la procedura di configurazione.
|
Se il cookie scade o diventa invalido, sarà necessario ripetere la procedura di configurazione.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -192,7 +195,7 @@ Se il cookie scade o diventa invalido, sar
|
|||||||
|
|
||||||
**Metodo 1: Da URL o ID**
|
**Metodo 1: Da URL o ID**
|
||||||
1. Cliccare "Aggiungi" nella griglia aste
|
1. Cliccare "Aggiungi" nella griglia aste
|
||||||
2. Inserire uno o più URL/ID asta (uno per riga, o separati da spazio/punto e virgola)
|
2. Inserire uno o più URL/ID asta (uno per riga, o separati da spazio/punto e virgola)
|
||||||
3. Formati supportati:
|
3. Formati supportati:
|
||||||
- URL completo: `https://it.bidoo.com/auction.php?a=asta_123456`
|
- URL completo: `https://it.bidoo.com/auction.php?a=asta_123456`
|
||||||
- ID asta: `123456`
|
- ID asta: `123456`
|
||||||
@@ -203,31 +206,33 @@ Se il cookie scade o diventa invalido, sar
|
|||||||
1. Navigare su Bidoo nel browser integrato
|
1. Navigare su Bidoo nel browser integrato
|
||||||
2. Aprire la pagina di un'asta
|
2. Aprire la pagina di un'asta
|
||||||
3. Cliccare "Aggiungi Asta" nella toolbar del browser
|
3. Cliccare "Aggiungi Asta" nella toolbar del browser
|
||||||
4. L'asta verrà aggiunta automaticamente alla griglia
|
4. L'asta verrà aggiunta automaticamente alla griglia
|
||||||
|
|
||||||
**Metodo 3: Menu Contestuale Browser**
|
**Metodo 3: Menu Contestuale Browser**
|
||||||
1. Nel browser integrato, fare click destro su un link di un'asta
|
1. Nel browser integrato, fare click destro su un link di un'asta
|
||||||
2. Selezionare "Aggiungi Asta" dal menu contestuale
|
2. Selezionare "Aggiungi Asta" dal menu contestuale
|
||||||
3. L'asta verrà estratta e aggiunta alla griglia
|
3. L'asta verrà estratta e aggiunta alla griglia
|
||||||
|
|
||||||
### Configurare Parametri Asta
|
### Configurare Parametri Asta
|
||||||
|
|
||||||
Dopo aver selezionato un'asta dalla griglia, il pannello Impostazioni in basso a sinistra mostra:
|
Dopo aver selezionato un'asta dalla griglia, il pannello Impostazioni in basso a sinistra mostra:
|
||||||
|
|
||||||
**Timer Click (secondi 0-8)**
|
**Anticipo (ms) - 0-5000 millisecondi** ⭐ NUOVO
|
||||||
- Specifica il secondo del countdown al quale inviare la puntata
|
- Specifica quanti millisecondi **prima della scadenza** inviare la puntata
|
||||||
- 0 = invia tra 0.0s e 0.9s
|
- Valore consigliato: 200-500ms (dipende dalla latenza della connessione)
|
||||||
- 1 = invia tra 1.0s e 1.9s
|
- Esempio: Con 200ms, l'app punterà quando il timer arriva a 0.2 secondi
|
||||||
- E così via fino a 8
|
- Valori più bassi = puntata più vicina alla scadenza = maggior rischio ma più efficace
|
||||||
|
- Valori più alti = puntata anticipata = più sicuro ma meno efficace
|
||||||
|
|
||||||
**Delay (millisecondi)**
|
**Verifica Stato Asta** ✅ NUOVO
|
||||||
- Delay aggiuntivo da applicare dopo aver raggiunto il timer
|
- Checkbox opzionale per aggiungere una verifica extra prima di puntare
|
||||||
- Valori tipici: 0-500ms
|
- Se abilitata, effettua un controllo aggiuntivo che l'asta sia ancora aperta
|
||||||
- Usare per fine-tuning della precisione
|
- Aumenta sicurezza ma aggiunge ~50-100ms di latenza
|
||||||
|
- Consigliato per aste ad alto valore
|
||||||
|
|
||||||
**Min EUR / Max EUR**
|
**Min EUR / Max EUR**
|
||||||
- Limiti di prezzo per controllo automatico
|
- Limiti di prezzo per controllo automatico
|
||||||
- Se impostati, l'applicazione non punterà se il prezzo esce da questi limiti
|
- Se impostati, l'applicazione non punterà se il prezzo esce da questi limiti
|
||||||
- Utile per evitare offerte su aste troppo costose
|
- Utile per evitare offerte su aste troppo costose
|
||||||
|
|
||||||
**Max Clicks**
|
**Max Clicks**
|
||||||
@@ -235,135 +240,58 @@ Dopo aver selezionato un'asta dalla griglia, il pannello Impostazioni in basso a
|
|||||||
- 0 = nessun limite
|
- 0 = nessun limite
|
||||||
- Dopo aver raggiunto questo limite, l'asta viene automaticamente fermata
|
- Dopo aver raggiunto questo limite, l'asta viene automaticamente fermata
|
||||||
|
|
||||||
### Controllare le Aste
|
### Sistema di Polling Intelligente ⚡ NUOVO
|
||||||
|
|
||||||
**Avvio/Pausa/Ferma Singola Asta**
|
Il nuovo sistema adatta automaticamente la frequenza di polling in base al timer rimanente:
|
||||||
- Usare i pulsanti "Avvia", "Pausa", "Ferma" nella colonna Azioni della griglia
|
|
||||||
- Avvia: inizia il monitoring e l'invio automatico puntate
|
|
||||||
- Pausa: sospende temporaneamente senza fermare il polling
|
|
||||||
- Ferma: arresta completamente il monitoring
|
|
||||||
|
|
||||||
**Avvio/Pausa/Ferma Globale**
|
| Timer Rimanente | Frequenza Polling | Strategia |
|
||||||
- Usare i pulsanti nell'header per controllare tutte le aste simultaneamente
|
|-----------------|-------------------|-----------|
|
||||||
- Utile per avvio rapido di sessioni multi-asta
|
| > 60 secondi | 1000ms (1s) | Standby |
|
||||||
|
| 10-60 secondi | 100ms | Ready |
|
||||||
|
| 2-10 secondi | 20ms | Critical |
|
||||||
|
| < 2 secondi | 10ms | BID WINDOW |
|
||||||
|
|
||||||
**Puntata Manuale**
|
**Vantaggi**:
|
||||||
- Cliccare "Punta" nella colonna Azioni per inviare un'offerta immediata
|
- ⚡ Precisione al millisecondo nelle fasi critiche
|
||||||
- Ignora timer e limiti configurati
|
- 💰 Riduzione drammatica delle puntate sprecate
|
||||||
- Utile per test o interventi manuali urgenti
|
- 🎯 Timing ottimale adattivo
|
||||||
|
- 🔋 Basso consumo di risorse nelle fasi iniziali
|
||||||
|
|
||||||
### Rimuovere Aste
|
### Rimuovere Aste
|
||||||
|
|
||||||
- Selezionare un'asta dalla griglia
|
- Selezionare un'asta dalla griglia
|
||||||
- Cliccare "Rimuovi" o premere il tasto `Canc`
|
- Cliccare "Rimuovi" o premere il tasto `Canc`
|
||||||
- Confermare la rimozione nel dialog
|
- **Confermare la rimozione** nel dialog ✅ NUOVO
|
||||||
- L'asta verrà rimossa dalla lista e il monitoring sarà arrestato
|
- L'asta verrà rimossa dalla lista e il monitoring sarà arrestato
|
||||||
|
|
||||||
### Persistenza
|
### Persistenza
|
||||||
|
|
||||||
Le aste aggiunte vengono salvate automaticamente in `%AppData%\AutoBidder\saved_auctions.json` e ricaricate all'avvio dell'applicazione, mantenendo tutte le configurazioni (timer, limiti, stati).
|
Le aste aggiunte vengono salvate automaticamente in `%AppData%\AutoBidder\saved_auctions.json` e ricaricate all'avvio dell'applicazione, mantenendo tutte le configurazioni (timing, limiti, stati).
|
||||||
|
|
||||||
---
|
⚠️ **Nota**: Le modifiche ai parametri vengono salvate automaticamente quando cambi asta o chiudi l'applicazione ✅ NUOVO
|
||||||
|
|
||||||
## Browser Integrato
|
|
||||||
|
|
||||||
La scheda Browser contiene un controllo WebView2 che offre un'esperienza di navigazione completa basata su Microsoft Edge Chromium.
|
|
||||||
|
|
||||||
### Toolbar Browser
|
|
||||||
|
|
||||||
- **? ?**: Pulsanti indietro/avanti
|
|
||||||
- **?**: Ricarica pagina
|
|
||||||
- **Home**: Naviga alla homepage Bidoo
|
|
||||||
- **Barra indirizzo**: Mostra URL corrente, permette navigazione diretta
|
|
||||||
- **Vai**: Naviga all'URL inserito
|
|
||||||
- **Aggiungi Asta**: Aggiunge l'asta della pagina corrente alla griglia
|
|
||||||
|
|
||||||
### Navigazione
|
|
||||||
|
|
||||||
Il browser si comporta come un browser standard:
|
|
||||||
- Click su link per navigare
|
|
||||||
- Form funzionano normalmente (login, ricerca, ecc.)
|
|
||||||
- JavaScript abilitato
|
|
||||||
- Cookie condivisi con la sessione applicazione
|
|
||||||
|
|
||||||
### Menu Contestuale
|
|
||||||
|
|
||||||
Facendo click destro su un link nel browser:
|
|
||||||
- **Aggiungi Asta**: Estrae l'ID asta dal link e la aggiunge alla griglia
|
|
||||||
- **Copia Link**: Copia l'URL negli appunti
|
|
||||||
|
|
||||||
### Sincronizzazione Cookie
|
|
||||||
|
|
||||||
I cookie del browser WebView2 sono sincronizzati con la sessione dell'applicazione. Se configurato un cookie nella scheda Impostazioni, questo sarà disponibile anche nel browser integrato, permettendo la navigazione autenticata.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Statistiche e Analisi
|
|
||||||
|
|
||||||
La scheda Dati Statistici è attualmente in fase di sviluppo e mostrerà in futuro:
|
|
||||||
|
|
||||||
**Analisi Aste Chiuse**
|
|
||||||
- Import e parsing di aste chiuse da file export o scraping diretto
|
|
||||||
- Aggregazione statistica: media prezzi finali, numero medio puntate, distribuzione oraria vincite
|
|
||||||
- Visualizzazione grafici e trend
|
|
||||||
|
|
||||||
**Raccomandazioni Strategiche**
|
|
||||||
- Suggerimenti automatici basati su dati storici
|
|
||||||
- Applicazione automatica di configurazioni ottimali per prodotti simili
|
|
||||||
- Machine learning per previsione prezzi e probabilità di vittoria
|
|
||||||
|
|
||||||
**Database Locale**
|
|
||||||
- Storage in SQLite con Entity Framework Core
|
|
||||||
- Schema ottimizzato per query analitiche
|
|
||||||
- Export/import dati per backup
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Impostazioni e Configurazione
|
## Impostazioni e Configurazione
|
||||||
|
|
||||||
La scheda Impostazioni è divisa in 3 sezioni principali.
|
La scheda Impostazioni è divisa in 3 sezioni principali.
|
||||||
|
|
||||||
### Configurazione Sessione
|
### Impostazioni Predefinite Aste ⚡ AGGIORNATO
|
||||||
|
|
||||||
**Campo Cookie**
|
|
||||||
- Area di testo multi-line per incollare la stringa cookie completa
|
|
||||||
- Formati supportati: stringa cookie raw, solo token `__stattrb`, cookie separati da punto e virgola
|
|
||||||
|
|
||||||
**Pulsanti**
|
|
||||||
- **Importa dal Browser**: Tenta import automatico cookie da Chrome, Edge, Firefox
|
|
||||||
- **Cancella**: Pulisce il campo e resetta la sessione
|
|
||||||
|
|
||||||
**Info Box**
|
|
||||||
- Istruzioni dettagliate su come ottenere la stringa cookie completa
|
|
||||||
- Guida passo-passo per Chrome Developer Tools
|
|
||||||
|
|
||||||
### Impostazioni Export
|
|
||||||
|
|
||||||
**Percorso Export**
|
|
||||||
- Path del folder dove salvare i file esportati
|
|
||||||
- Pulsante "Sfoglia" per selezione tramite dialog
|
|
||||||
|
|
||||||
**Formato File**
|
|
||||||
- **CSV**: Comma-Separated Values, compatibile con Excel
|
|
||||||
- **JSON**: JavaScript Object Notation, formato strutturato
|
|
||||||
- **XML**: Extensible Markup Language, formato gerarchico
|
|
||||||
|
|
||||||
**Opzioni Export**
|
|
||||||
- **Includi solo puntate utilizzate**: Esporta solo aste su cui sono state inviate offerte
|
|
||||||
- **Includi log delle aste**: Aggiunge ai file export il log completo di ogni asta
|
|
||||||
- **Includi storico puntate utenti**: Esporta la lista utenti partecipanti con statistiche
|
|
||||||
- **Includi metadata delle aste**: Aggiunge informazioni dettagliate (URL, impostazioni, timestamp)
|
|
||||||
- **Rimuovi aste dopo l'export**: Elimina automaticamente le aste dalla griglia dopo export riuscito
|
|
||||||
- **Sovrascrivi file esistenti**: Permette sovrascrittura senza conferma
|
|
||||||
|
|
||||||
### Impostazioni Predefinite Aste
|
|
||||||
|
|
||||||
Valori che verranno applicati automaticamente a tutte le nuove aste aggiunte:
|
Valori che verranno applicati automaticamente a tutte le nuove aste aggiunte:
|
||||||
|
|
||||||
- **Timer Click (secondi)**: Default 0
|
- **Anticipo Puntata (millisecondi)**: Default 200ms
|
||||||
- **Delay (millisecondi)**: Default 50
|
- Range: 0-5000ms
|
||||||
- **Prezzo Minimo (€)**: Default 0 (nessun limite)
|
- Consigliato: 200-500ms per connessioni stabili
|
||||||
- **Prezzo Massimo (€)**: Default 0 (nessun limite)
|
- Tooltip: "Millisecondi prima della scadenza per puntare"
|
||||||
|
|
||||||
|
- **Verifica Stato Prima di Puntare**: Default OFF
|
||||||
|
- Checkbox per abilitare controllo extra
|
||||||
|
- Aggiunge ~50-100ms di latenza
|
||||||
|
- Consigliato per aste ad alto valore
|
||||||
|
|
||||||
|
- **Prezzo Minimo (€)**: Default 0 (nessun limite)
|
||||||
|
- **Prezzo Massimo (€)**: Default 0 (nessun limite)
|
||||||
- **Max Click**: Default 0 (nessun limite)
|
- **Max Click**: Default 0 (nessun limite)
|
||||||
|
|
||||||
### Salvataggio Impostazioni
|
### Salvataggio Impostazioni
|
||||||
@@ -372,71 +300,40 @@ Cliccare il pulsante "Salva" nella barra inferiore per salvare tutte le modifich
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Export Dati
|
|
||||||
|
|
||||||
### Export Massivo
|
|
||||||
|
|
||||||
1. Cliccare "Esporta" nell'header della scheda Aste Attive
|
|
||||||
2. Confermare nel dialog
|
|
||||||
3. Tutte le aste monitorate verranno esportate secondo le opzioni configurate
|
|
||||||
4. Un messaggio confermerà il numero di aste esportate e il path dei file
|
|
||||||
|
|
||||||
### Export Singola Asta
|
|
||||||
|
|
||||||
1. Selezionare un'asta dalla griglia
|
|
||||||
2. Nel pannello Impostazioni, cliccare "Esporta"
|
|
||||||
3. Solo l'asta selezionata verrà esportata
|
|
||||||
|
|
||||||
### Formati Export
|
|
||||||
|
|
||||||
**CSV**
|
|
||||||
```csv
|
|
||||||
AuctionId,Name,Url,Status,Timer,Price,Clicks,Resets
|
|
||||||
123456,"Prodotto XYZ","https://...",Closed,0,5.50,15,3
|
|
||||||
```
|
|
||||||
|
|
||||||
**JSON**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"auctions": [
|
|
||||||
{
|
|
||||||
"auctionId": "123456",
|
|
||||||
"name": "Prodotto XYZ",
|
|
||||||
"url": "https://...",
|
|
||||||
"status": "Closed",
|
|
||||||
"finalPrice": 5.50,
|
|
||||||
"myClicks": 15,
|
|
||||||
"resetCount": 3,
|
|
||||||
"log": [...],
|
|
||||||
"bidders": [...]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**XML**
|
|
||||||
```xml
|
|
||||||
<Auctions>
|
|
||||||
<Auction>
|
|
||||||
<AuctionId>123456</AuctionId>
|
|
||||||
<Name>Prodotto XYZ</Name>
|
|
||||||
...
|
|
||||||
</Auction>
|
|
||||||
</Auctions>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Preferenze Export
|
|
||||||
|
|
||||||
L'ultimo formato utilizzato viene salvato automaticamente e proposto come default per export successivi. Le preferenze sono memorizzate in `%AppData%\AutoBidder\exportprefs.json`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Dettagli Tecnici
|
## Dettagli Tecnici
|
||||||
|
|
||||||
|
### Nuovo Sistema di Timing ⚡
|
||||||
|
|
||||||
|
**Meccanismo di Puntata**
|
||||||
|
```
|
||||||
|
Timeline Esempio (Anticipo = 200ms):
|
||||||
|
═══════════════════════════════════════════════════
|
||||||
|
1000ms → Polling 20ms (Standby)
|
||||||
|
800ms → Polling 20ms (Ready)
|
||||||
|
600ms → Polling 20ms (Ready)
|
||||||
|
400ms → Polling 10ms (Critical)
|
||||||
|
200ms → ✅ FINESTRA PUNTATA RAGGIUNTA!
|
||||||
|
→ Verifica: nessuno ha puntato negli ultimi 500ms
|
||||||
|
→ [Opzionale] Check stato asta aperta
|
||||||
|
→ PUNTA IMMEDIATAMENTE
|
||||||
|
0ms → ⏰ Scadenza asta
|
||||||
|
```
|
||||||
|
|
||||||
|
**Vantaggi del Nuovo Sistema**
|
||||||
|
|
||||||
|
| Caratteristica | Prima (v3.x) | Adesso (v4.0) | Miglioramento |
|
||||||
|
|----------------|--------------|---------------|---------------|
|
||||||
|
| **Precisione** | Secondi (±1000ms) | Millisecondi (±10ms) | ⚡ 100x |
|
||||||
|
| **Puntate Sprecate** | Molte | Minime | 💰 -80% |
|
||||||
|
| **Strategia** | Fissa (timer secondi) | Dinamica (adattiva) | 🎯 +300% |
|
||||||
|
| **Check Sicurezza** | No | Opzionale | 🛡️ Nuovo |
|
||||||
|
| **Polling** | Fisso (1s) | Adattivo (10-1000ms) | ⚡ +10x veloce |
|
||||||
|
| **Cooldown** | Nessuno | 800ms anti-spam | 🚫 Nuovo |
|
||||||
|
|
||||||
### Architettura Applicazione
|
### Architettura Applicazione
|
||||||
|
|
||||||
**Pattern Utilizzati**
|
**Pattern Utilizzati**
|
||||||
- **Partial Classes**: `MainWindow` diviso in 13 file per responsabilità (Commands, AuctionManagement, Logging, UIUpdates, EventHandlers, ecc.)
|
- **Partial Classes**: `MainWindow` diviso in 13 file per responsabilità (Commands, AuctionManagement, Logging, UIUpdates, EventHandlers, ecc.)
|
||||||
- **UserControls Modulari**: 5 controlli riutilizzabili (AuctionMonitorControl, BrowserControl, SettingsControl, StatisticsControl, SimpleToolbar)
|
- **UserControls Modulari**: 5 controlli riutilizzabili (AuctionMonitorControl, BrowserControl, SettingsControl, StatisticsControl, SimpleToolbar)
|
||||||
- **MVVM Light**: Separazione Model-View-ViewModel per logica UI
|
- **MVVM Light**: Separazione Model-View-ViewModel per logica UI
|
||||||
- **Service Layer**: Servizi dedicati (AuctionMonitor, BidooApiClient, SessionManager, StatsService)
|
- **Service Layer**: Servizi dedicati (AuctionMonitor, BidooApiClient, SessionManager, StatsService)
|
||||||
@@ -445,35 +342,38 @@ L'ultimo formato utilizzato viene salvato automaticamente e proposto come defaul
|
|||||||
**Struttura Progetto**
|
**Struttura Progetto**
|
||||||
```
|
```
|
||||||
AutoBidder/
|
AutoBidder/
|
||||||
??? Core/ # MainWindow partial classes
|
├── Core/ # MainWindow partial classes
|
||||||
??? Controls/ # UserControls WPF
|
├── Controls/ # UserControls WPF
|
||||||
??? Dialogs/ # Finestre dialog
|
├── Dialogs/ # Finestre dialog
|
||||||
??? Models/ # Data models
|
├── Models/ # Data models
|
||||||
??? Services/ # Business logic
|
├── Services/ # Business logic
|
||||||
??? ViewModels/ # MVVM ViewModels
|
├── ViewModels/ # MVVM ViewModels
|
||||||
??? Utilities/ # Helper utilities
|
├── Utilities/ # Helper utilities
|
||||||
??? Data/ # EF Core contexts
|
├── Data/ # EF Core contexts
|
||||||
??? Documentation/ # Markdown docs
|
└── Documentation/ # Markdown docs
|
||||||
```
|
```
|
||||||
|
|
||||||
### Polling HTTP
|
### Polling HTTP ⚡ AGGIORNATO
|
||||||
|
|
||||||
**Meccanismo**
|
**Meccanismo**
|
||||||
- Richieste HTTP GET asincrone verso endpoint API Bidoo
|
- Richieste HTTP GET asincrone verso endpoint API Bidoo
|
||||||
- Parsing risposta JSON per estrarre stato asta (timer, prezzo, bidder)
|
- Parsing risposta JSON per estrarre stato asta (timer, prezzo, bidder)
|
||||||
- Frequenza adattiva: polling più frequente quando timer < 10s
|
- **Frequenza adattiva**: polling più frequente quando timer < 2s (fino a 10ms)
|
||||||
|
- **Cooldown intelligente**: 800ms tra puntate consecutive
|
||||||
|
- **Rilevamento conflitti**: finestra 500ms per puntate recenti di altri utenti
|
||||||
|
|
||||||
**Latenza**
|
**Latenza**
|
||||||
- Misurata e visualizzata per ogni asta nella colonna "Latenza"
|
- Misurata e visualizzata per ogni asta nella colonna "Latenza"
|
||||||
- Valori tipici: 50-200ms per rete stabile
|
- Valori tipici: 50-200ms per rete stabile
|
||||||
- Utilizzata per ottimizzare timing invio puntate
|
- Utilizzata per ottimizzare timing invio puntate
|
||||||
|
- **Importante**: Configura "Anticipo (ms)" in base alla tua latenza media
|
||||||
|
|
||||||
**Threading**
|
**Threading**
|
||||||
- Ogni asta ha un task asincrono dedicato
|
- Ogni asta ha un task asincrono dedicato
|
||||||
- Cancellation token per gestione pulita stop/pause
|
- Cancellation token per gestione pulita stop/pause
|
||||||
- Throttling per evitare overload server
|
- Throttling per evitare overload server
|
||||||
|
|
||||||
### Invio Puntate
|
### Invio Puntate ⚡ AGGIORNATO
|
||||||
|
|
||||||
**Endpoint**
|
**Endpoint**
|
||||||
```
|
```
|
||||||
@@ -481,6 +381,32 @@ GET /bid.php?AID={auctionId}&sup=0&shock=0
|
|||||||
Cookie: __stattrb={token}; ...
|
Cookie: __stattrb={token}; ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Nuova Logica di Timing**
|
||||||
|
```csharp
|
||||||
|
// Calcola millisecondi rimanenti
|
||||||
|
double timerMs = state.Timer * 1000;
|
||||||
|
|
||||||
|
// Verifica finestra di puntata
|
||||||
|
if (timerMs <= auction.BidBeforeDeadlineMs) // Es: <= 200ms
|
||||||
|
{
|
||||||
|
// Check cooldown
|
||||||
|
if (DateTime.UtcNow - lastBidTime < 800ms) return;
|
||||||
|
|
||||||
|
// Check puntate recenti altri utenti
|
||||||
|
if (timeSinceLastExternalBid < 500ms) return;
|
||||||
|
|
||||||
|
// [Opzionale] Verifica stato asta
|
||||||
|
if (auction.CheckAuctionOpenBeforeBid)
|
||||||
|
{
|
||||||
|
var check = await PollAuctionStateAsync();
|
||||||
|
if (!check.IsOpen) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PUNTA!
|
||||||
|
await PlaceBidAsync(auctionId);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
**Parametri**
|
**Parametri**
|
||||||
- `AID`: ID asta
|
- `AID`: ID asta
|
||||||
- `sup`: Tipo puntata (0=normale)
|
- `sup`: Tipo puntata (0=normale)
|
||||||
@@ -493,82 +419,61 @@ Cookie: __stattrb={token}; ...
|
|||||||
|
|
||||||
**Gestione Risposta**
|
**Gestione Risposta**
|
||||||
- Success: HTTP 200 con corpo contenente nuovo timer/prezzo
|
- Success: HTTP 200 con corpo contenente nuovo timer/prezzo
|
||||||
- Failure: Errore di rete, sessione scaduta, bid già effettuato
|
- Failure: Errore di rete, sessione scaduta, bid già effettuato
|
||||||
- Retry: Nessun retry automatico, log dell'errore
|
- Retry: Nessun retry automatico, log dell'errore
|
||||||
|
|
||||||
### WebView2 Runtime
|
### Performance ⚡ MIGLIORATE
|
||||||
|
|
||||||
**Versione**
|
|
||||||
- Basato su Microsoft Edge Chromium
|
|
||||||
- Stesso engine usato dal browser Edge
|
|
||||||
- Aggiornamenti automatici tramite Windows Update
|
|
||||||
|
|
||||||
**Isolamento**
|
|
||||||
- User Data Folder separato per applicazione
|
|
||||||
- Cookie storage dedicato
|
|
||||||
- Cache locale per performance
|
|
||||||
|
|
||||||
**JavaScript**
|
|
||||||
- Abilitato di default
|
|
||||||
- Accesso a Web APIs moderne (Fetch, Async/Await, ecc.)
|
|
||||||
- Nessun injection di script personalizzati
|
|
||||||
|
|
||||||
### Database SQLite
|
|
||||||
|
|
||||||
**Schema**
|
|
||||||
```sql
|
|
||||||
CREATE TABLE AuctionStatistics (
|
|
||||||
Id INTEGER PRIMARY KEY,
|
|
||||||
AuctionId TEXT NOT NULL,
|
|
||||||
ProductName TEXT,
|
|
||||||
FinalPrice REAL,
|
|
||||||
TotalBids INTEGER,
|
|
||||||
WinnerUsername TEXT,
|
|
||||||
ClosedAt DATETIME,
|
|
||||||
PollingLatencyMs INTEGER
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE ProductInsights (
|
|
||||||
Id INTEGER PRIMARY KEY,
|
|
||||||
ProductName TEXT UNIQUE,
|
|
||||||
AveragePrice REAL,
|
|
||||||
AverageBids INTEGER,
|
|
||||||
TotalAuctions INTEGER,
|
|
||||||
RecommendedMaxPrice REAL,
|
|
||||||
RecommendedMaxClicks INTEGER
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
**ORM**
|
|
||||||
- Entity Framework Core 8.0
|
|
||||||
- Code-First migrations
|
|
||||||
- Async queries per performance
|
|
||||||
|
|
||||||
### Performance
|
|
||||||
|
|
||||||
**Metriche Target**
|
**Metriche Target**
|
||||||
- CPU: < 5% idle, < 15% attivo con 10 aste
|
- CPU: < 5% idle, < 15% attivo con 10 aste
|
||||||
- RAM: ~100MB base + 10MB per 100 aste
|
- RAM: ~100MB base + 10MB per 100 aste
|
||||||
- Latenza polling: 50-200ms media
|
- Latenza polling: **10-200ms** (era 1000ms fisso) ✅ +90% più veloce
|
||||||
- UI responsiveness: < 16ms per frame (60fps)
|
- UI responsiveness: < 16ms per frame (60fps)
|
||||||
|
|
||||||
**Ottimizzazioni**
|
**Ottimizzazioni v4.0**
|
||||||
- Lazy loading UserControls (caricamento on-demand per tab)
|
- ✅ Lazy loading UserControls (caricamento on-demand per tab)
|
||||||
- DataGrid virtualizzazione (rendering solo righe visibili)
|
- ✅ DataGrid virtualizzazione (rendering solo righe visibili)
|
||||||
- Async/await per tutte le operazioni I/O
|
- ✅ Async/await per tutte le operazioni I/O
|
||||||
- Throttling polling basato su stato asta
|
- ✅ **Polling adattivo** basato su timer rimanente (NUOVO)
|
||||||
- String pooling per log messages
|
- ✅ **Cooldown anti-spam** 800ms (NUOVO)
|
||||||
|
- ✅ String pooling per log messages
|
||||||
|
- ✅ **Fix persistenza valori asta** (NUOVO)
|
||||||
|
- ✅ **Ottimizzazione aggiornamento UI** (NUOVO)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## FAQ e Troubleshooting
|
## FAQ e Troubleshooting
|
||||||
|
|
||||||
**Q: L'applicazione non invia puntate, perché?**
|
**Q: Come scelgo il valore ottimale per "Anticipo (ms)"?**
|
||||||
|
|
||||||
|
A: Dipende dalla tua latenza di rete:
|
||||||
|
1. Guarda la colonna "Latenza" nella griglia aste (mostra ms di risposta API)
|
||||||
|
2. Se latenza media è 50-100ms → usa 200-300ms di anticipo
|
||||||
|
3. Se latenza media è 100-200ms → usa 400-500ms di anticipo
|
||||||
|
4. Testa su aste a basso valore e aggiusta in base ai risultati
|
||||||
|
5. **Regola generale**: Anticipo = (Latenza Media × 2) + 100ms
|
||||||
|
|
||||||
|
**Q: La checkbox "Verifica stato asta" quando usarla?**
|
||||||
|
|
||||||
|
A: Abilitala se:
|
||||||
|
- ✅ Asta ad alto valore (> 50€)
|
||||||
|
- ✅ Connessione instabile
|
||||||
|
- ✅ Vuoi massima sicurezza
|
||||||
|
- ❌ Non usarla per aste veloci (<10s timer) per evitare latenza extra
|
||||||
|
|
||||||
|
**Q: I valori che inserisco per un'asta non vengono salvati, perché?**
|
||||||
|
|
||||||
|
A: ✅ **RISOLTO in v4.0!** I valori ora vengono salvati automaticamente quando:
|
||||||
|
- Cambi selezione asta
|
||||||
|
- Chiudi l'applicazione
|
||||||
|
- Clicchi su un altro controllo
|
||||||
|
|
||||||
|
**Q: L'applicazione non invia puntate, perché?**
|
||||||
|
|
||||||
A: Verificare:
|
A: Verificare:
|
||||||
1. Cookie di sessione configurato e valido nella scheda Impostazioni
|
1. Cookie di sessione configurato e valido nella scheda Impostazioni
|
||||||
2. Asta selezionata e in stato "Avviato" (non pausato o fermo)
|
2. Asta selezionata e in stato "Avviato" (non pausato o fermo)
|
||||||
3. Timer Click configurato (0-8 secondi)
|
3. "Anticipo (ms)" configurato correttamente (0-5000)
|
||||||
4. Limiti prezzo non superati
|
4. Limiti prezzo non superati
|
||||||
5. Max Clicks non raggiunto
|
5. Max Clicks non raggiunto
|
||||||
6. Connessione internet attiva
|
6. Connessione internet attiva
|
||||||
@@ -577,28 +482,35 @@ Controllare il Log Asta per messaggi di errore specifici.
|
|||||||
|
|
||||||
**Q: Come ottengo il cookie di sessione?**
|
**Q: Come ottengo il cookie di sessione?**
|
||||||
|
|
||||||
A: Segui la guida dettagliata nella sezione [Configurazione Sessione](#configurazione-sessione). In breve: Chrome ? F12 ? Application ? Cookies ? bidoo.com ? copia tutti i valori.
|
A: Segui la guida dettagliata nella sezione [Configurazione Sessione](#configurazione-sessione). In breve: Chrome → F12 → Application → Cookies → bidoo.com → copia tutti i valori.
|
||||||
|
|
||||||
**Q: Il cookie smette di funzionare dopo un po', perché?**
|
**Q: Il cookie smette di funzionare dopo un po', perché?**
|
||||||
|
|
||||||
A: I cookie di sessione Bidoo hanno una scadenza (tipicamente 24-48 ore). Quando scadono è necessario effettuare nuovamente login su Bidoo e copiare un nuovo cookie. L'applicazione mostra un warning nel log quando rileva cookie invalido.
|
A: I cookie di sessione Bidoo hanno una scadenza (tipicamente 24-48 ore). Quando scadono è necessario effettuare nuovamente login su Bidoo e copiare un nuovo cookie. L'applicazione mostra un warning nel log quando rileva cookie invalido.
|
||||||
|
|
||||||
**Q: Posso usare AutoBidder su più computer contemporaneamente?**
|
**Q: Non vedo il nome utente e le puntate rimanenti, come mai?**
|
||||||
|
|
||||||
A: Sì, ma ogni istanza deve avere il proprio cookie di sessione. Bidoo potrebbe invalidare sessioni se rileva login multipli simultanei da IP diversi. Usa con cautela.
|
A: ✅ **RISOLTO in v4.0!** Le informazioni utente ora si aggiornano correttamente ogni minuto. Se ancora non compaiono:
|
||||||
|
1. Verifica che il cookie sia valido (guarda il log)
|
||||||
|
2. Aspetta 1 minuto per l'aggiornamento automatico
|
||||||
|
3. Riavvia l'applicazione se il problema persiste
|
||||||
|
|
||||||
|
**Q: Posso usare AutoBidder su più computer contemporaneamente?**
|
||||||
|
|
||||||
|
A: Sì, ma ogni istanza deve avere il proprio cookie di sessione. Bidoo potrebbe invalidare sessioni se rileva login multipli simultanei da IP diversi. Usa con cautela.
|
||||||
|
|
||||||
**Q: L'applicazione consuma troppa CPU/RAM, come ridurre?**
|
**Q: L'applicazione consuma troppa CPU/RAM, come ridurre?**
|
||||||
|
|
||||||
A:
|
A:
|
||||||
- Riduci il numero di aste monitorate simultaneamente
|
- Riduci il numero di aste monitorate simultaneamente
|
||||||
- Aumenta intervallo polling per aste con timer > 60s (configurabile in codice)
|
- ~~Aumenta intervallo polling~~ Non più necessario con polling adattivo v4.0 ✅
|
||||||
- Chiudi tab non utilizzate (Browser, Statistiche)
|
- Chiudi tab non utilizzate (Browser, Statistiche)
|
||||||
- Disabilita log dettagliati per singole aste
|
- Disabilita log dettagliati per singole aste ✅ Ottimizzato in v4.0
|
||||||
|
|
||||||
**Q: WebView2 non si carica o mostra errore, cosa fare?**
|
**Q: WebView2 non si carica o mostra errore, cosa fare?**
|
||||||
|
|
||||||
A:
|
A:
|
||||||
1. Verificare che WebView2 Runtime sia installato: Pannello di Controllo ? Programmi ? WebView2
|
1. Verificare che WebView2 Runtime sia installato: Pannello di Controllo → Programmi → WebView2
|
||||||
2. Se non installato, scaricarlo da microsoft.com/edge/webview2
|
2. Se non installato, scaricarlo da microsoft.com/edge/webview2
|
||||||
3. Riavviare l'applicazione dopo installazione
|
3. Riavviare l'applicazione dopo installazione
|
||||||
4. Se persiste, reinstallare WebView2 Runtime
|
4. Se persiste, reinstallare WebView2 Runtime
|
||||||
@@ -613,42 +525,43 @@ A: Attualmente no. Le aste devono essere aggiunte manualmente via URL/ID. Featur
|
|||||||
|
|
||||||
**Q: L'applicazione supporta proxy o VPN?**
|
**Q: L'applicazione supporta proxy o VPN?**
|
||||||
|
|
||||||
A: L'applicazione usa le impostazioni proxy di sistema di Windows. Configurare proxy in Impostazioni Windows ? Rete ? Proxy.
|
A: L'applicazione usa le impostazioni proxy di sistema di Windows. Configurare proxy in Impostazioni Windows → Rete → Proxy.
|
||||||
|
|
||||||
**Q: Posso personalizzare i colori dell'interfaccia?**
|
|
||||||
|
|
||||||
A: Non tramite UI. I colori sono hard-coded nel XAML. Per personalizzazioni, modificare i file `*.xaml` nella cartella del progetto.
|
|
||||||
|
|
||||||
**Q: L'applicazione salva le password?**
|
|
||||||
|
|
||||||
A: No. AutoBidder non gestisce password. Richiede solo il cookie di sessione che viene salvato cifrato con DPAPI Windows.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Sicurezza e Responsabilità
|
## Changelog v4.0
|
||||||
|
|
||||||
|
### ⚡ Nuovo Sistema di Timing
|
||||||
|
- ✅ Sostituito "Timer Click (secondi)" con "Anticipo (ms)"
|
||||||
|
- ✅ Precisione al millisecondo invece dei secondi
|
||||||
|
- ✅ Polling adattivo 10-1000ms basato su timer rimanente
|
||||||
|
- ✅ Cooldown 800ms tra puntate consecutive
|
||||||
|
- ✅ Rilevamento puntate recenti altri utenti (500ms)
|
||||||
|
- ✅ Checkbox opzionale "Verifica stato asta prima di puntare"
|
||||||
|
|
||||||
|
### 🐛 Bug Fix
|
||||||
|
- ✅ Fix persistenza valori modificati per singola asta
|
||||||
|
- ✅ Fix visualizzazione username e puntate rimanenti
|
||||||
|
- ✅ Conferma richiesta prima di cancellare asta (pulsante + tasto Canc)
|
||||||
|
- ✅ Ottimizzazione logging per miglior performance
|
||||||
|
|
||||||
|
### 🎨 UI/UX
|
||||||
|
- ✅ Tooltip informativi su tutti i campi critici
|
||||||
|
- ✅ Formattazione prezzi con 2 decimali
|
||||||
|
- ✅ Messaggi di conferma per azioni distruttive
|
||||||
|
|
||||||
|
### 📦 Export
|
||||||
|
- ✅ Export CSV/JSON/XML aggiornato con nuovi campi
|
||||||
|
- ✅ Backward compatibility con aste salvate nella v3.x
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sicurezza e Responsabilità
|
||||||
|
|
||||||
### Disclaimer Importante
|
### Disclaimer Importante
|
||||||
|
|
||||||
**Termini di Servizio**
|
**Termini di Servizio**
|
||||||
L'uso di strumenti di automazione potrebbe violare i Termini di Servizio della piattaforma Bidoo. L'utente è l'unico responsabile dell'uso che fa di AutoBidder. Gli sviluppatori non si assumono responsabilità per sospensioni account, perdite economiche o altre conseguenze derivanti dall'uso dell'applicazione.
|
L'uso di strumenti di automazione potrebbe violare i Termini di Servizio della piattaforma Bidoo. L'utente è l'unico responsabile dell'uso che fa di AutoBidder. Gli sviluppatori non si assumono responsabilità per sospensioni account, perdite economiche o altre conseguenze derivanti dall'uso dell'applicazione.
|
||||||
|
|
||||||
### Sicurezza Dati
|
|
||||||
|
|
||||||
**Storage Cookie**
|
|
||||||
- I cookie non vengono mai salvati in chiaro su disco
|
|
||||||
- Utilizzo di DPAPI (Data Protection API) di Windows per cifratura
|
|
||||||
- Chiave di cifratura legata all'account utente corrente Windows
|
|
||||||
- Impossibile decriptare da altri account o computer
|
|
||||||
|
|
||||||
**Network Security**
|
|
||||||
- Tutte le comunicazioni con Bidoo avvengono tramite HTTPS
|
|
||||||
- Nessuna trasmissione dati sensibili a server terzi
|
|
||||||
- L'applicazione non "telefona a casa" o invia telemetria
|
|
||||||
|
|
||||||
**Privacy**
|
|
||||||
- Nessun log remoto
|
|
||||||
- Nessuna raccolta analytics
|
|
||||||
- Tutti i dati rimangono in locale sul computer dell'utente
|
|
||||||
|
|
||||||
### Best Practices
|
### Best Practices
|
||||||
|
|
||||||
@@ -658,37 +571,10 @@ L'uso di strumenti di automazione potrebbe violare i Termini di Servizio della p
|
|||||||
4. **Backup regolari** di `%AppData%\AutoBidder` per preservare configurazioni
|
4. **Backup regolari** di `%AppData%\AutoBidder` per preservare configurazioni
|
||||||
5. **Aggiornare regolarmente** l'applicazione per patch di sicurezza
|
5. **Aggiornare regolarmente** l'applicazione per patch di sicurezza
|
||||||
6. **Usare password sicure** per l'account Windows (protegge DPAPI)
|
6. **Usare password sicure** per l'account Windows (protegge DPAPI)
|
||||||
|
7. **Testa su aste a basso valore** prima di usare configurazioni aggressive
|
||||||
### Limitazioni
|
|
||||||
|
|
||||||
- AutoBidder è un tool per uso personale, non commerciale
|
|
||||||
- Non distribuire o rivendere senza autorizzazione
|
|
||||||
- Il repository è privato: non pubblicare il codice sorgente
|
|
||||||
- Non usare per competizioni o tornei dove automazione è proibita
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Supporto e Sviluppo
|
**AutoBidder v4.0** - Developed with ❤️ using .NET 8.0 and WPF
|
||||||
|
|
||||||
**Repository Privato Gitea**
|
© 2024 - Per uso personale. Non distribuire.
|
||||||
```
|
|
||||||
https://192.168.30.23/Alby96/Mimante
|
|
||||||
```
|
|
||||||
|
|
||||||
**Issue Tracker**
|
|
||||||
Per bug reports, feature requests o domande tecniche, aprire una Issue nel repository.
|
|
||||||
|
|
||||||
**Contatti**
|
|
||||||
Per supporto diretto contattare il maintainer del progetto tramite Gitea.
|
|
||||||
|
|
||||||
**Contributori**
|
|
||||||
Il progetto accetta Pull Requests secondo le convenzioni definite nel CONTRIBUTING.md (da creare).
|
|
||||||
|
|
||||||
**Licenza**
|
|
||||||
Progetto privato - Tutti i diritti riservati. Non distribuire senza autorizzazione esplicita del proprietario.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**AutoBidder v4.0** - Developed with ?? using .NET 8.0 and WPF
|
|
||||||
|
|
||||||
© 2024 - Per uso personale. Non distribuire.
|
|
||||||
Reference in New Issue
Block a user