From 5298a97c18f4114daf6b159e321d163df0bbb0b5 Mon Sep 17 00:00:00 2001 From: Lynnesbian Date: Sun, 24 Oct 2021 03:07:24 +1000 Subject: [PATCH] code cleanup, replace WebClient w/ HttpClient --- AddItemDialogue.cs | 6 +- MainWindow.cs | 201 +++++++++++++++++++++---------------------- Settings.cs | 12 +-- SettingsWindow.cs | 17 ++-- YahooAuctionsItem.cs | 58 +++++++------ buypeeb.csproj | 32 +++---- 6 files changed, 162 insertions(+), 164 deletions(-) diff --git a/AddItemDialogue.cs b/AddItemDialogue.cs index e7a0ca1..c3024b4 100644 --- a/AddItemDialogue.cs +++ b/AddItemDialogue.cs @@ -2,9 +2,6 @@ using Gtk; namespace Buypeeb { internal class AddItemDialogue : Dialog { - public Entry entryURL { get; } - public Entry entryName { get; } - public AddItemDialogue() : this(new Builder("add.glade")) { } private AddItemDialogue(Builder builder) : base(builder.GetObject("DialogueAdd").Handle) { @@ -15,6 +12,9 @@ namespace Buypeeb { DeleteEvent += Window_Shutdown; } + public Entry entryURL { get; } + public Entry entryName { get; } + private static void Window_Shutdown(object sender, DeleteEventArgs args) { Application.Quit(); } diff --git a/MainWindow.cs b/MainWindow.cs index 0a83eaa..93bc5d5 100755 --- a/MainWindow.cs +++ b/MainWindow.cs @@ -24,6 +24,7 @@ using System.Globalization; using System.IO; using System.Linq; using System.Net; +using System.Net.Http; using System.Text.Encodings.Web; using System.Text.Json; using System.Text.RegularExpressions; @@ -39,65 +40,28 @@ using Timeout = GLib.Timeout; namespace Buypeeb { [SuppressMessage("ReSharper", "UnusedMember.Local")] internal class MainWindow : Window { - private readonly string location; - private readonly JsonSerializerOptions jsonOptions; + // ...to here. + + private static readonly SemaphoreSlim TaskLimit = new(6); + private readonly Builder builder; + private readonly Label endingLabel; + private readonly Dictionary filterChecks = new(); + private readonly HttpClient httpClient; private readonly ListStore items; - private Settings settings; private readonly TreeView itemTreeView; - private readonly Builder builder; + private readonly JsonSerializerOptions jsonOptions; + private readonly string location; + private readonly SearchEntry searchEntry; // TODO: whenever we get something from the builder, cache it for later // that way we don't need to constantly do "builder.GetObject"s // when that is done, you can use the cache array to replace everything from here... private readonly Box selectionViewBox; - private readonly Label endingLabel; + private readonly Queue updateQueue = new(); private bool queueActive; - private readonly SearchEntry searchEntry; - private readonly Dictionary filterChecks = new Dictionary(); - - // ...to here. - - private static readonly SemaphoreSlim TaskLimit = new SemaphoreSlim(6); - private readonly Queue updateQueue = new Queue(); - - private IEnumerable filterQuery => - // father forgive me for i have lynned - from item in settings.watchlist.Values.ToList() - where (item.favourite != filterChecks["Favourites"].Active || - item.favourite == filterChecks["NonFavourites"].Active) && - (item.Available != filterChecks["Active"].Active || - item.Available == filterChecks["Ended"].Active) && - (item.endingToday != filterChecks["EndingToday"].Active || - item.endingToday == filterChecks["EndingAfterToday"].Active) && - (item.hasWinPrice != filterChecks["WithWinPrice"].Active || - item.hasWinPrice == filterChecks["WithNoWinPrice"].Active) && - (string.IsNullOrWhiteSpace(searchEntry.Text) || - item.name.ToLower().Contains(searchEntry.Text.ToLower()) || - item.originalName.ToLower().Contains(searchEntry.Text.ToLower())) - select item; - - private IEnumerable outdatedItemQuery => - // only returns items that meet all of the following: - // - marked as "ready", as in, they aren't in the process of updating - // - not updated since the interval - // - hasn't already ended - from item in settings.watchlist.Values.ToList() - where item.Ready && settings.ItemNotUpdatedSinceInterval(item) && item.endDate.CompareTo(DateTime.UtcNow) > 0 - select item; - - private YahooAuctionsItem selectedItem { - get { - if (itemTreeView.Selection.CountSelectedRows() == 0) { - // avoids incurring the wrath of Gtk-CRITICAL ** - return null; - } - - itemTreeView.Selection.GetSelected(out var iter); - return (YahooAuctionsItem) itemTreeView.Model.GetValue(iter, 0); - } - } + private Settings settings; public MainWindow() : this(new Builder("main.glade")) { } @@ -108,16 +72,20 @@ namespace Buypeeb { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All), }; - if (Environment.OSVersion.Platform == PlatformID.Win32NT) { - // C:\Users\Beebus\AppData\Roaming\Lynnear Software\buypeeb + if (Environment.OSVersion.Platform == + PlatformID.Win32NT) // C:\Users\Beebus\AppData\Roaming\Lynnear Software\buypeeb + { location = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Lynnear Software", "buypeeb"); - } else { - // ~/.config/Lynnear Software/buypeeb + } else // ~/.config/Lynnear Software/buypeeb + { location = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config", "Lynnear Software", "buypeeb"); } + // initialise http client + httpClient = new HttpClient(); + var userdata = System.IO.Path.Combine(location, "userdata.json"); if (File.Exists(userdata)) { try { @@ -188,6 +156,43 @@ namespace Buypeeb { DeleteEvent += WindowShutdown; } + private IEnumerable filterQuery => + // father forgive me for i have lynned + from item in settings.watchlist.Values.ToList() + where (item.favourite != filterChecks["Favourites"].Active || + item.favourite == filterChecks["NonFavourites"].Active) && + (item.Available != filterChecks["Active"].Active || + item.Available == filterChecks["Ended"].Active) && + (item.endingToday != filterChecks["EndingToday"].Active || + item.endingToday == filterChecks["EndingAfterToday"].Active) && + (item.hasWinPrice != filterChecks["WithWinPrice"].Active || + item.hasWinPrice == filterChecks["WithNoWinPrice"].Active) && + (string.IsNullOrWhiteSpace(searchEntry.Text) || + item.name.ToLower().Contains(searchEntry.Text.ToLower()) || + item.originalName.ToLower().Contains(searchEntry.Text.ToLower())) + select item; + + private IEnumerable outdatedItemQuery => + // only returns items that meet all of the following: + // - marked as "ready", as in, they aren't in the process of updating + // - not updated since the interval + // - hasn't already ended + from item in settings.watchlist.Values.ToList() + where item.Ready && settings.ItemNotUpdatedSinceInterval(item) && item.endDate.CompareTo(DateTime.UtcNow) > 0 + select item; + + private YahooAuctionsItem selectedItem { + get { + if (itemTreeView.Selection.CountSelectedRows() == 0) // avoids incurring the wrath of Gtk-CRITICAL ** + { + return null; + } + + itemTreeView.Selection.GetSelected(out var iter); + return (YahooAuctionsItem) itemTreeView.Model.GetValue(iter, 0); + } + } + private void WindowShutdown(object sender, DeleteEventArgs args) { SaveSettings(); Application.Quit(); @@ -196,7 +201,7 @@ namespace Buypeeb { // general behaviour /// - /// gets the path and iter for a given item id. + /// gets the path and iter for a given item id. /// /// the item id to find in the treeview /// a tuple of (TreePath, TreeIter) @@ -219,7 +224,7 @@ namespace Buypeeb { } /// - /// saves the settings to userdata.json. + /// saves the settings to userdata.json. /// private void SaveSettings() { var j = JsonSerializer.Serialize(settings, jsonOptions); @@ -234,10 +239,10 @@ namespace Buypeeb { } /// - /// updates the item with the given id. this method blocks and is intended to be run from a task. + /// updates the item with the given id. this method blocks and is intended to be run from a task. /// /// the id of the item to update - private void UpdateThread(string id) { + private async void UpdateThread(string id) { var item = settings.watchlist[id]; // Console.WriteLine($"Updating {id}..."); // set item.ready to false to show that it's still being updated @@ -253,18 +258,13 @@ namespace Buypeeb { } }); - using (var client = new WebClient()) { - // TODO: download should have timeout - try { - // item.Update(client.DownloadString("http://10.0.0.10/poop")); - item.Update(client.DownloadString(item.url)); - } catch (WebException e) { - if (((HttpWebResponse) e.Response).StatusCode == HttpStatusCode.NotFound) { - // the auction has ended (or otherwise been removed) - item.AuctionEnded(); - } else { - Console.WriteLine($"Failed to update item ${id}!"); - } + try { + item.Update(await httpClient.GetStringAsync(item.url)); + } catch (HttpRequestException e) { + if (e.StatusCode == HttpStatusCode.NotFound) { + item.AuctionEnded(); + } else { + Console.WriteLine($"Failed to update item ${id}! Status code ${e.StatusCode}: ${e.Message}"); } } @@ -286,7 +286,7 @@ namespace Buypeeb { } /// - /// processes the update queue. this is a blocking function. + /// processes the update queue. this is a blocking function. /// private void ProcessUpdateQueue() { queueActive = true; @@ -297,7 +297,7 @@ namespace Buypeeb { } /// - /// updates an item with the given id with a new task. + /// updates an item with the given id with a new task. /// /// the id of the task to update /// whether or not to call this.RenderList() after updating the item @@ -319,8 +319,8 @@ namespace Buypeeb { } /// - /// add every item in the watchlist to the update queue. if the update queue is already being processed - /// (this.queueActive), this will do nothing. + /// add every item in the watchlist to the update queue. if the update queue is already being processed + /// (this.queueActive), this will do nothing. /// private void UpdateItems() { if (queueActive) { @@ -347,7 +347,7 @@ namespace Buypeeb { } /// - /// updates the selection view, displaying the id, name, etc. for the currently selected item. + /// updates the selection view, displaying the id, name, etc. for the currently selected item. /// private void UpdateSelectionView() { // get the currently selected item @@ -397,7 +397,7 @@ namespace Buypeeb { } /// - /// opens a URL in the user's browser. + /// opens a URL in the user's browser. /// /// the url to open private static void OpenUrl(string url) { @@ -409,7 +409,7 @@ namespace Buypeeb { } /// - /// a simple MessageDialog constructor. + /// a simple MessageDialog constructor. /// /// the MessageDialog's format /// the MessageDialog's bt @@ -427,7 +427,7 @@ namespace Buypeeb { } /// - /// show a simple entry dialogue that allows the user to enter text and either cancel or submit it. + /// show a simple entry dialogue that allows the user to enter text and either cancel or submit it. /// /// the title of the entry dialogue /// the prompt that should be presented to the user @@ -470,7 +470,7 @@ namespace Buypeeb { } /// - /// gets the sort type selected by the user - "NameDescending", "EndingAscending", etc. + /// gets the sort type selected by the user - "NameDescending", "EndingAscending", etc. /// /// the id of the radiobutton without the "Sort" prefix private string GetSortType() { @@ -487,8 +487,8 @@ namespace Buypeeb { } /// - /// clears the treeview's liststore and adds everything in the watchlist to it, obeying sort order. tries to - /// reselect the item that the user had selected, if possible. + /// clears the treeview's liststore and adds everything in the watchlist to it, obeying sort order. tries to + /// reselect the item that the user had selected, if possible. /// private void RenderList() { string id = null; @@ -498,21 +498,15 @@ namespace Buypeeb { items.Clear(); var values = settings.watchlist.Values; - IOrderedEnumerable sorted; var type = GetSortType(); - if (type == "NameDescending") { - sorted = values.OrderByDescending(item => item.name); - } else if (type == "NameAscending") { - sorted = values.OrderBy(item => item.name); - } else if (type == "PriceDescending") { - sorted = values.OrderByDescending(item => item.Price); - } else if (type == "PriceAscending") { - sorted = values.OrderBy(item => item.Price); - } else if (type == "EndingDescending") { - sorted = values.OrderByDescending(item => item.endDate); - } else { - sorted = values.OrderBy(item => item.endDate); - } + var sorted = type switch { + "NameDescending" => values.OrderByDescending(item => item.name), + "NameAscending" => values.OrderBy(item => item.name), + "PriceDescending" => values.OrderByDescending(item => item.Price), + "PriceAscending" => values.OrderBy(item => item.Price), + "EndingDescending" => values.OrderByDescending(item => item.endDate), + _ => values.OrderBy(item => item.endDate), + }; if (settings.showFavouritesAtTopOfList) { foreach (var item in sorted.Where(item => item.favourite)) { @@ -753,9 +747,7 @@ namespace Buypeeb { private void ButtonSelectedRemoveClicked(object sender, EventArgs a) { var item = selectedItem; - var md = MsgBox( - $"Are you sure you want to remove the item \"{item.name}\"?" - ); + var md = MsgBox($"Are you sure you want to remove the item \"{item.name}\"?"); var response = (ResponseType) md.Run(); md.Dispose(); @@ -815,7 +807,8 @@ namespace Buypeeb { } private void TextViewSelectedNotesFocusOut(object sender, FocusOutEventArgs args) { - // the "save" button does nothing, however, when you click the save button, you transfer focus to it, firing this event! + // the "save" button does nothing, however, when you click the save button, you transfer focus to it, firing this + // event! // how very sneaky var noteBuffer = (TextBuffer) builder.GetObject("TextBufferSelectedNotes"); if (selectedItem != null) { @@ -834,7 +827,7 @@ namespace Buypeeb { // timers /// - /// updates the end time displayed in the selection box. runs every second to update the countdown timer. + /// updates the end time displayed in the selection box. runs every second to update the countdown timer. /// /// true private bool UpdateSelectionEndTime() { @@ -870,7 +863,7 @@ namespace Buypeeb { } /// - /// updates all items that need updating. runs every ten seconds. + /// updates all items that need updating. runs every ten seconds. /// /// true private bool AutoUpdateItems() { @@ -965,10 +958,10 @@ namespace Buypeeb { // first, check to see if any filters are set that would exclude everything, such as hiding both active and ended auctions // if so, there's no need to run the more expensive linq query if ( - (Filtered("Favourites") && Filtered("NonFavourites")) || - (Filtered("Active") && Filtered("Ended")) || - (Filtered("EndingToday") && Filtered("EndingAfterToday")) || - (Filtered("WithWinPrice") && Filtered("WithNoWinPrice")) + Filtered("Favourites") && Filtered("NonFavourites") || + Filtered("Active") && Filtered("Ended") || + Filtered("EndingToday") && Filtered("EndingAfterToday") || + Filtered("WithWinPrice") && Filtered("WithNoWinPrice") ) { return false; } diff --git a/Settings.cs b/Settings.cs index a12b189..e3174f8 100644 --- a/Settings.cs +++ b/Settings.cs @@ -3,6 +3,12 @@ using System.Collections.Generic; namespace Buypeeb { internal class Settings { + public Settings() { + // create a new watchlist from an empty dictionary if it's null, which should only happen if either this is the + // first time the program has been run, or there's something wrong with userdata.json + watchlist ??= new Dictionary(); + } + public int updateInterval { get; set; } = 10 * 60; public int favouriteUpdateInterval { get; set; } = 5 * 60; public int updateIntervalCritical { get; set; } = 60; @@ -13,12 +19,6 @@ namespace Buypeeb { public Dictionary watchlist { get; set; } - public Settings() { - // create a new watchlist from an empty dictionary if it's null, which should only happen if either this is the - // first time the program has been run, or there's something wrong with userdata.json - watchlist ??= new Dictionary(); - } - public YahooAuctionsItem Watch(string url, string name) { var id = BuypeebApp.IDFromURL(url); Console.WriteLine(id); diff --git a/SettingsWindow.cs b/SettingsWindow.cs index 8544707..cc23989 100644 --- a/SettingsWindow.cs +++ b/SettingsWindow.cs @@ -7,15 +7,16 @@ using Gtk; namespace Buypeeb { internal class SettingsWindow : Window { - private readonly List generalSwitches = new List(); - private readonly List updateIntervalEntries = new List(); - private readonly Settings settings; private readonly Builder builder; + private readonly List generalSwitches = new(); - private readonly List generalSwitchNames = new List - {"ShowSecondsInListView", "Autosave", "ShowFavouritesAtTopOfList"}; + private readonly List generalSwitchNames = + new() {"ShowSecondsInListView", "Autosave", "ShowFavouritesAtTopOfList"}; - private readonly List updateIntervalEntryNames = new List + private readonly Settings settings; + private readonly List updateIntervalEntries = new(); + + private readonly List updateIntervalEntryNames = new() {"UpdateInterval", "UpdateIntervalCritical", "FavouriteUpdateInterval", "FavouriteUpdateIntervalCritical"}; public SettingsWindow(Settings settings) : this(new Builder("settings.glade"), settings) { @@ -41,11 +42,11 @@ namespace Buypeeb { } private T GetSetting(string property) { - return (T) settings.GetType().GetProperty(property).GetValue(settings, null); + return (T) settings.GetType().GetProperty(property)?.GetValue(settings, null); } private void SetSetting(string property, T value) { - settings.GetType().GetProperty(property).SetValue(settings, value); + settings.GetType().GetProperty(property)?.SetValue(settings, value); } private string PropertyName(string property) { diff --git a/YahooAuctionsItem.cs b/YahooAuctionsItem.cs index 28fd7ce..fa925b7 100644 --- a/YahooAuctionsItem.cs +++ b/YahooAuctionsItem.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.Text.Json; using System.Text.Json.Serialization; @@ -9,6 +10,25 @@ using CsvHelper.Configuration.Attributes; namespace Buypeeb { internal class YahooAuctionsItem { + public bool AutoExtension; + public bool Available; + public int Bids; + public DateTime LastUpdated; + public int Price; + public bool Ready; + public DateTime StartDate; + public bool UpdateFailed; + public int WinPrice; + + public YahooAuctionsItem(string id, string name) { + this.id = id; + this.name = name; + } + + public YahooAuctionsItem() { + // parameterless constructor for deserialisation + } + [JsonIgnore] public string url => $"https://page.auctions.yahoo.co.jp/jp/auction/{id}"; [JsonIgnore] public string buyeeUrl => $"https://buyee.jp/item/yahoo/auction/{id}"; @@ -25,21 +45,13 @@ namespace Buypeeb { // you also don't want to private any of the setters, which will have a similar effect. [Ignore] public string id { get; set; } public string name { get; set; } - public int Price; - public int WinPrice; public string originalName { get; set; } public string notes { get; set; } public bool favourite { get; set; } - public DateTime StartDate; public DateTime endDate { get; set; } - public DateTime LastUpdated; - public int Bids; - public bool AutoExtension; - public bool Ready; - public bool Available; - public bool UpdateFailed; - [Ignore, JsonIgnore] + [Ignore] + [JsonIgnore] public bool updatedRecently { get { var later = LastUpdated.AddSeconds(15); @@ -51,27 +63,18 @@ namespace Buypeeb { [JsonIgnore] public string winPriceJpy => $"¥{WinPrice}"; - [Ignore, JsonIgnore] public string priceAud => $"${(Price / 75.0):f2}"; + [Ignore] [JsonIgnore] public string priceAud => $"${Price / 75.0:f2}"; - [Ignore, JsonIgnore] public string winPriceAud => $"${(WinPrice / 75.0):f2}"; + [Ignore] [JsonIgnore] public string winPriceAud => $"${WinPrice / 75.0:f2}"; - [Ignore, JsonIgnore] public bool endingToday => endDate.DayOfYear == DateTime.UtcNow.DayOfYear; + [Ignore] [JsonIgnore] public bool endingToday => endDate.DayOfYear == DateTime.UtcNow.DayOfYear; - [Ignore, JsonIgnore] public bool hasWinPrice => WinPrice != 0; + [Ignore] [JsonIgnore] public bool hasWinPrice => WinPrice != 0; - [Ignore, JsonIgnore] public bool endingSoon => DateTime.Compare(DateTime.UtcNow.AddMinutes(10), endDate) > 0; + [Ignore] [JsonIgnore] public bool endingSoon => DateTime.Compare(DateTime.UtcNow.AddMinutes(10), endDate) > 0; private bool success { get; set; } // TODO: custom setter that throws an exception if set to false or something idk - public YahooAuctionsItem(string id, string name) { - this.id = id; - this.name = name; - } - - public YahooAuctionsItem() { - // parameterless constructor for deserialisation - } - public void AuctionEnded() { // the page 404'd. this probably means that the auction has ended, and the page has been removed. Available = false; @@ -81,8 +84,8 @@ namespace Buypeeb { public void Update(string html) { // TODO: handle all the parsing errors and weird interpretation that could possibly happen here - var rx = new Regex(@"var pageData ?= ?(\{.+?\});", - RegexOptions.Singleline); // TODO: maybe compile and match the regex in another thread + // TODO: maybe compile and match the regex in another thread + var rx = new Regex(@"var pageData ?= ?(\{.+?\});", RegexOptions.Singleline); var m = rx.Match(html); Dictionary> jFull; @@ -98,6 +101,7 @@ namespace Buypeeb { var jst = TimeZoneInfo.CreateCustomTimeZone("JST", new TimeSpan(9, 0, 0), "Japan Standard Time", "Japan Standard Time"); + Debug.Assert(jFull != null, nameof(jFull) + " != null"); var j = jFull["items"]; originalName = j["productName"]; StartDate = TimeZoneInfo.ConvertTimeToUtc( @@ -122,7 +126,7 @@ namespace Buypeeb { // whether or not it's equal to "ari" (japanese for "yes"). rx = new Regex(@"自動延長.+\n.+>(.+)<"); m = rx.Match(html); - AutoExtension = (m.Groups[1].Value == "あり"); + AutoExtension = m.Groups[1].Value == "あり"; UpdateFailed = false; } diff --git a/buypeeb.csproj b/buypeeb.csproj index a60247b..9d2d54c 100755 --- a/buypeeb.csproj +++ b/buypeeb.csproj @@ -1,22 +1,22 @@ - - WinExe - netcoreapp5.0 - Buypeeb.BuypeebApp - + + WinExe + netcoreapp5.0 + Buypeeb.BuypeebApp + - - - - %(Filename)%(Extension) - - - + + + + %(Filename)%(Extension) + + + - - - - + + + +