diff --git a/Cargo.lock b/Cargo.lock index 73adefe..b31b96e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -735,6 +735,19 @@ dependencies = [ "typenum", ] +[[package]] +name = "cta-api" +version = "0.2.0" +source = "registry+https://git.ahines.net/joeyahines/_cargo-index.git" +checksum = "7f85231a3e0c11c63ef5a5675e7f6e16f5eac05f67a0dd94fc0984dd06ba250b" +dependencies = [ + "reqwest 0.12.15", + "serde", + "serde_json", + "thiserror 2.0.12", + "url", +] + [[package]] name = "ctr" version = "0.9.2" @@ -1080,13 +1093,14 @@ dependencies = [ [[package]] name = "fren" -version = "1.3.1" +version = "1.4.0" dependencies = [ "axum 0.8.1", "base64 0.22.1", "chrono", "chrono-tz 0.10.1", "config", + "cta-api", "emojis", "j_db", "json", @@ -1098,7 +1112,7 @@ dependencies = [ "raas_types", "rand 0.9.0", "regex", - "reqwest 0.12.9", + "reqwest 0.12.15", "serde", "serde_json", "sha3", @@ -1923,9 +1937,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", @@ -2430,7 +2444,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror 2.0.9", + "thiserror 2.0.12", "ucd-trie", ] @@ -2785,7 +2799,7 @@ dependencies = [ "rustc-hash 2.1.1", "rustls 0.23.20", "socket2", - "thiserror 2.0.9", + "thiserror 2.0.12", "tokio", "tracing", ] @@ -2804,7 +2818,7 @@ dependencies = [ "rustls 0.23.20", "rustls-pki-types", "slab", - "thiserror 2.0.9", + "thiserror 2.0.12", "tinyvec", "tracing", "web-time", @@ -3022,9 +3036,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.9" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" dependencies = [ "base64 0.22.1", "bytes", @@ -3061,6 +3075,7 @@ dependencies = [ "tokio-native-tls", "tokio-rustls 0.26.1", "tokio-util", + "tower 0.5.2", "tower-service", "url", "wasm-bindgen", @@ -3408,9 +3423,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.216" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] @@ -3447,9 +3462,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.216" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", @@ -3458,9 +3473,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.134" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", @@ -3732,7 +3747,7 @@ dependencies = [ "parking_lot 0.12.3", "pin-project", "rand 0.9.0", - "reqwest 0.12.9", + "reqwest 0.12.15", "ringbuf", "rubato", "rusty_pool", @@ -3793,7 +3808,7 @@ dependencies = [ "futures-util", "hls_m3u8", "patricia_tree", - "reqwest 0.12.9", + "reqwest 0.12.15", "tokio", "tracing", "url", @@ -4182,11 +4197,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.9" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.9", + "thiserror-impl 2.0.12", ] [[package]] @@ -4202,9 +4217,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.9" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", @@ -4696,7 +4711,7 @@ dependencies = [ "rustls 0.23.20", "rustls-pki-types", "sha1", - "thiserror 2.0.9", + "thiserror 2.0.12", "url", "utf-8", ] @@ -4979,20 +4994,21 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", @@ -5004,9 +5020,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.49" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", @@ -5017,9 +5033,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5027,9 +5043,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -5040,9 +5056,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-streams" @@ -5059,9 +5078,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -5154,33 +5173,38 @@ dependencies = [ ] [[package]] -name = "windows-registry" -version = "0.2.0" +name = "windows-link" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ "windows-result", "windows-strings", - "windows-targets 0.52.6", + "windows-targets 0.53.0", ] [[package]] name = "windows-result" -version = "0.2.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" dependencies = [ - "windows-targets 0.52.6", + "windows-link", ] [[package]] name = "windows-strings" -version = "0.1.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" dependencies = [ - "windows-result", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -5234,13 +5258,29 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -5253,6 +5293,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -5265,6 +5311,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -5277,12 +5329,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -5295,6 +5359,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -5307,6 +5377,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -5319,6 +5395,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -5331,6 +5413,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" version = "0.6.20" diff --git a/Cargo.toml b/Cargo.toml index 98bf3ee..29fd9ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fren" -version = "1.3.1" +version = "1.4.0" edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -8,7 +8,7 @@ edition = "2024" [dependencies] config = "0.15.11" structopt = "0.3.26" -reqwest = { version = "0.12.5", features = ["json", "multipart"] } +reqwest = { version = "0.12.15", features = ["json", "multipart"] } serde = "1.0.195" rand = "0.9.0" tera = "1.19.1" @@ -30,6 +30,7 @@ emojis = "0.6.2" poise = "0.6.1" tracing-subscriber = "0.3.19" log = "0.4.26" +cta-api = { version = "0.2.0", registry = "ahines"} [dependencies.tokio] version = "1.35.1" diff --git a/src/config.rs b/src/config.rs index a60692f..8d706a1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,6 +2,7 @@ use crate::album_manager::AlbumManager; use crate::error::Error; use crate::migrations::{CURRENT_DB_VERSION, do_migration}; use config::{Config, File}; +use cta_api::CTAClient; use j_db::database::Database; use j_db::metadata::DBMetadata; use poise::serenity_prelude::model::id::ChannelId; @@ -42,6 +43,7 @@ pub struct BotConfig { pub toys: Vec, pub effect_role_duration: i64, pub raas_server: String, + pub cta_key: String, pub picox: PicOxConfig, } @@ -75,6 +77,7 @@ pub struct GlobalData { pub bot_state: Mutex, pub db: Database, pub picox: AlbumManager, + pub cta: Mutex, pub speak_lock: Mutex<()>, } @@ -100,6 +103,7 @@ impl GlobalData { db, cfg: cfg.clone(), picox: AlbumManager::new(cfg.picox.api_base_url, &cfg.picox.token), + cta: Mutex::new(CTAClient::new(cfg.cta_key)), speak_lock: Default::default(), }) } diff --git a/src/discord/mod.rs b/src/discord/mod.rs index ed63c12..5a1b40b 100644 --- a/src/discord/mod.rs +++ b/src/discord/mod.rs @@ -10,6 +10,7 @@ mod little_fren; mod motivate; mod role; pub(crate) mod shop; +mod transit; pub(crate) mod voices; use crate::config::GlobalData; @@ -314,6 +315,7 @@ pub async fn run_bot(global_data: GlobalData) { shop::sell_item(), shop::shop(), shop::use_item(), + transit::cta_bets(), voices::list_voices(), voices::list_words(), voices::say(), @@ -347,7 +349,8 @@ pub async fn run_bot(global_data: GlobalData) { let intents = serenity::GatewayIntents::non_privileged() | serenity::GatewayIntents::DIRECT_MESSAGES | serenity::GatewayIntents::GUILDS - | serenity::GatewayIntents::MESSAGE_CONTENT; + | serenity::GatewayIntents::MESSAGE_CONTENT + | serenity::GatewayIntents::GUILD_MESSAGE_REACTIONS; let mut client = serenity::ClientBuilder::new(token, intents) .framework(framework) diff --git a/src/discord/transit.rs b/src/discord/transit.rs new file mode 100644 index 0000000..a34b323 --- /dev/null +++ b/src/discord/transit.rs @@ -0,0 +1,273 @@ +use crate::discord::Context; +use crate::error::Error; +use crate::user::User; +use chrono::{DateTime, TimeZone, Utc}; +use cta_api::models::eta::EtaRequest; +use log::debug; +use poise::serenity_prelude::{ + FormattedTimestampStyle, Mentionable, MessageBuilder, ReactionType, Timestamp, UserId, +}; +use std::collections::{HashMap, HashSet}; +use std::time::Duration; + +const CALI_STATION_ID: u32 = 30111; + +#[derive(Debug, PartialOrd, PartialEq, Copy, Clone)] +pub enum CtaBetType { + OnTime, + Early, + Late, +} + +impl CtaBetType { + pub fn emoji(&self) -> String { + match self { + CtaBetType::OnTime => "🟢".to_string(), + CtaBetType::Early => "🟡".to_string(), + CtaBetType::Late => "🔴".to_string(), + } + } + + pub fn outcome(est_time: DateTime, actual_time: DateTime) -> Self { + let diff = est_time - actual_time; + if diff.num_seconds().abs() <= 1 { + CtaBetType::OnTime + } else if diff.num_seconds().is_positive() { + CtaBetType::Early + } else { + CtaBetType::Late + } + } + + pub fn payout_factor(&self) -> f32 { + match self { + CtaBetType::OnTime => 10.0, + CtaBetType::Early => 2.0, + CtaBetType::Late => 1.5, + } + } +} + +/// Bet on the CTA +#[poise::command(prefix_command, category = "Transit", guild_only)] +pub async fn cta_bets(ctx: Context<'_>) -> Result<(), Error> { + let mut cta_api = ctx.data().cta.lock().await; + + let eta = cta_api + .fetch_eta(EtaRequest { + mapid: None, + stpid: Some(CALI_STATION_ID), + max: None, + rt: None, + }) + .await?; + + if eta.eta.is_empty() { + ctx.reply("No trains found, either the CTA is closed or borked. 50/50 shot really") + .await?; + return Ok(()); + } + + let next_train_eta = eta.eta.iter().find(|eta| eta.is_app == "0").unwrap(); + + let arrival_time = + chrono::NaiveDateTime::parse_from_str(&next_train_eta.arr_t, "%Y-%m-%dT%H:%M:%S").unwrap(); + let arrival_time = chrono_tz::America::Chicago + .from_local_datetime(&arrival_time) + .unwrap(); + let arrival_time = poise::serenity_prelude::model::Timestamp::from(arrival_time.to_utc()); + + let mut msg = MessageBuilder::new(); + + msg.push_line("# It's time to bet on public transit!"); + msg.push_line("The next train to arrive to the California station on the Blue Line is:"); + msg.push_line(format!("* **Train Run Number**: {}", next_train_eta.rn)); + msg.push_line(format!( + "* **Is Train Delayed?**: {}", + next_train_eta.is_dly == "1" + )); + msg.push_line(format!( + "* **Expected Arrival Time**: {}", + poise::serenity_prelude::utils::FormattedTimestamp::new( + arrival_time, + Some(FormattedTimestampStyle::LongTime) + ) + )); + msg.push_line(""); + msg.push_line("Bets are 100 Fren Coins"); + msg.push_bold_line("Only bet once, I'll know..."); + msg.push_line("* React 🟢 if the train will be exactly on time (10x bet)"); + msg.push_line("* React 🟡 if the train will be early (2x bet)"); + msg.push_line("* React 🔴 if the train will be late (1.5x bet)"); + + let bet_msg = ctx.reply(msg.build()).await?; + let msg = bet_msg.message().await?; + + msg.react(ctx, ReactionType::Unicode(CtaBetType::OnTime.emoji())) + .await?; + msg.react(ctx, ReactionType::Unicode(CtaBetType::Early.emoji())) + .await?; + msg.react(ctx, ReactionType::Unicode(CtaBetType::Late.emoji())) + .await?; + + let mut arr_time = None; + + // Try for 30 minutes + for _ in 0..(60 * 30) { + let train_status = cta_api + .fetch_train_schedule(next_train_eta.rn.parse().unwrap()) + .await?; + let cali_eta = train_status + .eta + .iter() + .find(|schedule| { + let sta_id: u32 = schedule.stp_id.parse().unwrap(); + sta_id == CALI_STATION_ID + }) + .unwrap(); + + if cali_eta.is_app == "1" { + debug!("Train has arrived!"); + arr_time = Some(Utc::now()); + break; + } + + tokio::time::sleep(Duration::from_secs(2)).await; + } + + if arr_time.is_none() { + ctx.reply("Yeah I don't think this train is coming, oh well RIP Chicago") + .await?; + return Ok(()); + } + + let outcome = CtaBetType::outcome(*arrival_time, arr_time.unwrap()); + + let user_id = ctx.cache().current_user().id; + + let msg = bet_msg.message().await?; + let reactions_on_time = msg + .reaction_users( + ctx, + ReactionType::Unicode(CtaBetType::OnTime.emoji()), + None, + None, + ) + .await?; + let reactions_early = msg + .reaction_users( + ctx, + ReactionType::Unicode(CtaBetType::Early.emoji()), + None, + None, + ) + .await?; + let reactions_late = msg + .reaction_users( + ctx, + ReactionType::Unicode(CtaBetType::Late.emoji()), + None, + None, + ) + .await?; + + let mut bets: HashMap = HashMap::new(); + let mut punish_users: HashSet = HashSet::new(); + + add_bets( + &user_id, + &CtaBetType::OnTime, + &reactions_on_time, + &mut bets, + &mut punish_users, + ); + add_bets( + &user_id, + &CtaBetType::Late, + &reactions_late, + &mut bets, + &mut punish_users, + ); + add_bets( + &user_id, + &CtaBetType::Early, + &reactions_early, + &mut bets, + &mut punish_users, + ); + + for user in bets.keys() { + User::take_funds(&ctx.data().db, *user, 100)?; + } + + let winners: Vec = bets + .iter() + .filter(|(_, bet)| **bet == outcome) + .filter(|(user, _)| !punish_users.contains(user)) + .map(|(id, _)| *id) + .collect(); + + let actual_timestamp = Timestamp::from(arr_time.unwrap()); + ctx.reply(format!( + "# {} Train has arrived @ {}", + outcome.emoji(), + poise::serenity_prelude::utils::FormattedTimestamp::new( + actual_timestamp, + Some(FormattedTimestampStyle::LongTime) + ) + )) + .await?; + + if winners.is_empty() { + ctx.reply("Looks like no one won, too bad...").await?; + } else { + let payout = (100.0 * outcome.payout_factor()) as u32; + let mut winner_msg = MessageBuilder::new(); + winner_msg.push_line("## Congrats to our winners:"); + winner_msg.push_line(format!("They each get {} Fren Coins!", payout)); + for winner in &winners { + User::give_funds(&ctx.data().db, *winner, payout)?; + winner_msg.push_line(format!("* {}", winner.mention())); + } + + ctx.reply(winner_msg.build()).await?; + } + + if !punish_users.is_empty() { + let mut cheater_msg = MessageBuilder::new(); + + cheater_msg + .push_line("## NOW FOR YOU CHEATING FUCKS, HOPE YOU ENJOY NOT WINNING ANYTHING:"); + + for cheater in &punish_users { + cheater_msg.push_line(format!("* {}", cheater.mention())); + } + + ctx.reply(cheater_msg.build()).await?; + } + + Ok(()) +} + +fn add_bets( + bot_user: &UserId, + bet_outcome: &CtaBetType, + reactions: &Vec, + bets: &mut HashMap, + punish_users: &mut HashSet, +) { + for user in reactions { + if &user.id == bot_user { + continue; + } + + if bets.contains_key(&user.id) { + debug!("Punishing user id {}", user.id); + punish_users.insert(user.id); + continue; + } + + debug!("Adding User id {} to {}", user.id, bet_outcome.emoji()); + bets.insert(user.id, *bet_outcome); + } +} diff --git a/src/error.rs b/src/error.rs index c99f4dd..4412213 100644 --- a/src/error.rs +++ b/src/error.rs @@ -21,6 +21,7 @@ pub enum Error { CommandError(String), IoError(std::io::Error), VoiceError(VoiceError), + CTAError(cta_api::Error), } impl StdError for Error {} @@ -79,6 +80,12 @@ impl From for Error { } } +impl From for Error { + fn from(value: cta_api::Error) -> Self { + Self::CTAError(value) + } +} + impl Display for Error { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { @@ -95,6 +102,7 @@ impl Display for Error { Error::CommandError(err_msg) => write!(f, "{}", err_msg), Error::IoError(err) => write!(f, "IO Error: {}", err), Error::VoiceError(err) => write!(f, "Voice error: {}", err), + Error::CTAError(err) => write!(f, "The CTA had an error, I'm shocked: {}", err), } } } diff --git a/src/main.rs b/src/main.rs index 8ccfe2a..24f3529 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,6 +21,7 @@ static START: Once = Once::new(); #[tokio::main] async fn main() { let args: Args = Args::from_args(); + tracing_subscriber::fmt::init(); let cfg = match BotConfig::new(&args.cfg_path) { Ok(cfg) => cfg, @@ -30,8 +31,6 @@ async fn main() { } }; - tracing_subscriber::fmt::init(); - START.call_once(|| { magick_wand_genesis(); }); diff --git a/src/models/task.rs b/src/models/task.rs index ca95ab5..1ffceea 100644 --- a/src/models/task.rs +++ b/src/models/task.rs @@ -111,8 +111,12 @@ impl Task { } TaskType::CheckBirthdays => { info!("Checking Birthdays"); + + let time = + chrono_tz::America::Chicago.from_utc_datetime(&Utc::now().naive_utc()); + let todays_birthdays = - BirthdayEntry::todays_birthdays(&data.db, chrono::Utc::now().date_naive())?; + BirthdayEntry::todays_birthdays(&data.db, time.date_naive())?; for birth in todays_birthdays { if let Ok(user) = diff --git a/src/user/mod.rs b/src/user/mod.rs index e422856..0343bf2 100644 --- a/src/user/mod.rs +++ b/src/user/mod.rs @@ -97,6 +97,16 @@ impl User { Ok(()) } + pub fn take_funds(db: &Database, discord_id: UserId, amount: u32) -> Result<(), Error> { + let mut wallet = Self::get_user(db, discord_id)?; + + wallet.coin_count -= amount as i64; + + db.insert::(wallet)?; + + Ok(()) + } + pub fn give_item( db: &Database, discord_id: UserId,