diff --git a/.gitignore b/.gitignore index 677b6ed..dd4e239 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ BuypeebApp.exe bin/ obj/ out/ +yahoo.html \ No newline at end of file diff --git a/Listing.cs b/Listing.cs index 60c16ff..31e0593 100644 --- a/Listing.cs +++ b/Listing.cs @@ -1,9 +1,16 @@ using System; using System.Globalization; +using System.Text.RegularExpressions; +using System.Collections.Generic; +using System.Text.Json; namespace Buypeeb { class Listing { - public string url { get; set; } + public string url { + get { + return $"https://page.auctions.yahoo.co.jp/jp/auction/{this.id}"; + } + } public string id { get; set; } public string name { get; set; } public int price = 0; @@ -15,24 +22,54 @@ namespace Buypeeb { public bool auto_extension; public bool ready; - public Listing(string url, string id, string name) { - this.url = url; + private bool success { get; set; } // TODO: custom setter that throws an exception if set to false or something idk + + public Listing(string id, string name) { this.id = id; this.name = name; this.ready = false; } public Listing() { - // parameterless constructur for deserialisation + // parameterless constructor for deserialisation } - public void Update() { - // use fake values for now - var rnd = new Random(); - this.price = rnd.Next(100, 5000); - this.bids = rnd.Next(0, 15); - this.name = "testing"; - this.original_name = "testing"; + public void Update(string html) { + var rx = new Regex(@"var pageData ?= ?(\{.+?\});", RegexOptions.Singleline); // TODO: maybe compile and match the regex in another thread + var m = rx.Match(html); + if (m == null) { + Console.WriteLine("no sir i don't like it"); + return; + } + + Dictionary> j_full; + try { + // master forgive me, but i must go all out, just this once... + j_full = JsonSerializer.Deserialize>>(m.Groups[1].Value); + } + catch (Exception e) { + throw e; + } + + var j = j_full["items"]; + this.original_name = j["productName"]; + this.success = int.TryParse(j["price"], out this.price); + this.success = int.TryParse(j["winPrice"], out this.win_price); + this.success = int.TryParse(j["bids"], out this.bids); + + if (String.IsNullOrWhiteSpace(this.name)) { + this.name = this.original_name; + } + + // as far as i can tell, neither the `pageData` nor the `conf` variables in the html seem to store whether or not the auction uses automatic extension + // the `conf` variable *does* store whether or not the auction has the "early end" feature enabled, in the key `earlyed`. + // unfortunately, it seems like the only way to get the auto extension info is to scrape the page for the info column that displays the auto ext status + // and check whether or not it's equal to "ari" (japanese for "yes"). + var autoExtensionCheck = new Regex(@"自動延長.+\n.+>(.+)<"); + m = rx.Match(html); + if (m.Groups[1].Value != null) { + this.auto_extension = (m.Groups[1].Value == "あり"); + } } public string PriceAUD() { diff --git a/MainWindow.cs b/MainWindow.cs index bd96126..b731891 100755 --- a/MainWindow.cs +++ b/MainWindow.cs @@ -38,6 +38,7 @@ namespace Buypeeb { class MainWindow : Window { private string location; + // private Queue statuses; private ListStore items; private Settings settings; @@ -116,6 +117,25 @@ namespace Buypeeb { this.statusLabel.Text = status ?? "Buypeeb"; } + private (TreePath path, TreeIter iter) GetRow(string id) { + // TODO: surely there's a better way to do this + TreeIter iter; + this.itemTreeView.Model.GetIterFirst(out iter); + + for (int i = 0; i < this.itemTreeView.Model.IterNChildren(); i++) { + var x = (Listing)this.itemTreeView.Model.GetValue(iter, 0); + if (x.id == id) { + return (this.itemTreeView.Model.GetPath(iter), iter); + } + else { + this.itemTreeView.Model.IterNext(ref iter); + } + } + + Console.WriteLine($"Couldn't find {id}!"); + return (null, iter); + } + private void SaveSettings() { string j = JsonSerializer.Serialize(this.settings); string p = System.IO.Path.Combine(this.location, "userdata.json"); @@ -135,33 +155,32 @@ namespace Buypeeb { // set item.ready to false to show that it's still being updated // this changes a few behaviours, such as displaying the price as "..." instead of whatever's currently stored item.ready = false; - // TODO: actually download the data - Thread.Sleep(3000); - string html = "Heeenlo"; - item.name = html; + + Gtk.Application.Invoke(delegate { + // TODO: find a way to not have to do this. i think we need to avoid actually modifying the items outside of the main thread :/ + var pathAndIter = this.GetRow(id); + this.items.EmitRowChanged(pathAndIter.path, pathAndIter.iter); + }); + + using (WebClient client = new WebClient()) { + // TODO: download should have timeout + item.Update(client.DownloadString(item.url)); + // item.Update(File.ReadAllText("yahoo.html")); + } + + Gtk.Application.Invoke(delegate { + var pathAndIter = this.GetRow(id); + this.items.EmitRowChanged(pathAndIter.path, pathAndIter.iter); + }); + item.ready = true; Console.WriteLine($"{id} updated."); - Gtk.Application.Invoke(delegate { - // TODO: surely there's a better way to do this - TreeIter iter; - this.itemTreeView.Model.GetIterFirst(out iter); - - for (int i = 0; i < this.itemTreeView.Model.IterNChildren(); i++) { - var x = (Listing)this.itemTreeView.Model.GetValue(iter, 0); - if (x.id == id) { - this.items.EmitRowChanged(this.itemTreeView.Model.GetPath(iter), iter); - break; - } - else { - this.itemTreeView.Model.IterNext(ref iter); - } - } - }); } private void UpdateItem(string id) { // don't start a new task if there are more than [tasklimit] tasks currently running // this makes sure we don't make 1000 simultaneous requests to yahoo auctions if there are 1000 items on the watchlist + this.settings.watchlist[id].ready = false; tasklimit.Wait(); var t = Task.Factory.StartNew(() => { this.UpdateThread(id); @@ -238,7 +257,6 @@ namespace Buypeeb { } private void ButtonSaveClicked(object sender, EventArgs a) { - Console.WriteLine("ButtonSaveClicked"); this.SaveSettings(); } @@ -281,17 +299,17 @@ namespace Buypeeb { private void RenderColumnPriceYen(Gtk.TreeViewColumn column, Gtk.CellRenderer cell, Gtk.ITreeModel model, Gtk.TreeIter iter) { Listing item = (Listing)model.GetValue(iter, 0); - (cell as Gtk.CellRendererText).Text = item.PriceJPY(); + (cell as Gtk.CellRendererText).Text = item.ready ? item.PriceJPY() : "..."; } private void RenderColumnPriceAUD(Gtk.TreeViewColumn column, Gtk.CellRenderer cell, Gtk.ITreeModel model, Gtk.TreeIter iter) { Listing item = (Listing)model.GetValue(iter, 0); - (cell as Gtk.CellRendererText).Text = item.PriceAUD(); + (cell as Gtk.CellRendererText).Text = item.ready ? item.PriceAUD() : "..."; } private void RenderColumnEnding(Gtk.TreeViewColumn column, Gtk.CellRenderer cell, Gtk.ITreeModel model, Gtk.TreeIter iter) { Listing item = (Listing)model.GetValue(iter, 0); - (cell as Gtk.CellRendererText).Text = "whatever"; + (cell as Gtk.CellRendererText).Text = item.ready ? "whatever" : "..."; } } diff --git a/Settings.cs b/Settings.cs index 0b69063..c816602 100644 --- a/Settings.cs +++ b/Settings.cs @@ -31,7 +31,7 @@ namespace Buypeeb { public void Watch(string url, string name) { string id = BuypeebApp.IDFromURL(url); Console.WriteLine(id); - this.watchlist[id] = new Listing(url, id, name); + this.watchlist[id] = new Listing(id, name); foreach (KeyValuePair entry in this.watchlist) { Console.WriteLine("{0} - {1}", entry.Value.name, entry.Value.price);