Rimuovi export settings, aggiungi filtro livello log
- Rimossa la sezione "Impostazioni Export" dalla UI e dal code-behind, inclusi controlli, eventi e file legacy di export. - Aggiunta configurazione del livello minimo di log (ErrorOnly, Normal, Informational, Debug, Trace) con guida e legenda colori. - La funzione di log ora filtra i messaggi in base al livello selezionato. - Aggiornati modelli di impostazioni e enum LogLevel per supportare i nuovi livelli. - Refactoring commenti e uniformità sezioni impostazioni. - Migliorata la chiarezza del log di avvio applicazione.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using System.Configuration;
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Windows;
|
||||
|
||||
|
||||
@@ -89,79 +89,7 @@
|
||||
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Margin="30,20">
|
||||
|
||||
<!-- SEZIONE 1: Impostazioni Export -->
|
||||
<Border Background="#252526"
|
||||
BorderBrush="#3E3E42"
|
||||
BorderThickness="1"
|
||||
CornerRadius="4"
|
||||
Padding="20"
|
||||
Margin="0,0,0,20">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Impostazioni Export"
|
||||
Style="{StaticResource SectionHeader}"/>
|
||||
|
||||
<TextBlock Text="Percorso di Export"
|
||||
Style="{StaticResource FieldLabel}"/>
|
||||
|
||||
<Grid Margin="0,0,0,20">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox Grid.Column="0"
|
||||
x:Name="ExportPathTextBox"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,0,10,0"/>
|
||||
|
||||
<Button Grid.Column="1"
|
||||
x:Name="ExportBrowseButton"
|
||||
Content="Sfoglia"
|
||||
Background="#007ACC"
|
||||
Style="{StaticResource ModernButton}"
|
||||
Click="ExportBrowseButton_Click"/>
|
||||
</Grid>
|
||||
|
||||
<TextBlock Text="Formato File"
|
||||
Style="{StaticResource FieldLabel}"/>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,20">
|
||||
<RadioButton x:Name="ExtCsv"
|
||||
Content="CSV"
|
||||
GroupName="ExportFormat"
|
||||
IsChecked="True"/>
|
||||
<RadioButton x:Name="ExtJson"
|
||||
Content="JSON"
|
||||
GroupName="ExportFormat"/>
|
||||
<RadioButton x:Name="ExtXml"
|
||||
Content="XML"
|
||||
GroupName="ExportFormat"/>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Text="Opzioni di Export"
|
||||
Style="{StaticResource FieldLabel}"/>
|
||||
|
||||
<StackPanel>
|
||||
<CheckBox x:Name="IncludeUsedBids"
|
||||
Content="Includi solo puntate utilizzate"
|
||||
IsChecked="True"/>
|
||||
<CheckBox x:Name="IncludeLogs"
|
||||
Content="Includi log delle aste"/>
|
||||
<CheckBox x:Name="IncludeUserBids"
|
||||
Content="Includi storico puntate utenti"
|
||||
IsChecked="True"/>
|
||||
<CheckBox x:Name="IncludeMetadata"
|
||||
Content="Includi metadata delle aste"
|
||||
IsChecked="True"/>
|
||||
<CheckBox x:Name="RemoveAfterExport"
|
||||
Content="Rimuovi aste dopo l'export"/>
|
||||
<CheckBox x:Name="OverwriteExisting"
|
||||
Content="Sovrascrivi file esistenti"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- SEZIONE 2: Impostazioni Predefinite Aste -->
|
||||
<!-- SEZIONE 1: Impostazioni Predefinite Aste -->
|
||||
<Border Background="#252526"
|
||||
BorderBrush="#3E3E42"
|
||||
BorderThickness="1"
|
||||
@@ -194,7 +122,7 @@
|
||||
<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="DefaultBidBeforeDeadlineMsTextBox" Text="200" Margin="10,10"/>
|
||||
|
||||
<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"/>
|
||||
<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"/>
|
||||
<CheckBox Grid.Row="1" Grid.Column="1" x:Name="DefaultCheckAuctionOpenCheckBox" Margin="10,10" VerticalAlignment="Center"/>
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Prezzo Minimo (€)" Foreground="#CCCCCC" Margin="0,10" VerticalAlignment="Center"/>
|
||||
@@ -209,7 +137,7 @@
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- SEZIONE 3: Stato Iniziale Aste -->
|
||||
<!-- SEZIONE 2: Stato Iniziale Aste -->
|
||||
<Border Background="#252526"
|
||||
BorderBrush="#3E3E42"
|
||||
BorderThickness="1"
|
||||
@@ -294,7 +222,7 @@
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- SEZIONE 4: Protezione Account -->
|
||||
<!-- SEZIONE 3: Protezione Account -->
|
||||
<Border Background="#252526"
|
||||
BorderBrush="#3E3E42"
|
||||
BorderThickness="1"
|
||||
@@ -357,7 +285,7 @@
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- SEZIONE 5: Limiti Log -->
|
||||
<!-- SEZIONE 4: Limiti Log -->
|
||||
<Border Background="#252526"
|
||||
BorderBrush="#3E3E42"
|
||||
BorderThickness="1"
|
||||
@@ -439,6 +367,131 @@
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- SEZIONE 5: Livello di Dettaglio Log -->
|
||||
<Border Background="#252526"
|
||||
BorderBrush="#3E3E42"
|
||||
BorderThickness="1"
|
||||
CornerRadius="4"
|
||||
Padding="20"
|
||||
Margin="0,20,0,0">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Livello di Dettaglio Log"
|
||||
Style="{StaticResource SectionHeader}"/>
|
||||
|
||||
<TextBlock Text="Configura il livello minimo dei messaggi da visualizzare nel log. Livelli più bassi mostrano solo messaggi critici, livelli più alti mostrano tutti i dettagli (utile per debug)."
|
||||
Foreground="#999999"
|
||||
FontSize="12"
|
||||
TextWrapping="Wrap"
|
||||
Margin="0,0,0,20"/>
|
||||
|
||||
<!-- Radio Buttons per livello log -->
|
||||
<StackPanel>
|
||||
<RadioButton x:Name="LogLevelErrorOnly"
|
||||
Content="Solo Errori"
|
||||
GroupName="LogLevel"
|
||||
Margin="0,5"
|
||||
ToolTip="Mostra solo errori critici (uso minimo per produzione)"/>
|
||||
|
||||
<RadioButton x:Name="LogLevelNormal"
|
||||
Content="Normale (Errori + Avvisi)"
|
||||
GroupName="LogLevel"
|
||||
IsChecked="True"
|
||||
Margin="0,5"
|
||||
ToolTip="Mostra errori e avvisi (uso giornaliero raccomandato)"/>
|
||||
|
||||
<RadioButton x:Name="LogLevelInformational"
|
||||
Content="Informativo (Include operazioni completate)"
|
||||
GroupName="LogLevel"
|
||||
Margin="0,5"
|
||||
ToolTip="Mostra anche messaggi informativi e conferme (uso dettagliato)"/>
|
||||
|
||||
<RadioButton x:Name="LogLevelDebug"
|
||||
Content="Debug (Include dettagli tecnici)"
|
||||
GroupName="LogLevel"
|
||||
Margin="0,5"
|
||||
ToolTip="Mostra anche messaggi di debug per sviluppo"/>
|
||||
|
||||
<RadioButton x:Name="LogLevelTrace"
|
||||
Content="Trace (Tutto - molto verboso)"
|
||||
GroupName="LogLevel"
|
||||
Margin="0,5"
|
||||
ToolTip="Mostra ogni singola operazione (debug avanzato)"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Info Box -->
|
||||
<Border Style="{StaticResource InfoBox}" Margin="0,15,0,0">
|
||||
<StackPanel>
|
||||
<TextBlock Text="🔍 Guida alla Scelta"
|
||||
FontWeight="Bold"
|
||||
Foreground="#FFB700"
|
||||
Margin="0,0,0,10"/>
|
||||
<TextBlock Foreground="#CCCCCC"
|
||||
FontSize="12"
|
||||
TextWrapping="Wrap"
|
||||
Margin="0,0,0,5">
|
||||
<Run>Solo Errori: Usa in produzione per vedere solo problemi critici.</Run>
|
||||
</TextBlock>
|
||||
<TextBlock Foreground="#CCCCCC"
|
||||
FontSize="12"
|
||||
TextWrapping="Wrap"
|
||||
Margin="0,0,0,5">
|
||||
<Run>Normale: Raccomandato per uso giornaliero. Mostra errori e avvisi importanti.</Run>
|
||||
</TextBlock>
|
||||
<TextBlock Foreground="#CCCCCC"
|
||||
FontSize="12"
|
||||
TextWrapping="Wrap"
|
||||
Margin="0,0,0,5">
|
||||
<Run>Informativo: Utile per seguire le operazioni principali (aggiunte aste, puntate).</Run>
|
||||
</TextBlock>
|
||||
<TextBlock Foreground="#CCCCCC"
|
||||
FontSize="12"
|
||||
TextWrapping="Wrap"
|
||||
Margin="0,0,0,5">
|
||||
<Run>Debug: Per sviluppo. Mostra parametri chiamate API e valori interni.</Run>
|
||||
</TextBlock>
|
||||
<TextBlock Foreground="#CCCCCC"
|
||||
FontSize="12"
|
||||
TextWrapping="Wrap"
|
||||
Margin="0,0,0,10">
|
||||
<Run>Trace: Debug avanzato. Mostra ogni singola chiamata (molto verboso).</Run>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Foreground="#CCCCCC"
|
||||
FontSize="12"
|
||||
FontWeight="Bold"
|
||||
Margin="0,5,0,5">Legenda colori log:</TextBlock>
|
||||
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="11" Margin="0,2">
|
||||
<Run Foreground="#E81123">■ ROSSO</Run>
|
||||
<Run Foreground="#CCCCCC"> = Errori critici</Run>
|
||||
</TextBlock>
|
||||
<TextBlock FontSize="11" Margin="0,2">
|
||||
<Run Foreground="#FFB700">■ ARANCIONE</Run>
|
||||
<Run Foreground="#CCCCCC"> = Avvisi</Run>
|
||||
</TextBlock>
|
||||
<TextBlock FontSize="11" Margin="0,2">
|
||||
<Run Foreground="#64B4FF">■ BLU</Run>
|
||||
<Run Foreground="#CCCCCC"> = Informazioni</Run>
|
||||
</TextBlock>
|
||||
<TextBlock FontSize="11" Margin="0,2">
|
||||
<Run Foreground="#00D800">■ VERDE</Run>
|
||||
<Run Foreground="#CCCCCC"> = Operazioni riuscite</Run>
|
||||
</TextBlock>
|
||||
<TextBlock FontSize="11" Margin="0,2">
|
||||
<Run Foreground="#FF8CFF">■ MAGENTA</Run>
|
||||
<Run Foreground="#CCCCCC"> = Debug</Run>
|
||||
</TextBlock>
|
||||
<TextBlock FontSize="11" Margin="0,2">
|
||||
<Run Foreground="#A0A0A0">■ GRIGIO</Run>
|
||||
<Run Foreground="#CCCCCC"> = Trace</Run>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
|
||||
@@ -25,26 +25,6 @@ namespace AutoBidder.Controls
|
||||
// ?? NUOVO: Proprietà per limite storia puntate
|
||||
public TextBox MaxBidHistoryEntries => MaxBidHistoryEntriesTextBox;
|
||||
|
||||
// ========================================
|
||||
// NOTA: Eventi cookie RIMOSSI
|
||||
// Gestione automatica tramite browser
|
||||
// ========================================
|
||||
|
||||
private void ExportBrowseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
RaiseEvent(new RoutedEventArgs(ExportBrowseClickedEvent, this));
|
||||
}
|
||||
|
||||
private void SaveSettingsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
RaiseEvent(new RoutedEventArgs(SaveSettingsClickedEvent, this));
|
||||
}
|
||||
|
||||
private void CancelSettingsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
RaiseEvent(new RoutedEventArgs(CancelSettingsClickedEvent, this));
|
||||
}
|
||||
|
||||
private void SaveDefaultsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
RaiseEvent(new RoutedEventArgs(SaveDefaultsClickedEvent, this));
|
||||
@@ -60,10 +40,7 @@ namespace AutoBidder.Controls
|
||||
{
|
||||
try
|
||||
{
|
||||
// 1. Salva impostazioni export
|
||||
RaiseEvent(new RoutedEventArgs(SaveSettingsClickedEvent, this));
|
||||
|
||||
// 2. Salva impostazioni predefinite aste
|
||||
// Salva impostazioni predefinite aste (export rimosso)
|
||||
RaiseEvent(new RoutedEventArgs(SaveDefaultsClickedEvent, this));
|
||||
|
||||
// UNICO MessageBox di conferma
|
||||
@@ -88,44 +65,16 @@ namespace AutoBidder.Controls
|
||||
private void CancelAllSettings_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Annulla tutte le modifiche
|
||||
RaiseEvent(new RoutedEventArgs(CancelSettingsClickedEvent, this));
|
||||
RaiseEvent(new RoutedEventArgs(CancelDefaultsClickedEvent, this));
|
||||
}
|
||||
|
||||
// Routed Events (cookie events RIMOSSI)
|
||||
public static readonly RoutedEvent ExportBrowseClickedEvent = EventManager.RegisterRoutedEvent(
|
||||
"ExportBrowseClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(SettingsControl));
|
||||
|
||||
public static readonly RoutedEvent SaveSettingsClickedEvent = EventManager.RegisterRoutedEvent(
|
||||
"SaveSettingsClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(SettingsControl));
|
||||
|
||||
public static readonly RoutedEvent CancelSettingsClickedEvent = EventManager.RegisterRoutedEvent(
|
||||
"CancelSettingsClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(SettingsControl));
|
||||
|
||||
// Routed Events
|
||||
public static readonly RoutedEvent SaveDefaultsClickedEvent = EventManager.RegisterRoutedEvent(
|
||||
"SaveDefaultsClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(SettingsControl));
|
||||
|
||||
public static readonly RoutedEvent CancelDefaultsClickedEvent = EventManager.RegisterRoutedEvent(
|
||||
"CancelDefaultsClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(SettingsControl));
|
||||
|
||||
public event RoutedEventHandler ExportBrowseClicked
|
||||
{
|
||||
add { AddHandler(ExportBrowseClickedEvent, value); }
|
||||
remove { RemoveHandler(ExportBrowseClickedEvent, value); }
|
||||
}
|
||||
|
||||
public event RoutedEventHandler SaveSettingsClicked
|
||||
{
|
||||
add { AddHandler(SaveSettingsClickedEvent, value); }
|
||||
remove { RemoveHandler(SaveSettingsClickedEvent, value); }
|
||||
}
|
||||
|
||||
public event RoutedEventHandler CancelSettingsClicked
|
||||
{
|
||||
add { AddHandler(CancelSettingsClickedEvent, value); }
|
||||
remove { RemoveHandler(CancelSettingsClickedEvent, value); }
|
||||
}
|
||||
|
||||
public event RoutedEventHandler SaveDefaultsClicked
|
||||
{
|
||||
add { AddHandler(SaveDefaultsClickedEvent, value); }
|
||||
|
||||
@@ -25,4 +25,4 @@ namespace AutoBidder.Controls
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,351 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Xml.Linq;
|
||||
using AutoBidder.Utilities;
|
||||
|
||||
namespace AutoBidder
|
||||
{
|
||||
/// <summary>
|
||||
/// Export functionality event handlers
|
||||
/// </summary>
|
||||
public partial class MainWindow
|
||||
{
|
||||
private CancellationTokenSource? _exportCts;
|
||||
|
||||
private void LoadExportSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
var s = SettingsManager.Load();
|
||||
if (s != null)
|
||||
{
|
||||
ExportPathTextBox.Text = s.ExportPath ?? string.Empty;
|
||||
if (!string.IsNullOrEmpty(s.LastExportExt))
|
||||
{
|
||||
var ext = s.LastExportExt.ToLowerInvariant();
|
||||
if (ext == ".json") ExtJson.IsChecked = true;
|
||||
else if (ext == ".xml") ExtXml.IsChecked = true;
|
||||
else ExtCsv.IsChecked = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ExtCsv.IsChecked = true;
|
||||
}
|
||||
|
||||
try { var cbOpen = this.FindName("ExportOpenToolbar") as System.Windows.Controls.CheckBox; if (cbOpen != null) cbOpen.IsChecked = s.ExportOpen; } catch { }
|
||||
try { var cbClosed = this.FindName("ExportClosedToolbar") as System.Windows.Controls.CheckBox; if (cbClosed != null) cbClosed.IsChecked = s.ExportClosed; } catch { }
|
||||
try { var cbUnknown = this.FindName("ExportUnknownToolbar") as System.Windows.Controls.CheckBox; if (cbUnknown != null) cbUnknown.IsChecked = s.ExportUnknown; } catch { }
|
||||
|
||||
try { IncludeUsedBids.IsChecked = s.IncludeOnlyUsedBids; } catch { }
|
||||
try { IncludeLogs.IsChecked = s.IncludeLogs; } catch { }
|
||||
try { IncludeUserBids.IsChecked = s.IncludeUserBids; } catch { }
|
||||
try { IncludeMetadata.IsChecked = s.IncludeMetadata; } catch { }
|
||||
try { RemoveAfterExport.IsChecked = s.RemoveAfterExport; } catch { }
|
||||
try { OverwriteExisting.IsChecked = s.OverwriteExisting; } catch { }
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private async void ExportAllButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = SettingsManager.Load();
|
||||
string ext = ExtJson.IsChecked == true ? ".json" : ExtXml.IsChecked == true ? ".xml" : ".csv";
|
||||
var dlg = new Microsoft.Win32.SaveFileDialog() { FileName = "auctions_export" + ext, Filter = "CSV files|*.csv|JSON files|*.json|XML files|*.xml|All files|*.*" };
|
||||
if (dlg.ShowDialog(this) != true) return;
|
||||
var path = dlg.FileName;
|
||||
|
||||
var all = _auctionMonitor.GetAuctions();
|
||||
var includeOpen = (this.FindName("ExportOpenToolbar") as System.Windows.Controls.CheckBox)?.IsChecked == true;
|
||||
var includeClosed = (this.FindName("ExportClosedToolbar") as System.Windows.Controls.CheckBox)?.IsChecked == true;
|
||||
var includeUnknown = (this.FindName("ExportUnknownToolbar") as System.Windows.Controls.CheckBox)?.IsChecked == true;
|
||||
|
||||
var selection = all.Where(a =>
|
||||
(includeOpen && a.IsActive) ||
|
||||
(includeClosed && !a.IsActive) ||
|
||||
(includeUnknown && ((a.BidHistory == null || a.BidHistory.Count == 0) && (a.BidderStats == null || a.BidderStats.Count == 0)))
|
||||
).ToList();
|
||||
|
||||
if (selection.Count == 0)
|
||||
{
|
||||
MessageBox.Show(this, "Nessuna asta da esportare.", "Esporta Aste", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
Log("[INFO] Esportazione in corso...", LogLevel.Info);
|
||||
|
||||
await Task.Run(() =>
|
||||
{
|
||||
if (path.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var json = System.Text.Json.JsonSerializer.Serialize(selection, new System.Text.Json.JsonSerializerOptions { WriteIndented = true });
|
||||
File.WriteAllText(path, json, Encoding.UTF8);
|
||||
}
|
||||
else if (path.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var doc = new XDocument(new XElement("Auctions",
|
||||
from a in selection
|
||||
select new XElement("Auction",
|
||||
new XElement("AuctionId", a.AuctionId),
|
||||
new XElement("Name", a.Name),
|
||||
new XElement("OriginalUrl", a.OriginalUrl ?? string.Empty)
|
||||
)
|
||||
));
|
||||
doc.Save(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
CsvExporter.ExportAllAuctions(selection, path);
|
||||
}
|
||||
});
|
||||
|
||||
try { ExportPreferences.SaveLastExportExtension(Path.GetExtension(path)); } catch { }
|
||||
|
||||
MessageBox.Show(this, "Esportazione completata.", "Esporta Aste", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
Log($"[EXPORT] Aste esportate -> {path}", LogLevel.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"[ERRORE] Esportazione massiva: {ex.Message}", LogLevel.Error);
|
||||
MessageBox.Show(this, "Errore durante esportazione: " + ex.Message, "Esporta Aste", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async void ExportToolbarButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = SettingsManager.Load();
|
||||
var chosenExt = ExtJson.IsChecked == true ? ".json" : ExtXml.IsChecked == true ? ".xml" : ".csv";
|
||||
|
||||
var includeOpen = (this.FindName("ExportOpenToolbar") as System.Windows.Controls.CheckBox)?.IsChecked == true;
|
||||
var includeClosed = (this.FindName("ExportClosedToolbar") as System.Windows.Controls.CheckBox)?.IsChecked == true;
|
||||
var includeUnknown = (this.FindName("ExportUnknownToolbar") as System.Windows.Controls.CheckBox)?.IsChecked == true;
|
||||
|
||||
var all = _auctionMonitor.GetAuctions();
|
||||
var selection = all.Where(a =>
|
||||
(includeOpen && a.IsActive) ||
|
||||
(includeClosed && !a.IsActive) ||
|
||||
(includeUnknown && ((a.BidHistory == null || a.BidHistory.Count == 0) && (a.BidderStats == null || a.BidderStats.Count == 0)))
|
||||
).ToList();
|
||||
|
||||
if (selection.Count == 0)
|
||||
{
|
||||
MessageBox.Show(this, "Nessuna asta da esportare.", "Esporta Aste", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
string folder;
|
||||
if (!string.IsNullOrWhiteSpace(settings?.ExportPath) && Directory.Exists(settings.ExportPath))
|
||||
{
|
||||
folder = settings.ExportPath!;
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show(this, "Percorso export non configurato o non valido.\nConfigura il percorso nelle Impostazioni.", "Percorso Export", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var confirm = MessageBox.Show(this, $"Esportare {selection.Count} asta/e in:\n{folder}\n\nFormato: {chosenExt.ToUpperInvariant()}\n(Un file separato per ogni asta)", "Conferma Esportazione", MessageBoxButton.YesNo, MessageBoxImage.Question);
|
||||
if (confirm != MessageBoxResult.Yes) return;
|
||||
|
||||
Log("[INFO] Esportazione in corso...", LogLevel.Info);
|
||||
|
||||
int exported = 0;
|
||||
int skipped = 0;
|
||||
|
||||
await Task.Run(() =>
|
||||
{
|
||||
foreach (var a in selection)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filename = $"auction_{a.AuctionId}{chosenExt}";
|
||||
var path = Path.Combine(folder, filename);
|
||||
|
||||
if (File.Exists(path) && settings != null && settings.OverwriteExisting != true)
|
||||
{
|
||||
skipped++;
|
||||
Log($"[SKIP] File già esistente: {filename}", LogLevel.Warn);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (chosenExt.Equals(".json", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// JSON EXPORT - AGGIORNATO
|
||||
var obj = new
|
||||
{
|
||||
AuctionId = a.AuctionId,
|
||||
Name = a.Name,
|
||||
OriginalUrl = a.OriginalUrl,
|
||||
MinPrice = a.MinPrice,
|
||||
MaxPrice = a.MaxPrice,
|
||||
BidBeforeDeadlineMs = a.BidBeforeDeadlineMs,
|
||||
CheckAuctionOpenBeforeBid = a.CheckAuctionOpenBeforeBid,
|
||||
IsActive = a.IsActive,
|
||||
IsPaused = a.IsPaused,
|
||||
BidHistory = a.BidHistory,
|
||||
Bidders = a.BidderStats.Values.ToList(),
|
||||
AuctionLog = a.AuctionLog.ToList()
|
||||
};
|
||||
var json = System.Text.Json.JsonSerializer.Serialize(obj, new System.Text.Json.JsonSerializerOptions { WriteIndented = true });
|
||||
File.WriteAllText(path, json, Encoding.UTF8);
|
||||
}
|
||||
else if (chosenExt.Equals(".xml", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// XML EXPORT - AGGIORNATO
|
||||
var doc = new XDocument(
|
||||
new XElement("AuctionExport",
|
||||
new XElement("Metadata",
|
||||
new XElement("AuctionId", a.AuctionId),
|
||||
new XElement("Name", a.Name ?? string.Empty),
|
||||
new XElement("OriginalUrl", a.OriginalUrl ?? string.Empty),
|
||||
new XElement("MinPrice", a.MinPrice),
|
||||
new XElement("MaxPrice", a.MaxPrice),
|
||||
new XElement("BidBeforeDeadlineMs", a.BidBeforeDeadlineMs),
|
||||
new XElement("CheckAuctionOpenBeforeBid", a.CheckAuctionOpenBeforeBid),
|
||||
new XElement("IsActive", a.IsActive),
|
||||
new XElement("IsPaused", a.IsPaused)
|
||||
),
|
||||
new XElement("FinalPrice", a.BidHistory?.LastOrDefault()?.Price.ToString("F2", CultureInfo.InvariantCulture) ?? string.Empty),
|
||||
new XElement("TotalBids", a.BidHistory?.Count ?? 0),
|
||||
new XElement("Bidders",
|
||||
from b in a.BidderStats.Values.Where(x => x.BidCount > 0)
|
||||
select new XElement("Bidder",
|
||||
new XAttribute("Username", b.Username ?? string.Empty),
|
||||
new XAttribute("BidCount", b.BidCount),
|
||||
new XElement("LastBidTime", b.LastBidTimeDisplay ?? string.Empty)
|
||||
)
|
||||
),
|
||||
new XElement("AuctionLog",
|
||||
from l in a.AuctionLog
|
||||
select new XElement("Entry", l)
|
||||
),
|
||||
new XElement("BidHistory",
|
||||
from bh in a.BidHistory
|
||||
select new XElement("Entry",
|
||||
new XElement("Timestamp", bh.Timestamp.ToString("o")),
|
||||
new XElement("EventType", bh.EventType),
|
||||
new XElement("Bidder", bh.Bidder),
|
||||
new XElement("Price", bh.Price.ToString("F2", CultureInfo.InvariantCulture)),
|
||||
new XElement("Timer", bh.Timer.ToString("F2", CultureInfo.InvariantCulture)),
|
||||
new XElement("LatencyMs", bh.LatencyMs),
|
||||
new XElement("Success", bh.Success),
|
||||
new XElement("Notes", bh.Notes)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
doc.Save(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
// CSV EXPORT - AGGIORNATO
|
||||
using var sw = new StreamWriter(path, false, Encoding.UTF8);
|
||||
sw.WriteLine("Field,Value");
|
||||
sw.WriteLine($"AuctionId,{a.AuctionId}");
|
||||
sw.WriteLine($"Name,\"{EscapeCsv(a.Name)}\"");
|
||||
sw.WriteLine($"OriginalUrl,\"{EscapeCsv(a.OriginalUrl)}\"");
|
||||
sw.WriteLine($"MinPrice,{a.MinPrice}");
|
||||
sw.WriteLine($"MaxPrice,{a.MaxPrice}");
|
||||
sw.WriteLine($"BidBeforeDeadlineMs,{a.BidBeforeDeadlineMs}");
|
||||
sw.WriteLine($"CheckAuctionOpenBeforeBid,{a.CheckAuctionOpenBeforeBid}");
|
||||
sw.WriteLine($"IsActive,{a.IsActive}");
|
||||
sw.WriteLine($"IsPaused,{a.IsPaused}");
|
||||
sw.WriteLine();
|
||||
sw.WriteLine("--Auction Log--");
|
||||
sw.WriteLine("Message");
|
||||
foreach (var l in a.AuctionLog)
|
||||
{
|
||||
sw.WriteLine($"\"{EscapeCsv(l)}\"");
|
||||
}
|
||||
sw.WriteLine();
|
||||
sw.WriteLine("--Bidders--");
|
||||
sw.WriteLine("Username,BidCount,LastBidTime");
|
||||
foreach (var b in a.BidderStats.Values)
|
||||
{
|
||||
sw.WriteLine($"\"{EscapeCsv(b.Username)}\",{b.BidCount},\"{EscapeCsv(b.LastBidTimeDisplay)}\"");
|
||||
}
|
||||
sw.WriteLine();
|
||||
sw.WriteLine("--BidHistory--");
|
||||
sw.WriteLine("Timestamp,EventType,Bidder,Price,Timer,LatencyMs,Success,Notes");
|
||||
foreach (var bh in a.BidHistory)
|
||||
{
|
||||
sw.WriteLine($"\"{EscapeCsv(bh.Timestamp.ToString("o"))}\",{bh.EventType},\"{EscapeCsv(bh.Bidder)}\",{bh.Price:F2},{bh.Timer:F2},{bh.LatencyMs},{bh.Success},\"{EscapeCsv(bh.Notes)}\"");
|
||||
}
|
||||
}
|
||||
|
||||
exported++;
|
||||
Log($"[EXPORT] Asta esportata -> {path}", LogLevel.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"[ERRORE] Export asta {a.AuctionId}: {ex.Message}", LogLevel.Error);
|
||||
skipped++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
try { ExportPreferences.SaveLastExportExtension(chosenExt); } catch { }
|
||||
|
||||
MessageBox.Show(this, $"Esportazione completata.\n\nEsportate: {exported}\nIgnorate: {skipped}\nPercorso: {folder}", "Esporta Aste", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
Log($"[EXPORT] Completato: {exported} esportate, {skipped} ignorate -> {folder}", LogLevel.Success);
|
||||
|
||||
if ((this.FindName("RemoveAfterExport") as System.Windows.Controls.CheckBox)?.IsChecked == true && selection.Count > 0)
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
foreach (var a in selection)
|
||||
{
|
||||
try
|
||||
{
|
||||
_auctionMonitor.RemoveAuction(a.AuctionId);
|
||||
var vm = _auctionViewModels.FirstOrDefault(x => x.AuctionId == a.AuctionId);
|
||||
if (vm != null)
|
||||
{
|
||||
_auctionViewModels.Remove(vm);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"[WARN] Errore rimozione asta {a.AuctionId}: {ex.Message}", LogLevel.Warn);
|
||||
}
|
||||
}
|
||||
|
||||
SaveAuctions();
|
||||
UpdateTotalCount();
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"[ERRORE] Esportazione toolbar: {ex.Message}", LogLevel.Error);
|
||||
MessageBox.Show(this, "Errore durante esportazione: " + ex.Message, "Esporta Aste", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExportBrowseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var dlg = new Microsoft.Win32.SaveFileDialog() { FileName = "export.csv", Filter = "CSV files|*.csv|All files|*.*" };
|
||||
if (dlg.ShowDialog(this) == true)
|
||||
{
|
||||
ExportPathTextBox.Text = Path.GetDirectoryName(dlg.FileName) ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private string EscapeCsv(string? value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value)) return string.Empty;
|
||||
return value.Replace("\"", "\"\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,33 @@ namespace AutoBidder
|
||||
// ?? NUOVO: Carica limite minimo puntate
|
||||
MinimumRemainingBidsTextBox.Text = settings.MinimumRemainingBids.ToString();
|
||||
|
||||
// ?? NUOVO: Carica livello log
|
||||
var logLevelErrorOnly = Settings.FindName("LogLevelErrorOnly") as System.Windows.Controls.RadioButton;
|
||||
var logLevelNormal = Settings.FindName("LogLevelNormal") as System.Windows.Controls.RadioButton;
|
||||
var logLevelInformational = Settings.FindName("LogLevelInformational") as System.Windows.Controls.RadioButton;
|
||||
var logLevelDebug = Settings.FindName("LogLevelDebug") as System.Windows.Controls.RadioButton;
|
||||
var logLevelTrace = Settings.FindName("LogLevelTrace") as System.Windows.Controls.RadioButton;
|
||||
|
||||
switch (settings.MinLogLevel)
|
||||
{
|
||||
case "ErrorOnly":
|
||||
if (logLevelErrorOnly != null) logLevelErrorOnly.IsChecked = true;
|
||||
break;
|
||||
case "Informational":
|
||||
if (logLevelInformational != null) logLevelInformational.IsChecked = true;
|
||||
break;
|
||||
case "Debug":
|
||||
if (logLevelDebug != null) logLevelDebug.IsChecked = true;
|
||||
break;
|
||||
case "Trace":
|
||||
if (logLevelTrace != null) logLevelTrace.IsChecked = true;
|
||||
break;
|
||||
case "Normal":
|
||||
default:
|
||||
if (logLevelNormal != null) logLevelNormal.IsChecked = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Aggiorna indicatore visivo
|
||||
UpdateMinBidsIndicator(settings.MinimumRemainingBids);
|
||||
|
||||
@@ -85,67 +112,6 @@ namespace AutoBidder
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveSettingsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// ? Carica le impostazioni esistenti per non perdere gli altri valori
|
||||
var settings = Utilities.SettingsManager.Load() ?? new Utilities.AppSettings();
|
||||
|
||||
// === SEZIONE EXPORT: Percorso e Formato ===
|
||||
settings.ExportPath = ExportPathTextBox.Text;
|
||||
settings.LastExportExt = ExtJson.IsChecked == true ? ".json" : ExtXml.IsChecked == true ? ".xml" : ".csv";
|
||||
|
||||
// === SEZIONE EXPORT: Scope (Aste da esportare) ===
|
||||
var cbClosed = this.FindName("ExportClosedToolbar") as System.Windows.Controls.CheckBox;
|
||||
var cbUnknown = this.FindName("ExportUnknownToolbar") as System.Windows.Controls.CheckBox;
|
||||
var cbOpen = this.FindName("ExportOpenToolbar") as System.Windows.Controls.CheckBox;
|
||||
|
||||
var scope = "All";
|
||||
if (cbClosed != null && cbClosed.IsChecked == true) scope = "Closed";
|
||||
else if (cbUnknown != null && cbUnknown.IsChecked == true) scope = "Unknown";
|
||||
else if (cbOpen != null && cbOpen.IsChecked == true) scope = "Open";
|
||||
|
||||
settings.ExportScope = scope;
|
||||
settings.ExportOpen = cbOpen?.IsChecked ?? true;
|
||||
settings.ExportClosed = cbClosed?.IsChecked ?? true;
|
||||
settings.ExportUnknown = cbUnknown?.IsChecked ?? true;
|
||||
|
||||
// === SEZIONE EXPORT: Opzioni ? FIX: Aggiunte le 3 checkbox mancanti ===
|
||||
settings.IncludeOnlyUsedBids = IncludeUsedBids.IsChecked == true;
|
||||
settings.IncludeLogs = IncludeLogs.IsChecked == true;
|
||||
settings.IncludeUserBids = IncludeUserBids.IsChecked == true;
|
||||
settings.IncludeMetadata = IncludeMetadata.IsChecked == true; // ? AGGIUNTO
|
||||
settings.RemoveAfterExport = RemoveAfterExport.IsChecked == true; // ? AGGIUNTO
|
||||
settings.OverwriteExisting = OverwriteExisting.IsChecked == true; // ? AGGIUNTO
|
||||
|
||||
SettingsManager.Save(settings);
|
||||
ExportPreferences.SaveLastExportExtension(settings.LastExportExt);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"[ERRORE] Salvataggio impostazioni export: {ex.Message}", LogLevel.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void CancelSettingsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Ricarica impostazioni export
|
||||
LoadExportSettings();
|
||||
|
||||
// NOTA: Reload cookie RIMOSSO - ora automatico tramite browser
|
||||
|
||||
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)
|
||||
{
|
||||
try
|
||||
@@ -239,6 +205,29 @@ namespace AutoBidder
|
||||
Log("[ERRORE] Valore limite minimo puntate non valido (deve essere >= 0)", LogLevel.Error);
|
||||
}
|
||||
|
||||
// ?? NUOVO: Salva livello log
|
||||
var logLevelErrorOnly = Settings.FindName("LogLevelErrorOnly") as System.Windows.Controls.RadioButton;
|
||||
var logLevelNormal = Settings.FindName("LogLevelNormal") as System.Windows.Controls.RadioButton;
|
||||
var logLevelInformational = Settings.FindName("LogLevelInformational") as System.Windows.Controls.RadioButton;
|
||||
var logLevelDebug = Settings.FindName("LogLevelDebug") as System.Windows.Controls.RadioButton;
|
||||
var logLevelTrace = Settings.FindName("LogLevelTrace") as System.Windows.Controls.RadioButton;
|
||||
|
||||
string selectedLogLevel = "Normal"; // Default
|
||||
if (logLevelErrorOnly?.IsChecked == true)
|
||||
selectedLogLevel = "ErrorOnly";
|
||||
else if (logLevelInformational?.IsChecked == true)
|
||||
selectedLogLevel = "Informational";
|
||||
else if (logLevelDebug?.IsChecked == true)
|
||||
selectedLogLevel = "Debug";
|
||||
else if (logLevelTrace?.IsChecked == true)
|
||||
selectedLogLevel = "Trace";
|
||||
else if (logLevelNormal?.IsChecked == true)
|
||||
selectedLogLevel = "Normal";
|
||||
|
||||
settings.MinLogLevel = selectedLogLevel;
|
||||
|
||||
Log($"[LOG] Livello log impostato: {selectedLogLevel}", LogLevel.Info);
|
||||
|
||||
// === SEZIONE DEFAULTS: Stati Iniziali Aste ===
|
||||
var loadAuctionsRemember = Settings.FindName("LoadAuctionsRemember") as System.Windows.Controls.RadioButton;
|
||||
var loadAuctionsActive = Settings.FindName("LoadAuctionsActive") as System.Windows.Controls.RadioButton;
|
||||
@@ -290,5 +279,43 @@ namespace AutoBidder
|
||||
MessageBox.Show(this, "Errore durante ripristino: " + ex.Message, "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
// === HANDLER PER PULSANTI UNIFICATI ===
|
||||
|
||||
private void SaveAllSettings_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Salva tutte le impostazioni (ora solo defaults, export rimosso)
|
||||
SaveDefaultsButton_Click(sender, e);
|
||||
|
||||
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 (Exception ex)
|
||||
{
|
||||
Log($"[ERRORE] Salvataggio impostazioni: {ex.Message}", LogLevel.Error);
|
||||
MessageBox.Show(this, "Errore durante salvataggio: " + ex.Message, "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void CancelAllSettings_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Annulla tutte le modifiche
|
||||
LoadDefaultSettings();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using AutoBidder.Models;
|
||||
using AutoBidder.ViewModels;
|
||||
using AutoBidder.Utilities;
|
||||
using AutoBidder.Services; // ✅ AGGIUNTO per RequestPriority e HtmlResponse
|
||||
using AutoBidder.Services; // ? AGGIUNTO per RequestPriority e HtmlResponse
|
||||
|
||||
namespace AutoBidder
|
||||
{
|
||||
@@ -28,10 +28,10 @@ namespace AutoBidder
|
||||
string? productName = null;
|
||||
string originalUrl;
|
||||
|
||||
// Verifica se è un URL o solo un ID
|
||||
// Verifica se è un URL o solo un ID
|
||||
if (input.Contains("bidoo.com") || input.Contains("http"))
|
||||
{
|
||||
// È un URL - estrai ID e nome prodotto dall'URL stesso
|
||||
// È un URL - estrai ID e nome prodotto dall'URL stesso
|
||||
originalUrl = input.Trim();
|
||||
auctionId = ExtractAuctionId(originalUrl);
|
||||
if (string.IsNullOrEmpty(auctionId))
|
||||
@@ -44,7 +44,7 @@ namespace AutoBidder
|
||||
}
|
||||
else
|
||||
{
|
||||
// È solo un ID numerico - costruisci URL generico
|
||||
// È solo un ID numerico - costruisci URL generico
|
||||
auctionId = input.Trim();
|
||||
originalUrl = $"https://it.bidoo.com/auction.php?a=asta_{auctionId}";
|
||||
}
|
||||
@@ -52,11 +52,11 @@ namespace AutoBidder
|
||||
// Verifica duplicati
|
||||
if (_auctionViewModels.Any(a => a.AuctionId == auctionId))
|
||||
{
|
||||
MessageBox.Show("Asta già monitorata!", "Duplicato", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
MessageBox.Show("Asta già monitorata!", "Duplicato", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ MODIFICATO: Nome senza ID (già nella colonna separata)
|
||||
// ? MODIFICATO: Nome senza ID (già nella colonna separata)
|
||||
var displayName = string.IsNullOrEmpty(productName)
|
||||
? $"Asta {auctionId}"
|
||||
: DecodeAllHtmlEntities(productName);
|
||||
@@ -64,7 +64,7 @@ namespace AutoBidder
|
||||
// CARICA IMPOSTAZIONI PREDEFINITE SALVATE
|
||||
var settings = Utilities.SettingsManager.Load();
|
||||
|
||||
// ✅ Determina stato iniziale dalla configurazione
|
||||
// ? Determina stato iniziale dalla configurazione
|
||||
bool isActive = false;
|
||||
bool isPaused = false;
|
||||
|
||||
@@ -109,7 +109,7 @@ namespace AutoBidder
|
||||
};
|
||||
_auctionViewModels.Add(vm);
|
||||
|
||||
// ✅ Auto-start del monitoraggio se l'asta è attiva e il monitoraggio è fermo
|
||||
// ? Auto-start del monitoraggio se l'asta è attiva e il monitoraggio è fermo
|
||||
if (isActive && !_isAutomationActive)
|
||||
{
|
||||
_auctionMonitor.Start();
|
||||
@@ -124,7 +124,7 @@ namespace AutoBidder
|
||||
var stateText = isActive ? (isPaused ? "Paused" : "Active") : "Stopped";
|
||||
Log($"[ADD] Asta aggiunta con stato={stateText}, Anticipo={settings.DefaultBidBeforeDeadlineMs}ms", Utilities.LogLevel.Info);
|
||||
|
||||
// ✅ NUOVO: Se il nome non è stato estratto, recuperalo in background DOPO l'aggiunta
|
||||
// ? NUOVO: Se il nome non è stato estratto, recuperalo in background DOPO l'aggiunta
|
||||
if (string.IsNullOrEmpty(productName))
|
||||
{
|
||||
_ = FetchAuctionNameInBackgroundAsync(auction, vm);
|
||||
@@ -144,7 +144,7 @@ namespace AutoBidder
|
||||
{
|
||||
try
|
||||
{
|
||||
// ✅ USA IL SERVIZIO CENTRALIZZATO invece di HttpClient diretto
|
||||
// ? USA IL SERVIZIO CENTRALIZZATO invece di HttpClient diretto
|
||||
var response = await _htmlCacheService.GetHtmlAsync(
|
||||
auction.OriginalUrl,
|
||||
RequestPriority.Normal,
|
||||
@@ -153,7 +153,7 @@ namespace AutoBidder
|
||||
|
||||
if (!response.Success)
|
||||
{
|
||||
Log($"[WARN] Impossibile recuperare nome per asta {auction.AuctionId}: {response.Error}", LogLevel.Warn);
|
||||
Log($"[WARN] Impossibile recuperare nome per asta {auction.AuctionId}: {response.Error}", LogLevel.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -163,9 +163,9 @@ namespace AutoBidder
|
||||
if (match.Success)
|
||||
{
|
||||
var productName = match.Groups[1].Value.Trim().Replace(" - Bidoo", "");
|
||||
// ✅ Decodifica entity HTML (incluse quelle non standard)
|
||||
// ? Decodifica entity HTML (incluse quelle non standard)
|
||||
productName = DecodeAllHtmlEntities(productName);
|
||||
// ✅ MODIFICATO: Nome senza ID
|
||||
// ? MODIFICATO: Nome senza ID
|
||||
var newName = productName;
|
||||
|
||||
// Aggiorna il nome su thread UI
|
||||
@@ -182,12 +182,12 @@ namespace AutoBidder
|
||||
}
|
||||
else
|
||||
{
|
||||
Log($"[WARN] Nome non trovato nell'HTML per asta {auction.AuctionId}", LogLevel.Warn);
|
||||
Log($"[WARN] Nome non trovato nell'HTML per asta {auction.AuctionId}", LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"[WARN] Errore recupero nome per asta {auction.AuctionId}: {ex.Message}", LogLevel.Warn);
|
||||
Log($"[WARN] Errore recupero nome per asta {auction.AuctionId}: {ex.Message}", LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,16 +202,16 @@ namespace AutoBidder
|
||||
// Prima decodifica entity standard
|
||||
var decoded = System.Net.WebUtility.HtmlDecode(text);
|
||||
|
||||
// ✅ Poi sostituisci entity non standard che WebUtility.HtmlDecode non gestisce
|
||||
// ? Poi sostituisci entity non standard che WebUtility.HtmlDecode non gestisce
|
||||
decoded = decoded.Replace("+", "+");
|
||||
decoded = decoded.Replace("=", "=");
|
||||
decoded = decoded.Replace("−", "-");
|
||||
decoded = decoded.Replace("×", "×");
|
||||
decoded = decoded.Replace("÷", "÷");
|
||||
decoded = decoded.Replace("×", "×");
|
||||
decoded = decoded.Replace("÷", "÷");
|
||||
decoded = decoded.Replace("%", "%");
|
||||
decoded = decoded.Replace("$", "$");
|
||||
decoded = decoded.Replace("€", "€");
|
||||
decoded = decoded.Replace("£", "£");
|
||||
decoded = decoded.Replace("€", "€");
|
||||
decoded = decoded.Replace("£", "£");
|
||||
|
||||
return decoded;
|
||||
}
|
||||
@@ -236,7 +236,7 @@ namespace AutoBidder
|
||||
// Verifica duplicati
|
||||
if (_auctionViewModels.Any(a => a.AuctionId == auctionId))
|
||||
{
|
||||
MessageBox.Show("Asta già monitorata!", "Duplicato", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
MessageBox.Show("Asta già monitorata!", "Duplicato", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -244,7 +244,7 @@ namespace AutoBidder
|
||||
var name = $"Asta {auctionId}";
|
||||
try
|
||||
{
|
||||
// ✅ USA IL SERVIZIO CENTRALIZZATO
|
||||
// ? USA IL SERVIZIO CENTRALIZZATO
|
||||
var response = await _htmlCacheService.GetHtmlAsync(url, RequestPriority.Normal);
|
||||
|
||||
if (response.Success)
|
||||
@@ -261,7 +261,7 @@ namespace AutoBidder
|
||||
// CARICA IMPOSTAZIONI PREDEFINITE SALVATE
|
||||
var settings = Utilities.SettingsManager.Load();
|
||||
|
||||
// ✅ Determina stato iniziale dalla configurazione
|
||||
// ? Determina stato iniziale dalla configurazione
|
||||
bool isActive = false;
|
||||
bool isPaused = false;
|
||||
|
||||
@@ -306,7 +306,7 @@ namespace AutoBidder
|
||||
};
|
||||
_auctionViewModels.Add(vm);
|
||||
|
||||
// ✅ Auto-start del monitoraggio se l'asta è attiva e il monitoraggio è fermo
|
||||
// ? Auto-start del monitoraggio se l'asta è attiva e il monitoraggio è fermo
|
||||
if (isActive && !_isAutomationActive)
|
||||
{
|
||||
_auctionMonitor.Start();
|
||||
@@ -353,12 +353,12 @@ namespace AutoBidder
|
||||
{
|
||||
try
|
||||
{
|
||||
// Aspetta 30 secondi prima di ritentare (dà tempo alle altre richieste di completare)
|
||||
// Aspetta 30 secondi prima di ritentare (dà tempo alle altre richieste di completare)
|
||||
await System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(30));
|
||||
|
||||
// Trova aste con nomi generici "Asta XXXX"
|
||||
var auctionsWithGenericNames = _auctionViewModels
|
||||
.Where(vm => vm.Name.StartsWith("Asta ") && !vm.Name.Contains("Shop") && !vm.Name.Contains("€"))
|
||||
.Where(vm => vm.Name.StartsWith("Asta ") && !vm.Name.Contains("Shop") && !vm.Name.Contains("€"))
|
||||
.ToList();
|
||||
|
||||
if (auctionsWithGenericNames.Count > 0)
|
||||
@@ -375,7 +375,7 @@ namespace AutoBidder
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"[WARN] Errore retry nomi aste: {ex.Message}", LogLevel.Warn);
|
||||
Log($"[WARN] Errore retry nomi aste: {ex.Message}", LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,7 +396,7 @@ namespace AutoBidder
|
||||
{
|
||||
try
|
||||
{
|
||||
// ✅ Carica impostazioni
|
||||
// ? Carica impostazioni
|
||||
var settings = Utilities.SettingsManager.Load();
|
||||
|
||||
// Ottieni username corrente dalla sessione per ripristinare IsMyBid
|
||||
@@ -409,10 +409,10 @@ namespace AutoBidder
|
||||
// Protezione: rimuovi eventuali BidHistory null
|
||||
auction.BidHistory = auction.BidHistory?.Where(b => b != null).ToList() ?? new System.Collections.Generic.List<BidHistory>();
|
||||
|
||||
// ✅ Decode HTML entities (incluse quelle non standard)
|
||||
// ? Decode HTML entities (incluse quelle non standard)
|
||||
try { auction.Name = DecodeAllHtmlEntities(auction.Name ?? string.Empty); } catch { }
|
||||
|
||||
// ✅ Ripristina IsMyBid per tutte le puntate in RecentBids
|
||||
// ? Ripristina IsMyBid per tutte le puntate in RecentBids
|
||||
if (auction.RecentBids != null && auction.RecentBids.Count > 0 && !string.IsNullOrEmpty(currentUsername))
|
||||
{
|
||||
foreach (var bid in auction.RecentBids)
|
||||
@@ -422,11 +422,11 @@ namespace AutoBidder
|
||||
}
|
||||
|
||||
|
||||
// ✅ NUOVO: Gestione stato in base a RememberAuctionStates
|
||||
// ? NUOVO: Gestione stato in base a RememberAuctionStates
|
||||
if (settings.RememberAuctionStates)
|
||||
{
|
||||
// MODO 1: Ripristina lo stato salvato di ogni asta (IsActive e IsPaused vengono dal file salvato)
|
||||
// Non serve fare nulla, lo stato è già quello salvato nel file
|
||||
// Non serve fare nulla, lo stato è già quello salvato nel file
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -455,7 +455,7 @@ namespace AutoBidder
|
||||
_auctionViewModels.Add(vm);
|
||||
}
|
||||
|
||||
// ✅ Avvia monitoraggio se ci sono aste in stato Active O Paused
|
||||
// ? Avvia monitoraggio se ci sono aste in stato Active O Paused
|
||||
bool hasActiveOrPausedAuctions = auctions.Any(a => a.IsActive);
|
||||
|
||||
if (hasActiveOrPausedAuctions && auctions.Count > 0)
|
||||
@@ -538,7 +538,7 @@ namespace AutoBidder
|
||||
// Aggiorna Valore (Compra Subito)
|
||||
if (auction.BuyNowPrice.HasValue)
|
||||
{
|
||||
AuctionMonitor.ProductBuyNowPriceText.Text = $"{auction.BuyNowPrice.Value:F2}€";
|
||||
AuctionMonitor.ProductBuyNowPriceText.Text = $"{auction.BuyNowPrice.Value:F2}€";
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -548,7 +548,7 @@ namespace AutoBidder
|
||||
// Aggiorna Spese di Spedizione
|
||||
if (auction.ShippingCost.HasValue)
|
||||
{
|
||||
AuctionMonitor.ProductShippingCostText.Text = $"{auction.ShippingCost.Value:F2}€";
|
||||
AuctionMonitor.ProductShippingCostText.Text = $"{auction.ShippingCost.Value:F2}€";
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -579,28 +579,28 @@ namespace AutoBidder
|
||||
{
|
||||
bool hasGenericName = auction.Name.StartsWith("Asta ") &&
|
||||
!auction.Name.Contains("Shop") &&
|
||||
!auction.Name.Contains("€") &&
|
||||
!auction.Name.Contains("€") &&
|
||||
!auction.Name.Contains("Buono") &&
|
||||
!auction.Name.Contains("Carburante");
|
||||
|
||||
Log($"[PRODUCT INFO] Caricamento automatico per: {auction.Name}{(hasGenericName ? " (+ nome generico)" : "")}", Utilities.LogLevel.Info);
|
||||
|
||||
// ✅ USA IL SERVIZIO CENTRALIZZATO
|
||||
// ? USA IL SERVIZIO CENTRALIZZATO
|
||||
var response = await _htmlCacheService.GetHtmlAsync(
|
||||
auction.OriginalUrl,
|
||||
RequestPriority.High, // Priorità alta per info prodotto
|
||||
RequestPriority.High, // Priorità alta per info prodotto
|
||||
bypassCache: false
|
||||
);
|
||||
|
||||
if (!response.Success)
|
||||
{
|
||||
Log($"[PRODUCT INFO] Errore caricamento: {response.Error}", Utilities.LogLevel.Warn);
|
||||
Log($"[PRODUCT INFO] Errore caricamento: {response.Error}", Utilities.LogLevel.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
bool updated = false;
|
||||
|
||||
// 1. ✅ Se nome generico, estrai nome reale dal <title>
|
||||
// 1. ? Se nome generico, estrai nome reale dal <title>
|
||||
if (hasGenericName)
|
||||
{
|
||||
var matchTitle = System.Text.RegularExpressions.Regex.Match(response.Html, @"<title>([^<]+)</title>");
|
||||
@@ -608,7 +608,7 @@ namespace AutoBidder
|
||||
{
|
||||
var productName = matchTitle.Groups[1].Value.Trim().Replace(" - Bidoo", "");
|
||||
productName = DecodeAllHtmlEntities(productName);
|
||||
// ✅ MODIFICATO: Nome senza ID
|
||||
// ? MODIFICATO: Nome senza ID
|
||||
var newName = productName;
|
||||
|
||||
auction.Name = newName;
|
||||
@@ -617,15 +617,15 @@ namespace AutoBidder
|
||||
}
|
||||
}
|
||||
|
||||
// 2. ✅ Estrai informazioni prodotto (prezzo, spedizione, limiti)
|
||||
// 2. ? Estrai informazioni prodotto (prezzo, spedizione, limiti)
|
||||
var extracted = Utilities.ProductValueCalculator.ExtractProductInfo(response.Html, auction);
|
||||
if (extracted)
|
||||
{
|
||||
updated = true;
|
||||
Log($"[PRODUCT INFO] Valore={auction.BuyNowPrice:F2}€, Spedizione={auction.ShippingCost:F2}€{(response.FromCache ? " (cached)" : "")}", Utilities.LogLevel.Success);
|
||||
Log($"[PRODUCT INFO] Valore={auction.BuyNowPrice:F2}€, Spedizione={auction.ShippingCost:F2}€{(response.FromCache ? " (cached)" : "")}", Utilities.LogLevel.Success);
|
||||
}
|
||||
|
||||
// 3. ✅ Salva e aggiorna UI solo se qualcosa è cambiato
|
||||
// 3. ? Salva e aggiorna UI solo se qualcosa è cambiato
|
||||
if (updated)
|
||||
{
|
||||
SaveAuctions();
|
||||
@@ -650,7 +650,7 @@ namespace AutoBidder
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"[PRODUCT INFO] Errore caricamento: {ex.Message}", Utilities.LogLevel.Warn);
|
||||
Log($"[PRODUCT INFO] Errore caricamento: {ex.Message}", Utilities.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
@@ -41,7 +41,7 @@ namespace AutoBidder
|
||||
Log("[START ALL] Tutte le aste avviate/riprese", LogLevel.Info);
|
||||
}
|
||||
|
||||
// ✅ Salva gli stati aggiornati su disco
|
||||
// ? Salva gli stati aggiornati su disco
|
||||
SaveAuctions();
|
||||
UpdateGlobalControlButtons();
|
||||
}
|
||||
@@ -69,13 +69,13 @@ namespace AutoBidder
|
||||
_isAutomationActive = false;
|
||||
}
|
||||
|
||||
// ✅ Salva gli stati aggiornati su disco
|
||||
// ? Salva gli stati aggiornati su disco
|
||||
SaveAuctions();
|
||||
UpdateGlobalControlButtons();
|
||||
|
||||
if (sender != null) // Solo se chiamato dall'utente
|
||||
{
|
||||
Log("[STOP ALL] Monitoraggio fermato e tutte le aste arrestate", LogLevel.Warn);
|
||||
Log("[STOP ALL] Monitoraggio fermato e tutte le aste arrestate", LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -93,13 +93,13 @@ namespace AutoBidder
|
||||
vm.IsPaused = true;
|
||||
}
|
||||
|
||||
// ✅ Salva gli stati aggiornati su disco
|
||||
// ? Salva gli stati aggiornati su disco
|
||||
SaveAuctions();
|
||||
UpdateGlobalControlButtons();
|
||||
|
||||
if (sender != null) // Solo se chiamato dall'utente
|
||||
{
|
||||
Log("[PAUSE ALL] Tutte le aste in pausa", LogLevel.Warn);
|
||||
Log("[PAUSE ALL] Tutte le aste in pausa", LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -166,7 +166,7 @@ namespace AutoBidder
|
||||
|
||||
MessageBox.Show(summary, "Aggiunta aste", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
|
||||
// ✅ RIMOSSO: Retry automatico ora avviene alla selezione on-demand
|
||||
// ? RIMOSSO: Retry automatico ora avviene alla selezione on-demand
|
||||
// Le aste con nome generico vengono aggiornate automaticamente quando l'utente le seleziona
|
||||
}
|
||||
}
|
||||
@@ -187,7 +187,7 @@ namespace AutoBidder
|
||||
|
||||
// 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.",
|
||||
$"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);
|
||||
@@ -213,10 +213,10 @@ namespace AutoBidder
|
||||
|
||||
Log($"[REMOVE] Asta rimossa: {auctionName} (ID: {auctionId})", LogLevel.Success);
|
||||
|
||||
// ✅ NUOVO: Sposta il focus sulla riga successiva
|
||||
// ? NUOVO: Sposta il focus sulla riga successiva
|
||||
if (_auctionViewModels.Count > 0)
|
||||
{
|
||||
// Se c'è ancora almeno un'asta nella lista
|
||||
// Se c'è ancora almeno un'asta nella lista
|
||||
int newIndex;
|
||||
|
||||
if (currentIndex >= _auctionViewModels.Count)
|
||||
@@ -234,7 +234,7 @@ namespace AutoBidder
|
||||
MultiAuctionsGrid.SelectedIndex = newIndex;
|
||||
_selectedAuction = _auctionViewModels[newIndex];
|
||||
|
||||
// ✅ FIX: Salva il nome della NUOVA asta selezionata per il log
|
||||
// ? FIX: Salva il nome della NUOVA asta selezionata per il log
|
||||
var newAuctionName = _selectedAuction?.Name ?? "Sconosciuta";
|
||||
|
||||
// Forza il focus sulla griglia dopo un breve delay per permettere alla UI di aggiornarsi
|
||||
@@ -248,7 +248,7 @@ namespace AutoBidder
|
||||
MultiAuctionsGrid.ScrollIntoView(MultiAuctionsGrid.SelectedItem);
|
||||
}
|
||||
|
||||
// ✅ FIX: Usa la variabile locale invece di _selectedAuction.Name
|
||||
// ? FIX: Usa la variabile locale invece di _selectedAuction.Name
|
||||
Log($"[FOCUS] Focus spostato su: {newAuctionName}", LogLevel.Info);
|
||||
}), System.Windows.Threading.DispatcherPriority.Background);
|
||||
}
|
||||
@@ -278,7 +278,7 @@ namespace AutoBidder
|
||||
|
||||
// Conferma rimozione
|
||||
var result = MessageBox.Show(
|
||||
$"Rimuovere TUTTE le aste dal monitoraggio?\n\nSono presenti {count} aste monitorate.\n\nTutte le aste verranno eliminate dalla lista e non saranno più monitorate.",
|
||||
$"Rimuovere TUTTE le aste dal monitoraggio?\n\nSono presenti {count} aste monitorate.\n\nTutte le aste verranno eliminate dalla lista e non saranno più monitorate.",
|
||||
"Conferma Rimozione Totale",
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Warning);
|
||||
@@ -368,7 +368,7 @@ namespace AutoBidder
|
||||
}
|
||||
|
||||
// Ultimo tentativo fallito
|
||||
Log($"[WARN] Clipboard temporaneamente occupato. Il testo potrebbe essere stato copiato.", LogLevel.Warn);
|
||||
Log($"[WARN] Clipboard temporaneamente occupato. Il testo potrebbe essere stato copiato.", LogLevel.Warning);
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -405,8 +405,8 @@ namespace AutoBidder
|
||||
}
|
||||
else
|
||||
{
|
||||
Log($"[WARN] Browser interno non ancora inizializzato", LogLevel.Warn);
|
||||
MessageBox.Show("Il browser interno non è ancora pronto.\nRiprova tra qualche secondo.", "Browser", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
Log($"[WARN] Browser interno non ancora inizializzato", LogLevel.Warning);
|
||||
MessageBox.Show("Il browser interno non è ancora pronto.\nRiprova tra qualche secondo.", "Browser", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -456,12 +456,12 @@ namespace AutoBidder
|
||||
try
|
||||
{
|
||||
MessageBox.Show(
|
||||
$"Esportazione singola asta:\n\n{_selectedAuction.Name}\n(ID: {_selectedAuction.AuctionId})\n\nFunzionalità in sviluppo.\nUsa 'Esporta' dalla toolbar per esportare tutte le aste.",
|
||||
$"Esportazione singola asta:\n\n{_selectedAuction.Name}\n(ID: {_selectedAuction.AuctionId})\n\nFunzionalità in sviluppo.\nUsa 'Esporta' dalla toolbar per esportare tutte le aste.",
|
||||
"Export Asta",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Information);
|
||||
|
||||
Log($"[INFO] Richiesto export singolo per asta: {_selectedAuction.Name} (funzionalità in sviluppo)", LogLevel.Info);
|
||||
Log($"[INFO] Richiesto export singolo per asta: {_selectedAuction.Name} (funzionalità in sviluppo)", LogLevel.Info);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -503,12 +503,12 @@ namespace AutoBidder
|
||||
double shippingCost = auction.ShippingCost ?? 0;
|
||||
double totalValue = buyNowPrice + shippingCost;
|
||||
|
||||
// Max EUR = 40% del valore TOTALE (più conservativo del 50%)
|
||||
// Max EUR = 40% del valore TOTALE (più conservativo del 50%)
|
||||
double suggestedMaxPrice = totalValue * 0.40;
|
||||
suggestedMaxPrice = Math.Round(suggestedMaxPrice, 2);
|
||||
|
||||
// CALCOLA MAX CLICKS (numero massimo puntate conservativo)
|
||||
// Formula: (Valore Totale - Max EUR) / 0.20€ per puntata
|
||||
// Formula: (Valore Totale - Max EUR) / 0.20€ per puntata
|
||||
// Poi riduciamo del 20% per maggiore margine di sicurezza
|
||||
int maxClicksTheoretical = (int)Math.Floor((totalValue - suggestedMaxPrice) / 0.20);
|
||||
int suggestedMaxClicks = (int)Math.Floor(maxClicksTheoretical * 0.80); // 80% del teorico
|
||||
@@ -516,12 +516,12 @@ namespace AutoBidder
|
||||
// Minimo 10 puntate per dare comunque una chance
|
||||
if (suggestedMaxClicks < 10) suggestedMaxClicks = 10;
|
||||
|
||||
Log($"[LIMITI] Valore={buyNowPrice:F2}€ + Extra={shippingCost:F2}€ = Tot={totalValue:F2}€ → MaxEUR={suggestedMaxPrice:F2}€ (40%), MaxClicks={suggestedMaxClicks}", LogLevel.Info);
|
||||
Log($"[LIMITI] Valore={buyNowPrice:F2}€ + Extra={shippingCost:F2}€ = Tot={totalValue:F2}€ ? MaxEUR={suggestedMaxPrice:F2}€ (40%), MaxClicks={suggestedMaxClicks}", LogLevel.Info);
|
||||
|
||||
// CHIEDI CONFERMA
|
||||
var result = MessageBox.Show(
|
||||
$"Limiti suggeriti (conservativi):\n\n" +
|
||||
$"Max EUR: {suggestedMaxPrice:F2}€\n" +
|
||||
$"Max EUR: {suggestedMaxPrice:F2}€\n" +
|
||||
$"Max Clicks: {suggestedMaxClicks}\n\n" +
|
||||
$"Applicare questi valori?",
|
||||
"Conferma Limiti",
|
||||
@@ -544,7 +544,7 @@ namespace AutoBidder
|
||||
// SALVA
|
||||
SaveAuctions();
|
||||
|
||||
Log($"[LIMITI] Applicati: MaxEUR={suggestedMaxPrice:F2}€, MaxClicks={suggestedMaxClicks}", LogLevel.Success);
|
||||
Log($"[LIMITI] Applicati: MaxEUR={suggestedMaxPrice:F2}€, MaxClicks={suggestedMaxClicks}", LogLevel.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -574,8 +574,8 @@ namespace AutoBidder
|
||||
|
||||
if (currentIndex <= 0)
|
||||
{
|
||||
// Già in cima o non trovata
|
||||
Log($"[MOVE] L'asta è già in cima alla lista", LogLevel.Info);
|
||||
// Già in cima o non trovata
|
||||
Log($"[MOVE] L'asta è già in cima alla lista", LogLevel.Info);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -615,8 +615,8 @@ namespace AutoBidder
|
||||
|
||||
if (currentIndex < 0 || currentIndex >= _auctionViewModels.Count - 1)
|
||||
{
|
||||
// Già in fondo o non trovata
|
||||
Log($"[MOVE] L'asta è già in fondo alla lista", LogLevel.Info);
|
||||
// Già in fondo o non trovata
|
||||
Log($"[MOVE] L'asta è già in fondo alla lista", LogLevel.Info);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -324,23 +324,6 @@ namespace AutoBidder
|
||||
|
||||
// ===== SETTINGS CONTROL EVENTS =====
|
||||
|
||||
// NOTA: Handler cookie RIMOSSI - gestione automatica tramite browser
|
||||
|
||||
private void Settings_ExportBrowseClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ExportBrowseButton_Click(sender, e);
|
||||
}
|
||||
|
||||
private void Settings_SaveSettingsClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
SaveSettingsButton_Click(sender, e);
|
||||
}
|
||||
|
||||
private void Settings_CancelSettingsClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
CancelSettingsButton_Click(sender, e);
|
||||
}
|
||||
|
||||
private void Settings_SaveDefaultsClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
SaveDefaultsButton_Click(sender, e);
|
||||
|
||||
@@ -6,27 +6,63 @@ using AutoBidder.Utilities;
|
||||
namespace AutoBidder
|
||||
{
|
||||
/// <summary>
|
||||
/// Logging functionality with color-coded severity levels
|
||||
/// Logging functionality with color-coded severity levels and configurable minimum level filtering
|
||||
/// </summary>
|
||||
public partial class MainWindow
|
||||
{
|
||||
/// <summary>
|
||||
/// Scrive un messaggio nel log globale con filtraggio basato sul livello minimo configurato
|
||||
/// </summary>
|
||||
/// <param name="message">Messaggio da loggare</param>
|
||||
/// <param name="level">Livello di severità del messaggio</param>
|
||||
private void Log(string message, LogLevel level = LogLevel.Info)
|
||||
{
|
||||
Dispatcher.BeginInvoke(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// Carica impostazioni per ottenere livello minimo e limite righe
|
||||
var settings = SettingsManager.Load();
|
||||
|
||||
// Filtra messaggi in base al livello minimo configurato
|
||||
MinimumLogLevel minLevel = MinimumLogLevel.Normal; // Default
|
||||
if (Enum.TryParse<MinimumLogLevel>(settings.MinLogLevel, out var parsedLevel))
|
||||
{
|
||||
minLevel = parsedLevel;
|
||||
}
|
||||
|
||||
// Se il livello del messaggio è maggiore del minimo configurato, ignora
|
||||
if ((int)level > (int)minLevel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var timestamp = DateTime.Now.ToString("HH:mm:ss");
|
||||
var logEntry = $"[{timestamp}] {message}";
|
||||
|
||||
// Prefisso in base al livello per chiarezza
|
||||
string prefix = level switch
|
||||
{
|
||||
LogLevel.Error => "[ERROR]",
|
||||
LogLevel.Warning => "[WARN]",
|
||||
LogLevel.Info => "[INFO]",
|
||||
LogLevel.Success => "[OK]",
|
||||
LogLevel.Debug => "[DEBUG]",
|
||||
LogLevel.Trace => "[TRACE]",
|
||||
_ => "[LOG]"
|
||||
};
|
||||
|
||||
var logEntry = $"[{timestamp}] {prefix} {message}";
|
||||
|
||||
// Color coding based on severity for dark theme
|
||||
var color = level switch
|
||||
{
|
||||
LogLevel.Error => new SolidColorBrush(Color.FromRgb(232, 17, 35)), // #E81123 (Red)
|
||||
LogLevel.Warn => new SolidColorBrush(Color.FromRgb(255, 183, 0)), // #FFB700 (Yellow/Orange)
|
||||
LogLevel.Success => new SolidColorBrush(Color.FromRgb(0, 216, 0)), // #00D800 (Green)
|
||||
LogLevel.Info => new SolidColorBrush(Color.FromRgb(100, 180, 255)), // #64B4FF (Light Blue - più chiaro e leggibile)
|
||||
_ => new SolidColorBrush(Color.FromRgb(204, 204, 204)) // #CCCCCC (Light Gray)
|
||||
LogLevel.Error => new SolidColorBrush(Color.FromRgb(232, 17, 35)), // #E81123 (Red)
|
||||
LogLevel.Warning => new SolidColorBrush(Color.FromRgb(255, 191, 0)), // #FFBF00 (Yellow)
|
||||
LogLevel.Success => new SolidColorBrush(Color.FromRgb(0, 216, 0)), // #00D800 (Green)
|
||||
LogLevel.Info => new SolidColorBrush(Color.FromRgb(100, 180, 255)), // #64B4FF (Light Blue)
|
||||
LogLevel.Debug => new SolidColorBrush(Color.FromRgb(255, 140, 255)), // #FF8CFF (Magenta)
|
||||
LogLevel.Trace => new SolidColorBrush(Color.FromRgb(160, 160, 160)), // #A0A0A0 (Gray)
|
||||
_ => new SolidColorBrush(Color.FromRgb(204, 204, 204)) // #CCCCCC (Light Gray)
|
||||
};
|
||||
|
||||
var p = new System.Windows.Documents.Paragraph { Margin = new Thickness(0, 2, 0, 2) };
|
||||
@@ -34,8 +70,7 @@ namespace AutoBidder
|
||||
p.Inlines.Add(r);
|
||||
LogBox.Document.Blocks.Add(p);
|
||||
|
||||
// ? Mantieni solo gli ultimi N paragrafi (configurabile dalle impostazioni)
|
||||
var settings = SettingsManager.Load();
|
||||
// Mantieni solo gli ultimi N paragrafi (configurabile dalle impostazioni)
|
||||
int maxLogLines = settings.MaxGlobalLogLines;
|
||||
|
||||
if (LogBox.Document.Blocks.Count > maxLogLines)
|
||||
|
||||
@@ -184,7 +184,7 @@ namespace AutoBidder
|
||||
else
|
||||
{
|
||||
SetUserBanner(string.Empty, 0);
|
||||
Log("[SESSION] Sessione scaduta", LogLevel.Warn);
|
||||
Log("[SESSION] Sessione scaduta", LogLevel.Warning);
|
||||
CheckBrowserCookieAfterWebViewReady();
|
||||
}
|
||||
});
|
||||
@@ -194,7 +194,7 @@ namespace AutoBidder
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
SetUserBanner(string.Empty, 0);
|
||||
Log($"[SESSION] Errore verifica sessione: {ex.Message}", LogLevel.Warn);
|
||||
Log($"[SESSION] Errore verifica sessione: {ex.Message}", LogLevel.Warning);
|
||||
CheckBrowserCookieAfterWebViewReady();
|
||||
});
|
||||
}
|
||||
@@ -231,7 +231,7 @@ namespace AutoBidder
|
||||
{
|
||||
await Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
Log("[WARN] WebView non inizializzata dopo 60 secondi", LogLevel.Warn);
|
||||
Log("[WARN] WebView non inizializzata dopo 60 secondi", LogLevel.Warning);
|
||||
Log("[INFO] Per accedere:", LogLevel.Info);
|
||||
Log("[INFO] 1. Click su 'Non connesso' nella sidebar", LogLevel.Info);
|
||||
Log("[INFO] 2. Si aprirà la scheda Browser", LogLevel.Info);
|
||||
@@ -263,7 +263,7 @@ namespace AutoBidder
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"[WARN] Errore verifica cookie: {ex.Message}", LogLevel.Warn);
|
||||
Log($"[WARN] Errore verifica cookie: {ex.Message}", LogLevel.Warning);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace AutoBidder
|
||||
{
|
||||
if (EmbeddedWebView == null)
|
||||
{
|
||||
Log("[WARN] WebView2 non disponibile", LogLevel.Warn);
|
||||
Log("[WARN] WebView2 non disponibile", LogLevel.Warning);
|
||||
_webViewInitCompletionSource?.TrySetResult(false);
|
||||
return;
|
||||
}
|
||||
@@ -160,7 +160,7 @@ namespace AutoBidder
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"[WARN] Verifica cookie fallita: {ex.Message}", LogLevel.Warn);
|
||||
Log($"[WARN] Verifica cookie fallita: {ex.Message}", LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ namespace AutoBidder
|
||||
|
||||
if (completedTask == timeoutTask)
|
||||
{
|
||||
Log("[WARN] Timeout attesa inizializzazione WebView2", LogLevel.Warn);
|
||||
Log("[WARN] Timeout attesa inizializzazione WebView2", LogLevel.Warning);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -275,7 +275,7 @@ namespace AutoBidder
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"[WARN] Impossibile estrarre cookie da WebView: {ex.Message}", LogLevel.Warn);
|
||||
Log($"[WARN] Impossibile estrarre cookie da WebView: {ex.Message}", LogLevel.Warning);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -289,7 +289,7 @@ namespace AutoBidder
|
||||
{
|
||||
if (!_isWebViewInitialized || EmbeddedWebView?.CoreWebView2 == null)
|
||||
{
|
||||
Log("[WARN] Browser non inizializzato - attendi qualche secondo e riprova", LogLevel.Warn);
|
||||
Log("[WARN] Browser non inizializzato - attendi qualche secondo e riprova", LogLevel.Warning);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -299,7 +299,7 @@ namespace AutoBidder
|
||||
|
||||
if (string.IsNullOrEmpty(cookieString))
|
||||
{
|
||||
Log("[WARN] Nessun cookie trovato nel browser - assicurati di aver effettuato il login su bidoo.com", LogLevel.Warn);
|
||||
Log("[WARN] Nessun cookie trovato nel browser - assicurati di aver effettuato il login su bidoo.com", LogLevel.Warning);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -334,9 +334,6 @@
|
||||
<!-- Settings Panel -->
|
||||
<controls:SettingsControl x:Name="Settings"
|
||||
Visibility="Collapsed"
|
||||
ExportBrowseClicked="Settings_ExportBrowseClicked"
|
||||
SaveSettingsClicked="Settings_SaveSettingsClicked"
|
||||
CancelSettingsClicked="Settings_CancelSettingsClicked"
|
||||
SaveDefaultsClicked="Settings_SaveDefaultsClicked"
|
||||
CancelDefaultsClicked="Settings_CancelDefaultsClicked"/>
|
||||
</Grid>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
@@ -19,7 +19,7 @@ namespace AutoBidder
|
||||
private readonly AuctionMonitor _auctionMonitor;
|
||||
private readonly System.Collections.ObjectModel.ObservableCollection<AuctionViewModel> _auctionViewModels = new System.Collections.ObjectModel.ObservableCollection<AuctionViewModel>();
|
||||
|
||||
// ✅ NUOVO: Servizio centralizzato per HTTP requests con cache e rate limiting
|
||||
// ? NUOVO: Servizio centralizzato per HTTP requests con cache e rate limiting
|
||||
private readonly HtmlCacheService _htmlCacheService;
|
||||
|
||||
// UI State
|
||||
@@ -69,19 +69,6 @@ namespace AutoBidder
|
||||
public TextBox BrowserAddress => Browser.BrowserAddress;
|
||||
|
||||
// Properties to access UserControl elements - Settings
|
||||
public TextBox ExportPathTextBox => Settings.ExportPathTextBox;
|
||||
public Button ExportBrowseButton => Settings.ExportBrowseButton;
|
||||
public RadioButton ExtCsv => Settings.ExtCsv;
|
||||
public RadioButton ExtJson => Settings.ExtJson;
|
||||
public RadioButton ExtXml => Settings.ExtXml;
|
||||
public CheckBox IncludeUsedBids => Settings.IncludeUsedBids;
|
||||
public CheckBox IncludeLogs => Settings.IncludeLogs;
|
||||
public CheckBox IncludeUserBids => Settings.IncludeUserBids;
|
||||
public CheckBox IncludeMetadata => Settings.IncludeMetadata;
|
||||
public CheckBox RemoveAfterExport => Settings.RemoveAfterExport;
|
||||
public CheckBox OverwriteExisting => Settings.OverwriteExisting;
|
||||
|
||||
// Impostazioni predefinite aste - AGGIORNATO
|
||||
public TextBox DefaultBidBeforeDeadlineMs => Settings.DefaultBidBeforeDeadlineMsTextBox;
|
||||
public CheckBox DefaultCheckAuctionOpen => Settings.DefaultCheckAuctionOpenCheckBox;
|
||||
public TextBox DefaultMinPrice => Settings.DefaultMinPriceTextBox;
|
||||
@@ -94,7 +81,7 @@ namespace AutoBidder
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// ✅ Inizializza HtmlCacheService con:
|
||||
// ? Inizializza HtmlCacheService con:
|
||||
// - Max 3 richieste concorrenti
|
||||
// - Max 5 richieste al secondo
|
||||
// - Cache di 5 minuti
|
||||
@@ -110,13 +97,13 @@ namespace AutoBidder
|
||||
// Inizializza servizi
|
||||
_auctionMonitor = new AuctionMonitor();
|
||||
|
||||
// ✅ NUOVO: Inizializza SessionService
|
||||
// ? NUOVO: Inizializza SessionService
|
||||
InitializeSessionService();
|
||||
|
||||
// Initialize commands (from MainWindow.Commands.cs)
|
||||
InitializeCommands();
|
||||
|
||||
// ✅ NUOVO: Inizializza validazione campi numerici
|
||||
// ? NUOVO: Inizializza validazione campi numerici
|
||||
InitializeNumericInputValidation();
|
||||
|
||||
this.DataContext = this;
|
||||
@@ -127,7 +114,7 @@ namespace AutoBidder
|
||||
_auctionMonitor.OnLog += AuctionMonitor_OnLog;
|
||||
_auctionMonitor.OnResetCountChanged += AuctionMonitor_OnResetCountChanged;
|
||||
|
||||
// ✅ NUOVO: Registra evento stato connessione
|
||||
// ? NUOVO: Registra evento stato connessione
|
||||
AuctionMonitor.ConnectionStatusClicked += AuctionMonitor_ConnectionStatusClicked;
|
||||
|
||||
// Bind griglia
|
||||
@@ -135,9 +122,6 @@ namespace AutoBidder
|
||||
|
||||
// Carica aste salvate (from MainWindow.AuctionManagement.cs)
|
||||
LoadSavedAuctions();
|
||||
|
||||
// Load export settings (from MainWindow.EventHandlers.Export.cs)
|
||||
LoadExportSettings();
|
||||
|
||||
// CARICA IMPOSTAZIONI PREDEFINITE ASTE
|
||||
LoadDefaultSettings();
|
||||
@@ -145,15 +129,45 @@ namespace AutoBidder
|
||||
// Update initial button states
|
||||
UpdateGlobalControlButtons();
|
||||
|
||||
Log("[OK] AutoBidder v4.0 avviato", LogLevel.Success);
|
||||
// === LOG AVVIO APPLICAZIONE ===
|
||||
Log("???????????????????????????????????????????????", LogLevel.Info);
|
||||
Log(" AutoBidder v4.0 - Multi-Auction Monitor", LogLevel.Info);
|
||||
Log("???????????????????????????????????????????????", LogLevel.Info);
|
||||
|
||||
var settings = SettingsManager.Load();
|
||||
Log($"Configurazione caricata:", LogLevel.Info);
|
||||
Log($" • Livello log: {settings.MinLogLevel}", LogLevel.Info);
|
||||
Log($" • Limite log globale: {settings.MaxGlobalLogLines} righe", LogLevel.Info);
|
||||
Log($" • Limite log per asta: {settings.MaxLogLinesPerAuction} righe", LogLevel.Info);
|
||||
Log($" • Puntate minime da mantenere: {(settings.MinimumRemainingBids > 0 ? settings.MinimumRemainingBids.ToString() : "nessun limite")}", LogLevel.Info);
|
||||
Log($" • Anticipo puntata default: {settings.DefaultBidBeforeDeadlineMs}ms", LogLevel.Info);
|
||||
|
||||
var auctionCount = _auctionViewModels.Count;
|
||||
if (auctionCount > 0)
|
||||
{
|
||||
Log($"Aste caricate: {auctionCount}", LogLevel.Success);
|
||||
var activeCount = _auctionViewModels.Count(a => a.IsActive && !a.IsPaused);
|
||||
var pausedCount = _auctionViewModels.Count(a => a.IsPaused);
|
||||
var stoppedCount = _auctionViewModels.Count(a => !a.IsActive && !a.IsPaused);
|
||||
if (activeCount > 0) Log($" • Attive: {activeCount}", LogLevel.Info);
|
||||
if (pausedCount > 0) Log($" • In pausa: {pausedCount}", LogLevel.Info);
|
||||
if (stoppedCount > 0) Log($" • Fermate: {stoppedCount}", LogLevel.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("Nessuna asta caricata", LogLevel.Info);
|
||||
}
|
||||
|
||||
Log("[OK] Applicazione pronta", LogLevel.Success);
|
||||
Log("???????????????????????????????????????????????", LogLevel.Info);
|
||||
|
||||
// Initialize user info timers (from MainWindow.UserInfo.cs)
|
||||
InitializeUserInfoTimers();
|
||||
|
||||
// ✅ NUOVO: Carica sessione salvata
|
||||
// ? NUOVO: Carica sessione salvata
|
||||
LoadSavedSession();
|
||||
|
||||
// ✅ NUOVO: Pre-carica WebView2 in background per renderla subito disponibile
|
||||
// ? NUOVO: Pre-carica WebView2 in background per renderla subito disponibile
|
||||
InitializeWebView2();
|
||||
|
||||
// Attach WebView2 context menu handler
|
||||
@@ -176,7 +190,7 @@ namespace AutoBidder
|
||||
}
|
||||
catch { }
|
||||
|
||||
// ✅ Timer per pulizia cache periodica (ogni 10 minuti)
|
||||
// ? Timer per pulizia cache periodica (ogni 10 minuti)
|
||||
var cacheCleanupTimer = new System.Windows.Threading.DispatcherTimer
|
||||
{
|
||||
Interval = TimeSpan.FromMinutes(10)
|
||||
@@ -222,7 +236,7 @@ namespace AutoBidder
|
||||
{
|
||||
vm.UpdateState(state);
|
||||
|
||||
// ✅ NUOVO: Aggiorna storia puntate
|
||||
// ? NUOVO: Aggiorna storia puntate
|
||||
vm.RefreshBidHistory();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@@ -12,7 +12,7 @@ namespace AutoBidder.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Servizio completo API Bidoo (polling, puntate, info utente)
|
||||
/// Solo HTTP, nessuna modalità, browser o multi-click
|
||||
/// Solo HTTP, nessuna modalità, browser o multi-click
|
||||
/// </summary>
|
||||
public class BidooApiClient
|
||||
{
|
||||
@@ -90,7 +90,7 @@ namespace AutoBidder.Services
|
||||
if (!string.IsNullOrWhiteSpace(_session.CookieString))
|
||||
{
|
||||
request.Headers.Add("Cookie", _session.CookieString);
|
||||
// Log rimosso per ridurre verbosità
|
||||
// Log rimosso per ridurre verbosità
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -130,13 +130,13 @@ namespace AutoBidder.Services
|
||||
request.Headers.Add("Referer", "https://it.bidoo.com/");
|
||||
}
|
||||
|
||||
// Log rimosso per ridurre verbosità - headers sempre aggiunti
|
||||
// Log rimosso per ridurre verbosità - headers sempre aggiunti
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Estrae CSRF/Bid token dalla pagina asta
|
||||
/// PASSO 1: Ottenere la pagina HTML dell'asta per estrarre il token di sicurezza
|
||||
/// Il token può essere chiamato: bid_token, csrf_token, _token, etc.
|
||||
/// Il token può essere chiamato: bid_token, csrf_token, _token, etc.
|
||||
/// </summary>
|
||||
private async Task<(string? tokenName, string? tokenValue)> ExtractBidTokenAsync(string auctionId, string? auctionUrl = null)
|
||||
{
|
||||
@@ -187,12 +187,12 @@ namespace AutoBidder.Services
|
||||
if (match.Success)
|
||||
{
|
||||
var tokenValue = match.Groups[1].Value;
|
||||
Log($"[TOKEN] ✓ Token found: {pattern.name} = {tokenValue.Substring(0, Math.Min(20, tokenValue.Length))}...", auctionId);
|
||||
Log($"[TOKEN] ? Token found: {pattern.name} = {tokenValue.Substring(0, Math.Min(20, tokenValue.Length))}...", auctionId);
|
||||
return (pattern.name, tokenValue);
|
||||
}
|
||||
}
|
||||
|
||||
Log("[TOKEN] ⚠ No bid token found in HTML", auctionId);
|
||||
Log("[TOKEN] ? No bid token found in HTML", auctionId);
|
||||
return (null, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -309,7 +309,7 @@ namespace AutoBidder.Services
|
||||
state.IsMyBid = !string.IsNullOrEmpty(_session.Username) &&
|
||||
state.LastBidder.Equals(_session.Username, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
// ✅ NUOVO: Parse storia puntate
|
||||
// ? NUOVO: Parse storia puntate
|
||||
// Formato: 42;fedekikka2323;3,42;fedekikka2323;1764068204;3|41;chamorro1984;1764068194;3|...
|
||||
if (!string.IsNullOrEmpty(historyData))
|
||||
{
|
||||
@@ -317,7 +317,8 @@ namespace AutoBidder.Services
|
||||
}
|
||||
|
||||
state.ParsingSuccess = true;
|
||||
Log($"[PARSE SUCCESS] Timer: {state.Timer:F2}s, Price: €{state.Price:F2}, Bidder: {state.LastBidder}, Status: {state.Status}, History: {state.RecentBidsHistory?.Count ?? 0} bids", auctionId);
|
||||
// Log di successo rimosso - troppo verboso. Solo errori vengono loggati nel log asta.
|
||||
// Per debug dettagliato, usare livello log "Debug" o "Trace" nelle impostazioni.
|
||||
return state;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -337,14 +338,14 @@ namespace AutoBidder.Services
|
||||
{
|
||||
var entries = new List<BidHistoryEntry>();
|
||||
|
||||
// Il primo record è spesso il prezzo corrente con dati duplicati, lo saltiamo
|
||||
// Il primo record è spesso il prezzo corrente con dati duplicati, lo saltiamo
|
||||
var records = historyData.Split('|');
|
||||
|
||||
// Parsing prezzo corrente per calcolare i prezzi precedenti
|
||||
if (!int.TryParse(currentPriceStr, out var currentPriceIndex))
|
||||
return null;
|
||||
|
||||
// 📊 NUOVO: Carica impostazione limite visualizzazione puntate
|
||||
// ?? NUOVO: Carica impostazione limite visualizzazione puntate
|
||||
var settings = Utilities.SettingsManager.Load();
|
||||
var maxEntries = settings?.MaxBidHistoryEntries ?? 20; // Default 20 se non impostato
|
||||
|
||||
@@ -353,7 +354,7 @@ namespace AutoBidder.Services
|
||||
if (string.IsNullOrWhiteSpace(record))
|
||||
continue;
|
||||
|
||||
// 📊 Limita il numero di puntate basandosi sulle impostazioni
|
||||
// ?? Limita il numero di puntate basandosi sulle impostazioni
|
||||
if (maxEntries > 0 && entries.Count >= maxEntries)
|
||||
break;
|
||||
|
||||
@@ -523,7 +524,7 @@ namespace AutoBidder.Services
|
||||
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}");
|
||||
Log($"[USER INFO PARSED] Shop credit: €{credit:F2}");
|
||||
foundData = true;
|
||||
}
|
||||
|
||||
@@ -656,15 +657,15 @@ namespace AutoBidder.Services
|
||||
/// STATI API BIDOO:
|
||||
/// - ON: Asta attiva e in corso
|
||||
/// - OFF: Asta terminata definitivamente
|
||||
/// - STOP: Asta in pausa (tipicamente 00:00-10:00) - riprenderà più tardi
|
||||
/// - STOP: Asta in pausa (tipicamente 00:00-10:00) - riprenderà più tardi
|
||||
/// </summary>
|
||||
private AuctionStatus DetermineAuctionStatus(string apiStatus, bool hasWinner, bool iAmWinner, ref AuctionState state)
|
||||
{
|
||||
// Gestione stato STOP (pausa notturna)
|
||||
if (apiStatus == "STOP")
|
||||
{
|
||||
// L'asta è iniziata ma è in pausa
|
||||
// Controlla se c'è già un vincitore temporaneo
|
||||
// L'asta è iniziata ma è in pausa
|
||||
// Controlla se c'è già un vincitore temporaneo
|
||||
if (hasWinner)
|
||||
{
|
||||
state.LastBidder = state.LastBidder; // Mantieni il last bidder
|
||||
@@ -689,12 +690,12 @@ namespace AutoBidder.Services
|
||||
// Asta attiva
|
||||
if (hasWinner)
|
||||
{
|
||||
// Ci sono già puntate → Running
|
||||
// Ci sono già puntate ? Running
|
||||
return AuctionStatus.Running;
|
||||
}
|
||||
|
||||
// Nessuna puntata ancora → Pending o Scheduled
|
||||
// Se timer molto alto (> 30 minuti), è programmata per più tardi
|
||||
// Nessuna puntata ancora ? Pending o Scheduled
|
||||
// Se timer molto alto (> 30 minuti), è programmata per più tardi
|
||||
if (state.Timer > 1800) // 30 minuti
|
||||
{
|
||||
return AuctionStatus.Scheduled;
|
||||
@@ -899,7 +900,7 @@ namespace AutoBidder.Services
|
||||
Log($"[USER HTML ERROR] Puntate residue NON trovate nell'HTML");
|
||||
}
|
||||
|
||||
// Ritorna dati solo se almeno username è stato trovato
|
||||
// Ritorna dati solo se almeno username è stato trovato
|
||||
if (foundUsername)
|
||||
{
|
||||
Log($"[USER HTML SUCCESS] Dati estratti: {userData.Username}, {userData.RemainingBids} puntate");
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using AutoBidder.Models;
|
||||
|
||||
namespace AutoBidder.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Esporta statistiche aste in formato CSV
|
||||
/// </summary>
|
||||
public static class CsvExporter
|
||||
{
|
||||
/// <summary>
|
||||
/// Esporta cronologia completa di un'asta in CSV
|
||||
/// </summary>
|
||||
public static void ExportAuctionHistory(AuctionInfo auction, string filePath)
|
||||
{
|
||||
var csv = new StringBuilder();
|
||||
|
||||
// Header
|
||||
csv.AppendLine("Timestamp,Event Type,Bidder,Price,Timer,Latency (ms),Success,Notes");
|
||||
|
||||
// Data
|
||||
foreach (var entry in auction.BidHistory.OrderBy(h => h.Timestamp))
|
||||
{
|
||||
csv.AppendLine($"{entry.Timestamp:yyyy-MM-dd HH:mm:ss.fff}," +
|
||||
$"{entry.EventType}," +
|
||||
$"\"{entry.Bidder}\"," +
|
||||
$"{entry.Price:F2}," +
|
||||
$"{entry.Timer:F2}," +
|
||||
$"{entry.LatencyMs}," +
|
||||
$"{entry.Success}," +
|
||||
$"\"{entry.Notes}\"");
|
||||
}
|
||||
|
||||
File.WriteAllText(filePath, csv.ToString(), Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Esporta statistiche aggregate di un'asta in CSV
|
||||
/// </summary>
|
||||
public static void ExportAuctionStatistics(AuctionInfo auction, string filePath)
|
||||
{
|
||||
var stats = AuctionStatistics.Calculate(auction);
|
||||
var csv = new StringBuilder();
|
||||
|
||||
// Informazioni asta
|
||||
csv.AppendLine("=== AUCTION INFO ===");
|
||||
csv.AppendLine($"Auction ID,{stats.AuctionId}");
|
||||
csv.AppendLine($"Name,\"{stats.Name}\"");
|
||||
csv.AppendLine($"Monitoring Started,{stats.MonitoringStarted:yyyy-MM-dd HH:mm:ss}");
|
||||
csv.AppendLine($"Monitoring Duration,{stats.MonitoringDuration}");
|
||||
csv.AppendLine();
|
||||
|
||||
// Contatori
|
||||
csv.AppendLine("=== COUNTERS ===");
|
||||
csv.AppendLine($"Total Bids,{stats.TotalBids}");
|
||||
csv.AppendLine($"My Bids,{stats.MyBids}");
|
||||
csv.AppendLine($"Opponent Bids,{stats.OpponentBids}");
|
||||
csv.AppendLine($"Resets,{stats.Resets}");
|
||||
csv.AppendLine($"Unique Bidders,{stats.UniqueBidders}");
|
||||
csv.AppendLine();
|
||||
|
||||
// Prezzi
|
||||
csv.AppendLine("=== PRICES ===");
|
||||
csv.AppendLine($"Start Price,{stats.StartPrice:F2}");
|
||||
csv.AppendLine($"Current Price,{stats.CurrentPrice:F2}");
|
||||
csv.AppendLine($"Min Price,{stats.MinPrice:F2}");
|
||||
csv.AppendLine($"Max Price,{stats.MaxPrice:F2}");
|
||||
csv.AppendLine($"Avg Price,{stats.AvgPrice:F2}");
|
||||
csv.AppendLine();
|
||||
|
||||
// Performance
|
||||
csv.AppendLine("=== PERFORMANCE ===");
|
||||
csv.AppendLine($"Avg Click Latency (ms),{stats.AvgClickLatencyMs}");
|
||||
csv.AppendLine($"Min Click Latency (ms),{stats.MinClickLatencyMs}");
|
||||
csv.AppendLine($"Max Click Latency (ms),{stats.MaxClickLatencyMs}");
|
||||
csv.AppendLine($"Bids Per Minute,{stats.BidsPerMinute:F2}");
|
||||
csv.AppendLine($"Resets Per Hour,{stats.ResetsPerHour:F2}");
|
||||
csv.AppendLine($"My Bid Success Rate,{stats.MyBidSuccessRate:F1}%");
|
||||
csv.AppendLine();
|
||||
|
||||
// Competitor ranking
|
||||
csv.AppendLine("=== BIDDER RANKING ===");
|
||||
csv.AppendLine("Bidder,Bids Count");
|
||||
foreach (var bidder in stats.BidderRanking.OrderByDescending(b => b.Value))
|
||||
{
|
||||
csv.AppendLine($"\"{bidder.Key}\",{bidder.Value}");
|
||||
}
|
||||
|
||||
File.WriteAllText(filePath, csv.ToString(), Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Esporta tutte le aste in un unico CSV
|
||||
/// </summary>
|
||||
public static void ExportAllAuctions(IEnumerable<AuctionInfo> auctions, string filePath)
|
||||
{
|
||||
var csv = new StringBuilder();
|
||||
|
||||
// Header AGGIORNATO
|
||||
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
|
||||
foreach (var auction in auctions)
|
||||
{
|
||||
var totalBidders = auction.BidderStats?.Count ?? 0;
|
||||
var myBids = auction.BidHistory.Count(h => h.EventType == BidEventType.MyBid);
|
||||
|
||||
csv.AppendLine($"{auction.AuctionId}," +
|
||||
$"\"{auction.Name}\"," +
|
||||
$"{auction.BidBeforeDeadlineMs}," +
|
||||
$"{auction.CheckAuctionOpenBeforeBid}," +
|
||||
$"{auction.MinPrice:F2}," +
|
||||
$"{auction.MaxPrice:F2}," +
|
||||
$"{myBids}," +
|
||||
$"{auction.ResetCount}," +
|
||||
$"{totalBidders}," +
|
||||
$"{auction.IsActive}," +
|
||||
$"{auction.IsPaused}," +
|
||||
$"{auction.AddedAt:yyyy-MM-dd HH:mm:ss}," +
|
||||
$"{(auction.LastClickAt?.ToString("yyyy-MM-dd HH:mm:ss") ?? "")}");
|
||||
}
|
||||
|
||||
File.WriteAllText(filePath, csv.ToString(), Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace AutoBidder.Utilities
|
||||
{
|
||||
internal static class ExportPreferences
|
||||
{
|
||||
private class Prefs { public string? LastExportExt { get; set; } }
|
||||
private static readonly string _folder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "AutoBidder");
|
||||
private static readonly string _file = Path.Combine(_folder, "exportprefs.json");
|
||||
|
||||
public static string? LoadLastExportExtension()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(_file)) return null;
|
||||
var txt = File.ReadAllText(_file);
|
||||
var p = JsonSerializer.Deserialize<Prefs>(txt);
|
||||
if (p == null || string.IsNullOrEmpty(p.LastExportExt)) return null;
|
||||
return p.LastExportExt;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SaveLastExportExtension(string? ext)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(_folder)) Directory.CreateDirectory(_folder);
|
||||
var p = new Prefs { LastExportExt = ext };
|
||||
var txt = JsonSerializer.Serialize(p);
|
||||
File.WriteAllText(_file, txt);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,82 @@
|
||||
namespace AutoBidder.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Livelli di log per differenziare la verbosità dei messaggi.
|
||||
/// Permette di configurare il dettaglio desiderato tra uso normale e debug.
|
||||
/// </summary>
|
||||
internal enum LogLevel
|
||||
{
|
||||
Info,
|
||||
Success,
|
||||
Warn,
|
||||
Error
|
||||
{
|
||||
/// <summary>
|
||||
/// Errori critici che impediscono il funzionamento dell'applicazione o di una funzionalità.
|
||||
/// Sempre visibile. Esempi: errori di connessione, parsing fallito, eccezioni non gestite.
|
||||
/// </summary>
|
||||
Error = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Avvisi per situazioni anomale ma non critiche.
|
||||
/// Sempre visibile. Esempi: parametri mancanti, valori inaspettati, retry in corso.
|
||||
/// </summary>
|
||||
Warning = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Informazioni importanti sull'operatività dell'applicazione.
|
||||
/// Visibile in modalità normale e debug. Esempi: asta aggiunta, stato cambiato, operazioni completate.
|
||||
/// </summary>
|
||||
Info = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Conferme di operazioni riuscite con successo.
|
||||
/// Visibile in modalità normale e debug. Esempi: puntata effettuata, login riuscito, salvataggio completato.
|
||||
/// </summary>
|
||||
Success = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Informazioni dettagliate per il debug dell'applicazione.
|
||||
/// Visibile solo in modalità debug. Esempi: parametri chiamate API, valori variabili, flusso logico.
|
||||
/// </summary>
|
||||
Debug = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Tracciamento estremamente dettagliato di ogni operazione.
|
||||
/// Visibile solo in modalità trace. Esempi: ogni singola chiamata, timestamp precisi, dati raw HTTP.
|
||||
/// </summary>
|
||||
Trace = 5
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Livello minimo di log da mostrare all'utente.
|
||||
/// Controlla quali messaggi vengono effettivamente visualizzati nel log.
|
||||
/// </summary>
|
||||
internal enum MinimumLogLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Solo errori critici (uso giornaliero minimo).
|
||||
/// Mostra: Error
|
||||
/// </summary>
|
||||
ErrorOnly = LogLevel.Error,
|
||||
|
||||
/// <summary>
|
||||
/// Errori e avvisi (uso giornaliero normale).
|
||||
/// Mostra: Error, Warning
|
||||
/// </summary>
|
||||
Normal = LogLevel.Warning,
|
||||
|
||||
/// <summary>
|
||||
/// Informazioni operative standard (uso giornaliero dettagliato).
|
||||
/// Mostra: Error, Warning, Info, Success
|
||||
/// </summary>
|
||||
Informational = LogLevel.Success,
|
||||
|
||||
/// <summary>
|
||||
/// Informazioni dettagliate per debug (sviluppo applicazione).
|
||||
/// Mostra: Error, Warning, Info, Success, Debug
|
||||
/// </summary>
|
||||
Debug = LogLevel.Debug,
|
||||
|
||||
/// <summary>
|
||||
/// Tracciamento completo di ogni operazione (debug avanzato).
|
||||
/// Mostra: Error, Warning, Info, Success, Debug, Trace
|
||||
/// </summary>
|
||||
Trace = LogLevel.Trace
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,21 +6,6 @@ namespace AutoBidder.Utilities
|
||||
{
|
||||
internal class AppSettings
|
||||
{
|
||||
public string? ExportPath { get; set; }
|
||||
public string? LastExportExt { get; set; }
|
||||
public string ExportScope { get; set; } = "All"; // All, Closed, Unknown
|
||||
public bool IncludeOnlyUsedBids { get; set; } = true;
|
||||
public bool IncludeLogs { get; set; } = false;
|
||||
public bool IncludeUserBids { get; set; } = false;
|
||||
|
||||
// Added properties to match MainWindow expectations
|
||||
public bool ExportOpen { get; set; } = true;
|
||||
public bool ExportClosed { get; set; } = true;
|
||||
public bool ExportUnknown { get; set; } = true;
|
||||
public bool IncludeMetadata { get; set; } = true;
|
||||
public bool RemoveAfterExport { 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;
|
||||
@@ -74,6 +59,15 @@ namespace AutoBidder.Utilities
|
||||
/// Default: 20 (ultime 20 puntate)
|
||||
/// </summary>
|
||||
public int MaxBidHistoryEntries { get; set; } = 20;
|
||||
|
||||
// ?? NUOVO: LIVELLO MINIMO LOG
|
||||
/// <summary>
|
||||
/// Livello minimo di log da visualizzare.
|
||||
/// Valori: "ErrorOnly" (solo errori), "Normal" (errori e warning),
|
||||
/// "Informational" (info standard), "Debug" (dettagli sviluppo), "Trace" (tutto).
|
||||
/// Default: "Normal" (uso giornaliero - errori e warning)
|
||||
/// </summary>
|
||||
public string MinLogLevel { get; set; } = "Normal";
|
||||
}
|
||||
|
||||
internal static class SettingsManager
|
||||
|
||||
Reference in New Issue
Block a user