2020-09-01 10:41:29 +00:00
using System ;
using System.Globalization ;
2020-09-03 16:09:19 +00:00
using System.Text.RegularExpressions ;
using System.Collections.Generic ;
using System.Text.Json ;
2020-09-06 03:10:00 +00:00
using System.Text.Json.Serialization ;
using CsvHelper.Configuration.Attributes ;
2020-09-01 10:41:29 +00:00
namespace Buypeeb {
2020-09-04 09:13:16 +00:00
class YahooAuctionsItem {
2020-09-06 03:12:46 +00:00
[JsonIgnore]
2020-09-03 16:09:19 +00:00
public string url {
get {
return $"https://page.auctions.yahoo.co.jp/jp/auction/{this.id}" ;
}
}
2020-09-04 07:23:32 +00:00
2020-09-06 03:12:46 +00:00
[JsonIgnore]
2020-09-04 09:17:59 +00:00
public string buyeeUrl {
2020-09-04 07:23:32 +00:00
get {
return $"https://buyee.jp/item/yahoo/auction/{this.id}" ;
}
}
2020-09-04 10:16:42 +00:00
// 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
2020-09-06 03:12:46 +00:00
// anything with the attribute [Ignore] won't be put in the CSV, and things with [JsonIgnore] won't be put in userdata.json
2020-09-06 03:10:00 +00:00
[Ignore]
2020-09-02 04:12:56 +00:00
public string id { get ; set ; }
public string name { get ; set ; }
2020-09-05 05:02:37 +00:00
private int price = 0 ;
2020-09-04 09:17:59 +00:00
public int winPrice ;
2020-09-04 10:16:42 +00:00
public string originalName { get ; set ; }
2020-09-04 11:59:54 +00:00
public string notes { get ; set ; }
2020-09-02 04:12:56 +00:00
public bool favourite { get ; set ; } = false ;
2020-09-04 10:16:42 +00:00
public DateTime startDate ;
2020-09-06 02:56:10 +00:00
public DateTime endDate { get ; set ; }
2020-09-04 10:16:42 +00:00
public DateTime lastUpdated ;
2020-09-01 10:41:29 +00:00
public int bids ;
2020-09-04 09:17:59 +00:00
public bool autoExtension ;
2020-09-03 05:23:23 +00:00
public bool ready ;
2020-09-04 10:16:42 +00:00
public bool available ;
2020-09-06 03:10:00 +00:00
[Ignore, JsonIgnore]
2020-09-04 10:16:42 +00:00
public bool updatedRecently {
get {
var later = this . lastUpdated . AddSeconds ( 15 ) ;
return DateTime . Compare ( later , this . lastUpdated ) < 0 ;
}
}
2020-09-01 10:41:29 +00:00
2020-09-06 03:10:00 +00:00
[JsonIgnore]
2020-09-05 06:51:28 +00:00
public string priceJPY { get { return $"¥{this.price}" ; } }
2020-09-06 03:10:00 +00:00
[JsonIgnore]
2020-09-05 06:51:28 +00:00
public string winPriceJPY { get { return $"¥{this.winPrice}" ; } }
2020-09-06 03:10:00 +00:00
[Ignore, JsonIgnore]
2020-09-05 06:51:28 +00:00
public string priceAUD { get { return $"${(this.price / 75.0):f2}" ; } }
2020-09-06 03:10:00 +00:00
[Ignore, JsonIgnore]
2020-09-05 06:51:28 +00:00
public string winPriceAUD { get { return $"${(this.winPrice / 75.0):f2}" ; } }
2020-09-06 03:10:00 +00:00
[Ignore, JsonIgnore]
2020-09-05 06:51:28 +00:00
public bool endingToday { get { return this . endDate . DayOfYear = = DateTime . UtcNow . DayOfYear ; } }
2020-09-06 03:10:00 +00:00
[Ignore, JsonIgnore]
2020-09-05 06:51:28 +00:00
public bool hasWinPrice { get { return this . winPrice ! = 0 ; } }
2020-09-06 03:10:00 +00:00
[Ignore, JsonIgnore]
2020-09-05 18:36:17 +00:00
public bool endingSoon { get { return DateTime . Compare ( DateTime . UtcNow . AddMinutes ( 10 ) , this . endDate ) > 0 ; } }
2020-09-06 03:10:00 +00:00
private bool success { get ; set ; } // TODO: custom setter that throws an exception if set to false or something idk
2020-09-03 16:09:19 +00:00
2020-09-04 09:13:16 +00:00
public YahooAuctionsItem ( string id , string name ) {
2020-09-01 10:41:29 +00:00
this . id = id ;
this . name = name ;
}
2020-09-04 09:13:16 +00:00
public YahooAuctionsItem ( ) {
2020-09-03 16:09:19 +00:00
// parameterless constructor for deserialisation
2020-09-03 13:47:36 +00:00
}
2020-09-03 16:09:19 +00:00
public void Update ( string html ) {
2020-09-04 10:16:42 +00:00
// TODO: handle all the parsing errors and weird interpretation that could possibly happen here
2020-09-03 16:09:19 +00:00
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 < string , Dictionary < string , string > > j_full ;
try {
// master forgive me, but i must go all out, just this once...
j_full = JsonSerializer . Deserialize < Dictionary < string , Dictionary < string , string > > > ( m . Groups [ 1 ] . Value ) ;
}
catch ( Exception e ) {
2020-09-04 10:16:42 +00:00
Console . WriteLine ( "oh jeez oh man oh jeez oh man oh jeez oh man" ) ;
Console . WriteLine ( m . Groups [ 1 ] . Value ) ;
2020-09-03 16:09:19 +00:00
throw e ;
}
2020-09-04 12:53:57 +00:00
var jst = TimeZoneInfo . CreateCustomTimeZone ( "JST" , new TimeSpan ( 9 , 0 , 0 ) , "Japan Standard Time" , "Japen Standard Time" ) ;
2020-09-04 10:16:42 +00:00
2020-09-03 16:09:19 +00:00
var j = j_full [ "items" ] ;
2020-09-04 09:17:59 +00:00
this . originalName = j [ "productName" ] ;
2020-09-04 10:16:42 +00:00
this . startDate = TimeZoneInfo . ConvertTimeToUtc ( DateTime . ParseExact ( j [ "starttime" ] , "yyyy-MM-dd HH:mm:ss" , CultureInfo . InvariantCulture ) , jst ) ;
this . endDate = TimeZoneInfo . ConvertTimeToUtc ( DateTime . ParseExact ( j [ "endtime" ] , "yyyy-MM-dd HH:mm:ss" , CultureInfo . InvariantCulture ) , jst ) ;
this . lastUpdated = DateTime . UtcNow ;
this . available = j [ "isClosed" ] = = "0" ;
2020-09-03 16:09:19 +00:00
this . success = int . TryParse ( j [ "price" ] , out this . price ) ;
2020-09-04 09:17:59 +00:00
this . success = int . TryParse ( j [ "winPrice" ] , out this . winPrice ) ;
2020-09-03 16:09:19 +00:00
this . success = int . TryParse ( j [ "bids" ] , out this . bids ) ;
if ( String . IsNullOrWhiteSpace ( this . name ) ) {
2020-09-04 09:17:59 +00:00
this . name = this . originalName ;
2020-09-03 16:09:19 +00:00
}
// 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 ) {
2020-09-04 09:17:59 +00:00
this . autoExtension = ( m . Groups [ 1 ] . Value = = "あり" ) ;
2020-09-03 16:09:19 +00:00
}
2020-09-01 10:41:29 +00:00
}
2020-09-05 06:51:28 +00:00
public override string ToString ( ) {
return $"{this.id}: {this.name}" ;
}
2020-09-01 10:41:29 +00:00
}
}