From 4a6922c2c172e68b3852e04bc81f4c56e0976aa4 Mon Sep 17 00:00:00 2001 From: Teascade Date: Sun, 23 Aug 2020 01:53:49 +0300 Subject: [PATCH] Initial commit --- .gitignore | 1 + Cargo.lock | 427 ++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 12 ++ config.toml | 7 + src/api.rs | 163 +++++++++++++++++++ src/config.rs | 32 ++++ src/main.rs | 56 +++++++ 7 files changed, 698 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 config.toml create mode 100644 src/api.rs create mode 100644 src/config.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..2f2ea5d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,427 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "argh" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca1877e24cecacd700d469066e0160c4f8497cc5635367163f50c8beec820154" +dependencies = [ + "argh_derive", + "argh_shared", +] + +[[package]] +name = "argh_derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e742194e0f43fc932bcb801708c2b279d3ec8f527e3acda05a6a9f342c5ef764" +dependencies = [ + "argh_shared", + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "argh_shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1ba68f4276a778591e36a0c348a269888f3a177c8d2054969389e3b59611ff5" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "base64" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" +dependencies = [ + "byteorder", +] + +[[package]] +name = "bumpalo" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "cc" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66120af515773fb005778dc07c261bd201ec8ce50bd6e7144c927753fe013381" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "chrono" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942f72db697d8767c22d46a598e01f2d3b475501ea43d0db4f16d90259182d0b" +dependencies = [ + "num-integer", + "num-traits", + "time", +] + +[[package]] +name = "heck" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "itoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" + +[[package]] +name = "js-sys" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a7e2c92a4804dd459b86c339278d0fe87cf93757fae222c3fa3ae75458bc73" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3" + +[[package]] +name = "log" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "minreq" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab229c252995e9d56cc66857f3ab2c41e3138b1a6c92089f013698388e64d6bd" +dependencies = [ + "lazy_static", + "rustls", + "serde", + "serde_json", + "webpki", + "webpki-roots", +] + +[[package]] +name = "num-integer" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" + +[[package]] +name = "proc-macro2" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ring" +version = "0.16.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "952cd6b98c85bbc30efa1ba5783b8abf12fec8b3287ffa52605b9432313e34e4" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustls" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b25a18b1bf7387f0145e7f8324e700805aade3842dd3db2e74e4cdeb4677c09e" +dependencies = [ + "base64", + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "sct" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "serde" +version = "1.0.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "syn" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d8d6567fe7c7f8835a3a98af4208f3846fba258c1bc3c31d6e506239f11f9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "thingy" +version = "0.1.0" +dependencies = [ + "argh", + "chrono", + "minreq", + "serde", + "toml", +] + +[[package]] +name = "time" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "toml" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" +dependencies = [ + "serde", +] + +[[package]] +name = "unicode-segmentation" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "wasm-bindgen" +version = "0.2.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0563a9a4b071746dd5aedbc3a28c6fe9be4586fb3fbadb67c400d4f53c6b16c" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc71e4c5efa60fb9e74160e89b93353bc24059999c0ae0fb03affc39770310b0" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97c57cefa5fa80e2ba15641578b44d36e7a64279bc5ed43c6dbaf329457a2ed2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841a6d1c35c6f596ccea1f82504a192a60378f64b3bb0261904ad8f2f5657556" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93b162580e34310e5931c4b792560108b10fd14d64915d7fff8ff00180e70092" + +[[package]] +name = "web-sys" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dda38f4e5ca63eda02c059d243aa25b5f35ab98451e518c51612cd0f1bd19a47" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab146130f5f790d45f82aeeb09e55a256573373ec64409fc19a6fb82fb1032ae" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cd5736df7f12a964a5067a12c62fa38e1bd8080aff1f80bc29be7c80d19ab4" +dependencies = [ + "webpki", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..18a8ae0 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "thingy" +version = "0.1.0" +authors = ["Teascade "] +edition = "2018" + +[dependencies] +serde = { version = "1.0", features = ["derive"] } +toml = "0.5" +minreq = { version = "2.2.0", features = ["https", "json-using-serde"] } +chrono = "0.4" +argh = "0.1" \ No newline at end of file diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..5ab2f43 --- /dev/null +++ b/config.toml @@ -0,0 +1,7 @@ +api_key = "E3GrOiQAnY61BP623XXzt9Fo87A1IQrS1FFzD57P" +tags_url = "https://platform.yepzon.com/tags" +states_url = "https://platform.yepzon.com/tags/{tag}/states" +locations_url = "https://platform.yepzon.com/tags/{tag}/locations/{state}" +timestamp_format = "%Y-%m-%dT%H:%M:%S%.fZ" +between_format = "%d.%m.%Y %H:%M:%S" +throttle = 110 diff --git a/src/api.rs b/src/api.rs new file mode 100644 index 0000000..d59213b --- /dev/null +++ b/src/api.rs @@ -0,0 +1,163 @@ +use super::Config; +use chrono::format::ParseError; +use chrono::NaiveDateTime; +use minreq::{Error, Response}; +use serde::Deserialize; +use std::thread::sleep; +use std::time::{Duration, Instant}; + +pub trait Timestamped { + fn timestamp(&self) -> Option; +} + +#[derive(Deserialize, Debug)] +pub struct TagModel { + pub id: Option, + pub imei: Option, + #[serde(rename(deserialize = "batteryValue"))] + pub battery_value: Option, + #[serde(rename(deserialize = "btId"))] + pub bt_id: Option, + #[serde(rename(deserialize = "nfcId"))] + pub nfc_id: Option, +} + +#[derive(Deserialize, Debug, Clone)] +pub struct StateModel { + pub id: Option, + pub timestamp: Option, + #[serde(skip_deserializing)] + pub state: Option, + #[serde(rename(deserialize = "realState"))] + pub real_state: Option, +} + +impl Timestamped for StateModel { + fn timestamp(&self) -> Option { + self.timestamp.clone() + } +} + +#[derive(Deserialize, Debug, Clone)] +#[serde(deny_unknown_fields)] +pub struct LocationModel { + pub timestamp: Option, + #[serde(rename(deserialize = "type"))] + pub loc_type: Option, + #[serde(rename(deserialize = "coordinateLat"))] + pub lat: Option, + #[serde(rename(deserialize = "coordinateLng"))] + pub lon: Option, + pub accuracy: Option, +} + +impl Timestamped for LocationModel { + fn timestamp(&self) -> Option { + self.timestamp.clone() + } +} + +pub struct API { + config: Config, + pub last_response_ping: u32, +} + +impl API { + pub fn new(config: Config) -> API { + API { + last_response_ping: config.throttle, + config: config, + } + } + + pub fn get_tags(&mut self) -> Result, Error> { + let response = self.request(self.config.tags_url.clone())?; + response.json() + } + + pub fn get_states(&mut self, tag_id: &String) -> Result, Error> { + let response = self.request(str::replace(&self.config.states_url, "{tag}", &tag_id))?; + response.json() + } + + pub fn get_locations( + &mut self, + tag_id: &String, + state_id: &String, + ) -> Result, Error> { + let url = str::replace(&self.config.locations_url, "{tag}", &tag_id); + let url = str::replace(&url, "{state}", &state_id); + let response = self.request(url)?; + response.json() + } + + pub fn get_between( + list: Vec, + from: Option, + to: Option, + inclusive_from: bool, + config: &Config, + ) -> Vec<(NaiveDateTime, T)> { + let mut timestamped = Vec::new(); + for item in list { + if let Some(res) = API::parse_timestamp(&item.timestamp(), config) { + match res { + Ok(t) => { + if let Some(to) = to { + if t > to { + continue; + } + } + let mut too_old = false; + if let Some(from) = from { + if t < from { + too_old = true; + } + } + if too_old { + if inclusive_from { + timestamped.push((t.clone(), item)); + } + break; + } + timestamped.push((t.clone(), item)); + } + Err(e) => panic!(e), + } + } else { + println!("Skipped item, did not have timestmap"); + } + } + timestamped + } + + fn request(&mut self, url: String) -> Result { + let before = Instant::now(); + let response = minreq::get(url) + .with_header("content-type", "application/json") + .with_header("x-api-key", &self.config.api_key) + .send(); + let after = Instant::now(); + let min_time = Duration::new(0, self.config.throttle * 1_000_000); + let duration = after.duration_since(before); + self.last_response_ping = (duration.as_millis() as u32).max(self.config.throttle); + if min_time > duration { + sleep(min_time - duration); + } + response + } + + fn parse_timestamp( + timestamp: &Option, + config: &Config, + ) -> Option> { + if let Some(timestamp) = ×tamp { + Some(NaiveDateTime::parse_from_str( + ×tamp, + &config.timestamp_format, + )) + } else { + None + } + } +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..a7b9e23 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,32 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Config { + pub api_key: String, + + pub tags_url: String, + pub states_url: String, + pub locations_url: String, + + pub timestamp_format: String, + pub between_format: String, + + pub throttle: u32, +} + +impl Default for Config { + fn default() -> Config { + Config { + api_key: "E3GrOiQAnY61BP623XXzt9Fo87A1IQrS1FFzD57P".to_owned(), + + tags_url: "https://platform.yepzon.com/tags".to_owned(), + states_url: "https://platform.yepzon.com/tags/{tag}/states".to_owned(), + locations_url: "https://platform.yepzon.com/tags/{tag}/locations/{state}".to_owned(), + + timestamp_format: "%Y-%m-%dT%H:%M:%S%.fZ".to_owned(), + between_format: "%d.%m.%Y %H:%M:%S".to_owned(), + + throttle: 110, + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..f8b1731 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,56 @@ +mod api; +mod config; + +use api::API; +use chrono::{NaiveDateTime, ParseError}; +use config::Config; +use std::fs::File; +use std::io::prelude::*; + +fn main() { + let config = Config::default(); + + let mut file = File::create("config.toml").unwrap(); + file.write_all(&toml::to_vec(&config).unwrap()).ok(); +} + +fn run(config: &Config, from: Option, to: Option) -> Result<(), ParseError> { + let mut api = API::new(config.clone()); + + let from = get_opt(from.map(|f| NaiveDateTime::parse_from_str(&f, &config.between_format)))?; + let to = get_opt(to.map(|t| NaiveDateTime::parse_from_str(&t, &config.between_format)))?; + + let tags = api.get_tags().unwrap(); + let first_tag = tags[0].id.clone().unwrap(); + let state_list = api.get_states(&first_tag).unwrap(); + + let states = API::get_between(state_list.clone(), from, to, true, &config); + let len = states.len(); + + let mut locations = Vec::new(); + for (idx, (_, state)) in states.iter().enumerate() { + println!("Expected {}s left", exp_time(&api, (len - idx) as u32)); + let mut location_list = api + .get_locations(&first_tag, state.id.as_ref().unwrap()) + .unwrap(); + locations.append(&mut location_list); + } + + dbg!(API::get_between(locations, from, to, false, &config)); + Ok(()) +} + +fn get_opt(option: Option>) -> Result, B> { + if let Some(res) = option { + match res { + Ok(a) => Ok(Some(a)), + Err(b) => Err(b), + } + } else { + Ok(None) + } +} + +fn exp_time(api: &API, reqs_left: u32) -> f32 { + (reqs_left * api.last_response_ping) as f32 / 1000. +}