diff --git a/Cargo.lock b/Cargo.lock index 2aa1c75..17c5b84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,7 +28,7 @@ dependencies = [ [[package]] name = "am-rate-bot" -version = "0.3.0" +version = "0.3.1" dependencies = [ "argh", "env_logger", diff --git a/Cargo.toml b/Cargo.toml index 03ee1f0..daaf403 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "am-rate-bot" -version = "0.3.0" +version = "0.3.1" edition = "2021" authors = ["lucky"] diff --git a/src/collector.rs b/src/collector.rs index 29868d0..d154ea2 100644 --- a/src/collector.rs +++ b/src/collector.rs @@ -1,7 +1,7 @@ use crate::sources; use crate::sources::{ acba, aeb, ameria, amio, ardshin, arm_swiss, armsoft, artsakh, byblos, cba, converse, evoca, - fast, idbank, ineco, lsoft, mellat, unibank, vtb_am, Currency, RateType, Source, + fast, idbank, ineco, lsoft, mellat, moex, unibank, vtb_am, Currency, RateType, Source, SourceAphenaTrait, SourceCashUrlTrait, SourceSingleUrlTrait, }; use reqwest::Client; @@ -73,6 +73,7 @@ async fn collect(client: &Client, source: Source) -> Result, Error> { Source::Amio => collect_amio(&client).await?, Source::Byblos => collect_byblos(&client).await?, Source::IdBank => collect_idbank(&client).await?, + Source::MOEX => collect_moex(&client).await?, }; Ok(rates) } @@ -359,6 +360,41 @@ async fn collect_idbank(client: &Client) -> Result, Error> { Ok(rates) } +async fn collect_moex(client: &Client) -> Result, Error> { + let resp: moex::Response = moex::Response::get_rates(&client).await?; + let boardid = "CETS"; + let facevalue = resp + .securities + .data + .iter() + .filter(|v| v.0 == boardid) + .map(|v| v.1) + .next() + .expect("panic"); + let last = resp + .marketdata + .data + .iter() + .filter(|v| v.0 == boardid) + .filter_map(|v| v.1) + .next(); + let Some(last) = last else { + return Err(Error::NoRates); + }; + let mut rates = vec![]; + if last == 0.0 { + return Ok(rates); + } + let rate = facevalue / last; + rates.push(Rate { + currency: Currency::rub(), + rate_type: RateType::NoCash, + buy: rate, + sell: rate, + }); + Ok(rates) +} + mod tests { use super::*; use crate::sources::tests::build_client; @@ -482,4 +518,11 @@ mod tests { collect(&c, Source::IdBank).await?; Ok(()) } + + #[tokio::test] + async fn test_collect_moex() -> Result<(), Box> { + let c = build_client()?; + collect(&c, Source::MOEX).await?; + Ok(()) + } } diff --git a/src/generator.rs b/src/generator.rs index 12fd5f7..5b3fe0f 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -132,7 +132,7 @@ pub fn generate_table( table.sort_by(|a, b| sort(a.rate, b.rate)); let best_rate = table .iter() - .filter(|r| r.source != Source::CBA) + .filter(|r| ![Source::CBA, Source::MOEX].contains(&r.source)) .map(|r| r.rate) .next() .unwrap_or_default(); diff --git a/src/sources/mod.rs b/src/sources/mod.rs index fb7e226..305bcda 100644 --- a/src/sources/mod.rs +++ b/src/sources/mod.rs @@ -20,6 +20,7 @@ pub mod idbank; pub mod ineco; pub mod lsoft; pub mod mellat; +pub mod moex; pub mod unibank; mod utils; pub mod vtb_am; @@ -55,6 +56,7 @@ pub trait SourceCashUrlTrait { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum Source { CBA, + MOEX, Acba, Ameria, Ardshin, @@ -77,6 +79,7 @@ impl Source { pub fn iter() -> impl Iterator { [ Self::CBA, + Self::MOEX, Self::Acba, Self::Ameria, Self::Ardshin, @@ -100,7 +103,7 @@ impl Source { pub fn prefix(&self) -> &str { match self { - Self::CBA => "#", + Self::CBA | Self::MOEX => "#", _ => "*", } } @@ -110,6 +113,7 @@ impl Display for Source { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let s: String = match self { Source::CBA => "CBA".into(), + Source::MOEX => "MOEX'".into(), Source::Acba => "Acba".into(), Source::Ameria => "Ameria".into(), Source::Ardshin => "Ardshin".into(), @@ -377,4 +381,11 @@ pub(crate) mod tests { let _: idbank::Response = idbank::Response::get_rates(&c).await?; Ok(()) } + + #[tokio::test] + async fn test_moex() -> Result<(), Box> { + let c = build_client()?; + let _: moex::Response = moex::Response::get_rates(&c).await?; + Ok(()) + } } diff --git a/src/sources/moex.rs b/src/sources/moex.rs new file mode 100644 index 0000000..ffecd06 --- /dev/null +++ b/src/sources/moex.rs @@ -0,0 +1,54 @@ +use crate::sources::SourceSingleUrlTrait; +use serde::Deserialize; + +pub const API_URL: &str = "https://iss.moex.com/iss/engines/currency/markets/selt/securities/AMDRUB_TOM.json?iss.meta=off&marketdata.columns=BOARDID,LAST,VALTODAY_USD&securities.columns=BOARDID,FACEVALUE"; + +#[derive(Debug, Deserialize)] +pub struct Response { + pub dataversion: DataVersion, + pub marketdata: MarketData, + pub marketdata_yields: MarketDataYields, + pub securities: Securities, +} + +#[derive(Debug, Deserialize)] +pub struct DataVersion { + pub columns: Vec, + pub data: Vec, +} + +#[derive(Debug, Deserialize)] +pub struct DataVersionData(pub i32, pub i64); + +#[derive(Debug, Deserialize)] +pub struct MarketData { + pub columns: Vec, + pub data: Vec, +} + +#[derive(Debug, Deserialize)] +pub struct MarketDataData(pub String, pub Option, pub Option); + +#[derive(Debug, Deserialize)] +pub struct MarketDataYields { + pub columns: Vec, + pub data: Vec, +} + +#[derive(Debug, Deserialize)] +pub struct MarketDataYieldsData(pub String, pub String); + +#[derive(Debug, Deserialize)] +pub struct Securities { + pub columns: Vec, + pub data: Vec, +} + +#[derive(Debug, Deserialize)] +pub struct SecuritiesData(pub String, pub f64); + +impl SourceSingleUrlTrait for Response { + fn url() -> String { + API_URL.into() + } +}