|
|
@@ -5,23 +5,22 @@ use chrono::{Utc, Duration, Local, DateTime};
|
|
|
use select::document::Document;
|
|
|
use select::predicate::Name;
|
|
|
use parse_duration::parse;
|
|
|
-use serde;
|
|
|
use serde_json;
|
|
|
use std::io::BufWriter;
|
|
|
use std::io::BufReader;
|
|
|
use std::fs::File;
|
|
|
|
|
|
-/// Fucking egun is a mess. It does not even have classes. This is an attempt to parse it.
|
|
|
+/// Fucking egun is a mess. It does not even use css and is built using tables. This is an attempt to parse it.
|
|
|
|
|
|
|
|
|
|
|
|
-#[derive(Serialize, Deserialize, Debug, Default)]
|
|
|
+#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
|
|
struct Auction {
|
|
|
price: f32,
|
|
|
desc: String,
|
|
|
gcal: String,
|
|
|
thumb: String,
|
|
|
- remaining: String,
|
|
|
+ remaining: i64,
|
|
|
url: String,
|
|
|
timestamp: i64,
|
|
|
is_price_final: bool
|
|
|
@@ -31,7 +30,7 @@ struct Auction {
|
|
|
struct Query {
|
|
|
url: String,
|
|
|
auctions: Vec<Auction>,
|
|
|
- frequency: i64
|
|
|
+ frequency: i64,
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -39,6 +38,13 @@ impl Query {
|
|
|
fn run(&mut self) {
|
|
|
self.auctions = parse_url(&self.url);
|
|
|
}
|
|
|
+
|
|
|
+ fn detect_frequency(&self) {
|
|
|
+ let _a = &self.auctions
|
|
|
+ .iter()
|
|
|
+ .map(|x| x)
|
|
|
+ .collect::<Vec<_>>();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
impl Default for Query {
|
|
|
@@ -67,12 +73,20 @@ fn date_to_gcal(date: DateTime<Local>) -> String {
|
|
|
}
|
|
|
|
|
|
/// Parse a relative offset "x Tage HH:MM" into proper time
|
|
|
-fn parse_end_date(timestring: &String) -> Option<DateTime<Local>> {
|
|
|
+// fn parse_end_date(timestring: &String) -> Option<DateTime<Local>> {
|
|
|
+// match parse(&format!("{} minutes", timestring.replace(":", " hours ").replace("Tage", "days"))).ok() {
|
|
|
+// Some(old_duration) => match Duration::from_std(old_duration).ok() {
|
|
|
+// Some(chronoduration) => Some(Utc::now().with_timezone(&Local) + chronoduration),
|
|
|
+// None => None
|
|
|
+// }
|
|
|
+// None => None
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+/// Parse a relative offset "x Tage HH:MM" into proper time
|
|
|
+fn parse_remaining(timestring: &String) -> Option<Duration> {
|
|
|
match parse(&format!("{} minutes", timestring.replace(":", " hours ").replace("Tage", "days"))).ok() {
|
|
|
- Some(old_duration) => match Duration::from_std(old_duration).ok() {
|
|
|
- Some(chronoduration) => Some(Utc::now().with_timezone(&Local) + chronoduration),
|
|
|
- None => None
|
|
|
- }
|
|
|
+ Some(old_duration) => Duration::from_std(old_duration).ok(),
|
|
|
None => None
|
|
|
}
|
|
|
}
|
|
|
@@ -87,13 +101,15 @@ fn parse_price(price: &str) -> Option<f32> {
|
|
|
|
|
|
|
|
|
fn parse_url(url: &str) -> Vec<Auction> {
|
|
|
- let mut resp = reqwest::get(url).unwrap();
|
|
|
- assert!(resp.status().is_success());
|
|
|
- // dbg!(&resp.text());
|
|
|
+ let mut auctions = vec![];
|
|
|
|
|
|
- let text = resp.text().unwrap();
|
|
|
+ if let Ok(mut resp) = reqwest::get(url) {
|
|
|
+ if !resp.status().is_success() {
|
|
|
+ return auctions;
|
|
|
+ }
|
|
|
+
|
|
|
+ let text = resp.text().unwrap_or("".to_string());
|
|
|
|
|
|
- let mut auctions = vec![];
|
|
|
|
|
|
|
|
|
for node in Document::from_read(text.as_bytes())
|
|
|
@@ -123,6 +139,8 @@ fn parse_url(url: &str) -> Vec<Auction> {
|
|
|
// instantiate mutable Auction
|
|
|
let mut auction = Auction::default();
|
|
|
auction.desc = name.clone();
|
|
|
+ auction.thumb = format!("http://egun.de/market/images/picture.gif");
|
|
|
+
|
|
|
|
|
|
// get image
|
|
|
if let Some(img) = node
|
|
|
@@ -154,7 +172,6 @@ fn parse_url(url: &str) -> Vec<Auction> {
|
|
|
}
|
|
|
|
|
|
|
|
|
-
|
|
|
// get article url
|
|
|
if let Some(url) = node
|
|
|
.children().into_iter()
|
|
|
@@ -169,7 +186,7 @@ fn parse_url(url: &str) -> Vec<Auction> {
|
|
|
}
|
|
|
|
|
|
// TODO: check if https://doc.rust-lang.org/std/time/struct.SystemTime.html works too
|
|
|
- if let Some(remaining) = parse_end_date(
|
|
|
+ if let Some(remaining) = parse_remaining(
|
|
|
&node
|
|
|
.children()
|
|
|
.filter(|x| x.attr("align") == Some("center"))
|
|
|
@@ -181,8 +198,11 @@ fn parse_url(url: &str) -> Vec<Auction> {
|
|
|
.join(" ")
|
|
|
){
|
|
|
// dbg!(&t_remaining.children());
|
|
|
- auction.gcal = format!("http://www.google.com/calendar/event?action=TEMPLATE&dates={}&text={}&location=&details=", date_to_gcal(remaining), auction.desc);
|
|
|
- auction.timestamp = remaining.timestamp();
|
|
|
+
|
|
|
+ let end_date = Utc::now().with_timezone(&Local) + remaining;
|
|
|
+ auction.gcal = format!("http://www.google.com/calendar/event?action=TEMPLATE&dates={}&text={}&location=&details=", date_to_gcal(end_date), auction.desc);
|
|
|
+ auction.remaining = remaining.num_seconds();
|
|
|
+ auction.timestamp = end_date.timestamp();
|
|
|
// println!("ENDS\t{:?}", date_to_gcal(remaining));
|
|
|
|
|
|
}
|
|
|
@@ -191,25 +211,34 @@ fn parse_url(url: &str) -> Vec<Auction> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- auctions
|
|
|
+ }
|
|
|
+ auctions
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
- let url = "http://www.egun.de/market/list_items.php?mode=qry&plusdescr=off&wheremode=and&query=sniper&quick=1";
|
|
|
+
|
|
|
|
|
|
+ let mut auctions = vec![];
|
|
|
+
|
|
|
+ let reader = BufReader::new(File::open("urls.json").unwrap());
|
|
|
+ let urls: Vec<String> = serde_json::from_reader(reader).unwrap();
|
|
|
+
|
|
|
|
|
|
- let mut query = Query {
|
|
|
- url: url.to_string(),
|
|
|
- ..Default::default()
|
|
|
- };
|
|
|
+ for url in urls {
|
|
|
|
|
|
- query.run();
|
|
|
- dbg!(query);
|
|
|
+ let mut query = Query {
|
|
|
+ url: url.to_string(),
|
|
|
+ ..Default::default()
|
|
|
+ };
|
|
|
+ query.run();
|
|
|
+
|
|
|
+ auctions.extend(query.auctions);
|
|
|
+ }
|
|
|
|
|
|
// write out the file
|
|
|
let writer = BufWriter::new(File::create("db.json").unwrap());
|
|
|
- serde_json::to_writer_pretty(writer, &parse_url(url)).unwrap();
|
|
|
+ serde_json::to_writer_pretty(writer, &auctions).unwrap();
|
|
|
}
|