buypeeb-cs/YahooAuctionsItem.cs

128 lines
4.5 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using CsvHelper.Configuration.Attributes;
namespace Buypeeb {
internal class YahooAuctionsItem {
[JsonIgnore]
public string url => $"https://page.auctions.yahoo.co.jp/jp/auction/{id}";
[JsonIgnore]
public string buyeeUrl => $"https://buyee.jp/item/yahoo/auction/{id}";
// any items with a getter will be saved to userdata.json
// anything that's configurable by the user, such as the custom name, should be saved
// the id *must* be saved!
// i'm also saving the original name to make it easier to tell what the items are in the userdata.json
// there's not really a need for it i guess but it's my program and i can do what i want
// anything with the attribute [Ignore] won't be put in the CSV, and things with [JsonIgnore] won't be put in userdata.json
[Ignore]
public string id { get; }
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;
[Ignore, JsonIgnore]
public bool updatedRecently {
get {
var later = LastUpdated.AddSeconds(15);
return DateTime.Compare(later, LastUpdated) < 0;
}
}
[JsonIgnore]
public string priceJpy => $"¥{Price}";
[JsonIgnore]
public string winPriceJpy => $"¥{WinPrice}";
[Ignore, JsonIgnore]
public string priceAud => $"${(Price / 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 hasWinPrice => WinPrice != 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 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
var m = rx.Match(html);
Dictionary<string, Dictionary<string, string>> jFull;
try {
// master forgive me, but i must go all out, just this once...
jFull = JsonSerializer.Deserialize<Dictionary<string, Dictionary<string, string>>>(m.Groups[1].Value);
}
catch {
Console.WriteLine("oh jeez oh man oh jeez oh man oh jeez oh man");
Console.WriteLine(m.Groups[1].Value);
throw;
}
var jst = TimeZoneInfo.CreateCustomTimeZone("JST", new TimeSpan(9, 0, 0), "Japan Standard Time", "Japen Standard Time");
var j = jFull["items"];
originalName = j["productName"];
StartDate = TimeZoneInfo.ConvertTimeToUtc(DateTime.ParseExact(j["starttime"], "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture), jst);
endDate = TimeZoneInfo.ConvertTimeToUtc(DateTime.ParseExact(j["endtime"], "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture), jst);
LastUpdated = DateTime.UtcNow;
Available = j["isClosed"] == "0";
success = int.TryParse(j["price"], out Price);
success = int.TryParse(j["winPrice"], out WinPrice);
success = int.TryParse(j["bids"], out Bids);
if (String.IsNullOrWhiteSpace(name)) {
name = originalName;
}
// 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").
rx = new Regex(@"自動延長.+\n.+>(.+)<");
m = rx.Match(html);
if (m.Groups[1].Value != null) {
AutoExtension = (m.Groups[1].Value == "あり");
}
}
public override string ToString() {
return $"{id}: {name}";
}
}
}