mirror of
https://forge.katzen.cafe/schrottkatze/nix-configs.git
synced 2024-12-22 01:25:52 +01:00
continue working on traveldings (get live checkin thing working maybe??)
This commit is contained in:
parent
258d4639d7
commit
bd3674accf
5 changed files with 274 additions and 30 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1722,6 +1722,7 @@ dependencies = [
|
|||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ serde = { version = "1.0.209", features = ["derive"] }
|
|||
serde_json = "1.0.128"
|
||||
reqwest = {version = "0.12.7", default-features = false, features = ["rustls-tls", "charset", "http2"]}
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
thiserror = "1"
|
||||
anyhow = "1"
|
||||
chrono = { version = "0.4", features = ["serde"]}
|
||||
clap = { version = "4.5", features = ["derive"]}
|
||||
|
|
|
@ -1,9 +1,182 @@
|
|||
use crate::traewelling::TraewellingClient;
|
||||
use std::time::Duration;
|
||||
|
||||
use chrono::Local;
|
||||
use reqwest::StatusCode;
|
||||
use serde::Serialize;
|
||||
use tokio::time::sleep;
|
||||
|
||||
use crate::traewelling::{
|
||||
model::{JsonableData, Status, StopJourneyPart},
|
||||
RequestErr, TraewellingClient,
|
||||
};
|
||||
|
||||
pub async fn get_current_journey() -> anyhow::Result<()> {
|
||||
let client = TraewellingClient::new()?;
|
||||
|
||||
println!("active: {:#?}", client.get_active_checkin().await?);
|
||||
let mut state;
|
||||
let mut cur_active_checkin = None;
|
||||
|
||||
loop {
|
||||
match client.get_active_checkin().await {
|
||||
Ok(status) => {
|
||||
cur_active_checkin = Some(status);
|
||||
state = State::Live;
|
||||
}
|
||||
Err(err) => {
|
||||
if err == RequestErr::WithStatus(StatusCode::NOT_FOUND) {
|
||||
state = State::NoCheckin;
|
||||
cur_active_checkin = None;
|
||||
} else {
|
||||
state = State::NoConnectionOrSomethingElseDoesntWork;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match (state, &cur_active_checkin) {
|
||||
(State::Live | State::NoConnectionOrSomethingElseDoesntWork, Some(status)) => {
|
||||
let live = state == State::Live;
|
||||
let out = CurrentJourneyOutput::new(&status, live);
|
||||
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string(&out)
|
||||
.expect("serde should not make you sad but it does because it's serde")
|
||||
);
|
||||
sleep(Duration::from_secs(20)).await;
|
||||
}
|
||||
(_, None) | (State::NoCheckin, Some(_)) => {
|
||||
println!("null");
|
||||
sleep(Duration::from_secs(60)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
enum State {
|
||||
Live,
|
||||
NoConnectionOrSomethingElseDoesntWork,
|
||||
NoCheckin,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct CurrentJourneyOutput {
|
||||
live: bool,
|
||||
// Journey progress, 0.0-1.0
|
||||
progress: Option<f32>,
|
||||
time_left: Option<i64>,
|
||||
icon: String,
|
||||
|
||||
// Invalid data received?
|
||||
departure_err: bool,
|
||||
departure_planned: Option<i64>,
|
||||
departure_real: Option<i64>,
|
||||
departure_station: String,
|
||||
departure_ril100: Option<String>,
|
||||
departure_platform_data_available: bool,
|
||||
departure_platform_planned: Option<String>,
|
||||
departure_platform_real: Option<String>,
|
||||
|
||||
// Invalid data received?
|
||||
arrival_err: bool,
|
||||
arrival_planned: Option<i64>,
|
||||
arrival_real: Option<i64>,
|
||||
arrival_station: String,
|
||||
arrival_ril100: Option<String>,
|
||||
arrival_platform_data_available: bool,
|
||||
arrival_platform_planned: Option<String>,
|
||||
arrival_platform_real: Option<String>,
|
||||
}
|
||||
|
||||
impl CurrentJourneyOutput {
|
||||
fn new(checkin: &Status, live: bool) -> Self {
|
||||
let JsonableData {
|
||||
time_err: departure_err,
|
||||
time_planned: departure_planned,
|
||||
time_real: departure_real,
|
||||
station: departure_station,
|
||||
ril100: departure_ril100,
|
||||
platform_data_available: departure_platform_data_available,
|
||||
platform_planned: departure_platform_planned,
|
||||
platform_real: departure_platform_real,
|
||||
} = checkin.train.origin.get_time_data(StopJourneyPart::Origin);
|
||||
let JsonableData {
|
||||
time_err: arrival_err,
|
||||
time_planned: arrival_planned,
|
||||
time_real: arrival_real,
|
||||
station: arrival_station,
|
||||
ril100: arrival_ril100,
|
||||
platform_data_available: arrival_platform_data_available,
|
||||
platform_planned: arrival_platform_planned,
|
||||
platform_real: arrival_platform_real,
|
||||
} = checkin
|
||||
.train
|
||||
.destination
|
||||
.get_time_data(StopJourneyPart::Destination);
|
||||
|
||||
let (progress, time_left) = if !departure_err && !arrival_err {
|
||||
let departure = departure_real.unwrap_or(departure_planned.unwrap());
|
||||
let arrival = arrival_real.unwrap_or(arrival_planned.unwrap());
|
||||
let dur = arrival - departure;
|
||||
|
||||
let now = Local::now().timestamp();
|
||||
|
||||
let progress = ((now - departure) as f32) / dur as f32;
|
||||
let time_left = arrival - now;
|
||||
|
||||
(Some(progress), Some(time_left))
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
let icon = match checkin.train.category.as_str() {
|
||||
"nationalExpress" | "national" => "longDistanceTrans",
|
||||
"regionalExp" | "regional" => "regionalTrans",
|
||||
"suburban" => "localTrans",
|
||||
"subway" => "subTrans",
|
||||
"bus" => "bus",
|
||||
"tram" => "tram",
|
||||
"ferry" => "ferry",
|
||||
_ => "other",
|
||||
}
|
||||
.to_string();
|
||||
|
||||
CurrentJourneyOutput {
|
||||
live,
|
||||
progress,
|
||||
time_left,
|
||||
icon,
|
||||
departure_err,
|
||||
departure_planned,
|
||||
departure_real,
|
||||
departure_station,
|
||||
departure_ril100,
|
||||
departure_platform_data_available,
|
||||
departure_platform_planned,
|
||||
departure_platform_real,
|
||||
arrival_err,
|
||||
arrival_planned,
|
||||
arrival_real,
|
||||
arrival_station,
|
||||
arrival_ril100,
|
||||
arrival_platform_data_available,
|
||||
arrival_platform_planned,
|
||||
arrival_platform_real,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum TransportType {
|
||||
// FV, ob jetzt NJ, IC, ICE... egal
|
||||
LongDistanceTrans,
|
||||
RegionalTrans,
|
||||
// S-bahn...
|
||||
LocalTrans,
|
||||
// U-bahn
|
||||
SubTrans,
|
||||
Bus,
|
||||
Tram,
|
||||
Ferry,
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::{fmt, fs};
|
|||
use model::{Container, Status};
|
||||
use reqwest::{
|
||||
header::{self, HeaderMap},
|
||||
Client, ClientBuilder,
|
||||
Client, ClientBuilder, StatusCode,
|
||||
};
|
||||
|
||||
const KEY_PATH: &str = "/home/jade/Docs/traveldings-key";
|
||||
|
@ -19,7 +19,6 @@ impl TraewellingClient {
|
|||
let mut headers = HeaderMap::new();
|
||||
let token = fs::read_to_string(KEY_PATH)?;
|
||||
let key = header::HeaderValue::from_str(&format!("Bearer {token}"))?;
|
||||
println!("meow");
|
||||
headers.insert("Authorization", key);
|
||||
headers.insert(
|
||||
header::ACCEPT,
|
||||
|
@ -33,16 +32,17 @@ impl TraewellingClient {
|
|||
})
|
||||
}
|
||||
|
||||
pub async fn get_active_checkin(&self) -> anyhow::Result<Status> {
|
||||
let txt = self
|
||||
pub async fn get_active_checkin(&self) -> Result<Status, RequestErr> {
|
||||
let res = self
|
||||
.client
|
||||
.get(Self::fmt_url("user/statuses/active"))
|
||||
.send()
|
||||
.await?
|
||||
.text()
|
||||
.await?;
|
||||
if res.status() != StatusCode::OK {
|
||||
return Err(RequestErr::WithStatus(res.status()));
|
||||
}
|
||||
|
||||
println!("{txt}");
|
||||
let txt = res.text().await?;
|
||||
|
||||
let res: Container<Status> = serde_json::de::from_str(&txt)?;
|
||||
Ok(res.data)
|
||||
|
@ -53,4 +53,35 @@ impl TraewellingClient {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug, PartialEq, Eq)]
|
||||
pub enum RequestErr {
|
||||
#[error("Couldn't deserialize the json :(")]
|
||||
DeserializationError,
|
||||
#[error("an error related to connect happened!!")]
|
||||
RelatedToConnect,
|
||||
#[error("error haz status: {0}")]
|
||||
WithStatus(StatusCode),
|
||||
#[error("fuck if i know what went wrong :333 am silly ")]
|
||||
Other,
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for RequestErr {
|
||||
fn from(value: serde_json::Error) -> Self {
|
||||
eprintln!("serde error: {value:?}");
|
||||
Self::DeserializationError
|
||||
}
|
||||
}
|
||||
|
||||
impl From<reqwest::Error> for RequestErr {
|
||||
fn from(value: reqwest::Error) -> Self {
|
||||
if let Some(status) = value.status() {
|
||||
Self::WithStatus(status)
|
||||
} else if value.is_connect() {
|
||||
Self::RelatedToConnect
|
||||
} else {
|
||||
Self::Other
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod model;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use chrono::{DateTime, FixedOffset};
|
||||
use chrono::{DateTime, FixedOffset, Timelike};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
|
@ -9,39 +9,77 @@ pub struct Container<D> {
|
|||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Status {
|
||||
train: TransportResource,
|
||||
pub train: TransportResource,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TransportResource {
|
||||
category: String,
|
||||
line_name: String,
|
||||
distance: u32,
|
||||
duration: u32,
|
||||
operator: OperatorResource,
|
||||
origin: StopOverResource,
|
||||
destination: StopOverResource,
|
||||
pub category: String,
|
||||
pub line_name: String,
|
||||
pub distance: u32,
|
||||
pub duration: u32,
|
||||
pub operator: Option<OperatorResource>,
|
||||
pub origin: StopOverResource,
|
||||
pub destination: StopOverResource,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StopOverResource {
|
||||
name: String,
|
||||
ril_identifier: Option<String>,
|
||||
arrival: Option<DateTime<FixedOffset>>,
|
||||
arrival_planned: Option<DateTime<FixedOffset>>,
|
||||
arrival_real: Option<DateTime<FixedOffset>>,
|
||||
departure: Option<DateTime<FixedOffset>>,
|
||||
departure_planned: Option<DateTime<FixedOffset>>,
|
||||
departure_real: Option<DateTime<FixedOffset>>,
|
||||
platform: Option<String>,
|
||||
departure_platform_planned: Option<String>,
|
||||
departure_platform_real: Option<String>,
|
||||
pub name: String,
|
||||
pub ril_identifier: Option<String>,
|
||||
pub arrival_planned: Option<DateTime<FixedOffset>>,
|
||||
pub arrival_real: Option<DateTime<FixedOffset>>,
|
||||
pub departure_planned: Option<DateTime<FixedOffset>>,
|
||||
pub departure_real: Option<DateTime<FixedOffset>>,
|
||||
pub platform: Option<String>,
|
||||
pub departure_platform_planned: Option<String>,
|
||||
pub departure_platform_real: Option<String>,
|
||||
}
|
||||
|
||||
// ????
|
||||
pub struct JsonableData {
|
||||
pub time_err: bool,
|
||||
pub time_planned: Option<i64>,
|
||||
pub time_real: Option<i64>,
|
||||
pub station: String,
|
||||
pub ril100: Option<String>,
|
||||
pub platform_data_available: bool,
|
||||
pub platform_planned: Option<String>,
|
||||
pub platform_real: Option<String>,
|
||||
}
|
||||
|
||||
// What the meaning of the stop in the journey is
|
||||
pub enum StopJourneyPart {
|
||||
Origin,
|
||||
Destination,
|
||||
}
|
||||
impl StopOverResource {
|
||||
pub fn get_time_data(&self, journey_part: StopJourneyPart) -> JsonableData {
|
||||
let (time_planned, time_real) = match journey_part {
|
||||
StopJourneyPart::Origin => (self.departure_planned, self.departure_real),
|
||||
StopJourneyPart::Destination => (self.arrival_planned, self.arrival_real),
|
||||
};
|
||||
|
||||
let time_err = time_planned == None;
|
||||
|
||||
JsonableData {
|
||||
time_err,
|
||||
time_planned: time_planned.map(|ts| ts.timestamp()),
|
||||
time_real: time_real.map(|ts| ts.timestamp()),
|
||||
station: self.name.clone(),
|
||||
ril100: self.ril_identifier.clone(),
|
||||
platform_data_available: self.departure_platform_planned.is_none()
|
||||
|| self.departure_platform_real.is_none(),
|
||||
platform_planned: self.departure_platform_planned.clone(),
|
||||
platform_real: self.departure_platform_real.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct OperatorResource {
|
||||
name: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue