From 0fe422f6097a5e9d77d81fe97a4594b73b77f99e Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Sun, 18 Oct 2020 11:51:57 +0200 Subject: [PATCH] Continue work --- Cargo.lock | 52 +++++++++++++++++++++++- Cargo.toml | 1 + src/main.rs | 107 +++++++++++++++++++++++++++++++++++++++++++++---- src/slack.rs | 2 + src/weather.rs | 88 +++++++++++----------------------------- 5 files changed, 176 insertions(+), 74 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1e68c22..4e41228 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -78,6 +78,19 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi 0.3.9", +] + [[package]] name = "core-foundation" version = "0.7.0" @@ -239,7 +252,7 @@ checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] @@ -559,6 +572,25 @@ dependencies = [ "winapi 0.3.9", ] +[[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 = "num_cpus" version = "1.13.0" @@ -929,6 +961,17 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi 0.3.9", +] + [[package]] name = "tinyvec" version = "0.3.4" @@ -1099,6 +1142,12 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wasm-bindgen" version = "0.2.68" @@ -1171,6 +1220,7 @@ checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307" name = "weatherstat" version = "0.1.0" dependencies = [ + "chrono", "dotenv", "futures-util", "reqwest", diff --git a/Cargo.toml b/Cargo.toml index 97cc540..cf35126 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Rob Watson "] edition = "2018" [dependencies] +chrono = "0.4" dotenv = "0.15.0" futures-util = "0.3.6" reqwest = { version = "0.10.8", features = ["json"] } diff --git a/src/main.rs b/src/main.rs index e9affce..f27392d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,18 +2,20 @@ mod config; mod slack; mod weather; +use chrono::Timelike; use config::ConfigUser; use dotenv::dotenv; use serde::Serialize; use slack::SlackUser; use tokio::prelude::*; +use weather::WeatherSummary; #[tokio::main] async fn main() { dotenv().ok(); let config = config::get_config().unwrap(); - println!("Got config: {:?}", config); + println!("Got config: {:?}", &config); let slack_users = slack::list_users().await.unwrap(); println!("Got {} users: {:?}", slack_users.len(), slack_users); @@ -37,13 +39,102 @@ async fn main() { } async fn set_user_status(config_user: &ConfigUser, slack_user: &SlackUser) -> Result<(), String> { + let offset = chrono::offset::FixedOffset::east(slack_user.tz_offset); + let now = chrono::offset::Utc::now().with_timezone(&offset); + let is_pm = now.hour() < 6 || now.hour() >= 18; let sum = weather::get_summary(&config_user.location).await?; - let status = format!("{} {}\u{00b0}C", sum.name, sum.temp_max); - let emoji = format!(":{}:", sum.emoji); - println!( - "Set status for user {} to: {} {:?}", - &slack_user.id, &emoji, &status - ); - Ok(slack::set_status(&slack_user.id, &status, &emoji, 0).await?) + match &sum { + WeatherSummary::Thunderstorm(_, _) + | WeatherSummary::Clouds(_, _) + | WeatherSummary::Snow(_, _) + | WeatherSummary::Rain(_, _) + | WeatherSummary::Drizzle(_, _) + | WeatherSummary::Clear(_, _) + | WeatherSummary::Atmospheric(_, _) => { + let status = status_text(&sum, is_pm); + let emoji = format!(":{}:", &emoji(&sum, is_pm)); + + println!( + "Set status for user {} to: {} {:?}", + &slack_user.id, &emoji, &status + ); + let res = slack::set_status(&slack_user.id, &status, &emoji, 0).await?; + Ok(res) + } + } +} + +fn status_text(sum: &WeatherSummary, is_pm: bool) -> String { + match sum { + WeatherSummary::Thunderstorm(temp_min, temp_max) => { + if is_pm { + format!("Thunderstorm, min {}\u{00b0}C", temp_min) + } else { + format!("Thunderstorm, max {}\u{00b0}C", temp_max) + } + } + WeatherSummary::Clouds(temp_min, temp_max) => { + if is_pm { + format!("Cloudy, min {}\u{00b0}C", temp_min) + } else { + format!("Cloudy, max {}\u{00b0}C", temp_max) + } + } + WeatherSummary::Snow(temp_min, temp_max) => { + if is_pm { + format!("Snow, min {}\u{00b0}C", temp_min) + } else { + format!("Snow, max {}\u{00b0}C", temp_max) + } + } + WeatherSummary::Rain(temp_min, temp_max) => { + if is_pm { + format!("Rain, min {}\u{00b0}C", temp_min) + } else { + format!("Rain, max {}\u{00b0}C", temp_max) + } + } + WeatherSummary::Drizzle(temp_min, temp_max) => { + if is_pm { + format!("Drizzle, min {}\u{00b0}C", temp_min) + } else { + format!("Drizzle, max {}\u{00b0}C", temp_max) + } + } + WeatherSummary::Clear(temp_min, temp_max) => { + if is_pm { + format!("Clear, min {}\u{00b0}C", temp_min) + } else { + format!("Sunny, max {}\u{00b0}C", temp_max) + } + } + WeatherSummary::Atmospheric(temp_min, temp_max) => { + if is_pm { + format!("Foggy, min {}\u{00b0}C", temp_min) + } else { + format!("Foggy, max {}\u{00b0}C", temp_max) + } + } + } +} + +fn emoji(sum: &WeatherSummary, is_pm: bool) -> String { + let emoji = match sum { + WeatherSummary::Thunderstorm(_, _) => "thunder_cloud_and_rain", + WeatherSummary::Clouds(_, _) => "cloud", + WeatherSummary::Snow(_, _) => "snow_cloud", + WeatherSummary::Rain(_, _) => "rain_cloud", + WeatherSummary::Drizzle(_, _) => "rain_cloud", + WeatherSummary::Clear(_, _) => { + if is_pm { + "crescent_moon" + } else { + "sunny" + } + } + WeatherSummary::Atmospheric(_, _) => "foggy", + }; + + emoji.to_string() } diff --git a/src/slack.rs b/src/slack.rs index 5306038..58ed381 100644 --- a/src/slack.rs +++ b/src/slack.rs @@ -51,6 +51,8 @@ pub async fn set_status( value: None, }; + // Returns invalid_user unless the Slack app was installed by a team admin + // AND the Slack account is on a paid plan. :cry: slack_api::users_profile::set(&client, &api_key, &set_req) .map_err(|e| e.to_string()) .await?; diff --git a/src/weather.rs b/src/weather.rs index 17ec6e1..a63b255 100644 --- a/src/weather.rs +++ b/src/weather.rs @@ -25,77 +25,30 @@ struct APIResponse { } #[derive(Debug)] -pub struct WeatherSummary { - pub name: String, - pub emoji: String, - pub temp: i32, - pub temp_min: i32, - pub temp_max: i32, +pub enum WeatherSummary { + Thunderstorm(i32, i32), + Clouds(i32, i32), + Snow(i32, i32), + Rain(i32, i32), + Drizzle(i32, i32), + Clear(i32, i32), + Atmospheric(i32, i32), } impl From for WeatherSummary { fn from(resp: APIResponse) -> Self { - let temp = resp.main.temp as i32; let temp_min = resp.main.temp_min as i32; let temp_max = resp.main.temp_max as i32; match resp.weather[0].main.as_ref() { - "Thunderstorm" => Self { - name: "Thunderstorm".to_string(), - emoji: "thunder_cloud_and_rain".to_string(), - temp, - temp_min, - temp_max, - }, - "Clouds" => Self { - name: "Cloudy".to_string(), - emoji: "cloud".to_string(), - temp, - temp_min, - temp_max, - }, - "Snow" => Self { - name: "Snow".to_string(), - emoji: "snow_cloud".to_string(), - temp, - temp_min, - temp_max, - }, - "Rain" => Self { - name: "Rain".to_string(), - emoji: "rain_cloud".to_string(), - temp, - temp_min, - temp_max, - }, - "Drizzle" => Self { - name: "Drizzle".to_string(), - emoji: "rain_cloud".to_string(), - temp, - temp_min, - temp_max, - }, - "Clear" => Self { - name: "Clear".to_string(), - emoji: "sunny".to_string(), - temp, - temp_min, - temp_max, - }, - "Atmosphere" => Self { - name: "Atmospheric".to_string(), - emoji: "foggy".to_string(), - temp, - temp_min, - temp_max, - }, - _ => Self { - name: "Unknown".to_string(), - emoji: "cloud".to_string(), - temp, - temp_min, - temp_max, - }, + "Thunderstorm" => Self::Thunderstorm(temp_min, temp_max), + "Clouds" => Self::Clouds(temp_min, temp_max), + "Snow" => Self::Snow(temp_min, temp_max), + "Rain" => Self::Rain(temp_min, temp_max), + "Drizzle" => Self::Drizzle(temp_min, temp_max), + "Clear" => Self::Clear(temp_min, temp_max), + "Atmosphere" => Self::Atmospheric(temp_min, temp_max), + _ => panic!("unrecognized weather type"), } } } @@ -108,14 +61,19 @@ pub async fn get_summary(loc: &str) -> Result { ); let resp = reqwest::get(&url).map_err(|e| e.to_string()).await?; + + //println!("{}", resp.text().map_err(|e| e.to_string()).await?); + if !resp.status().is_success() { return Err(format!("Weather API response: {}", resp.status()).into()); } - let body = resp + let api_response = resp .json::() .map_err(|e| e.to_string()) .await?; - Ok(body.into()) + println!("Got API resp: {:?}", api_response); + + Ok(api_response.into()) }