Continue work

This commit is contained in:
Rob Watson 2020-10-18 11:51:57 +02:00
parent bc2e1d0f40
commit 0fe422f609
5 changed files with 176 additions and 74 deletions

52
Cargo.lock generated
View File

@ -78,6 +78,19 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 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]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.7.0" version = "0.7.0"
@ -239,7 +252,7 @@ checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"wasi", "wasi 0.9.0+wasi-snapshot-preview1",
] ]
[[package]] [[package]]
@ -559,6 +572,25 @@ dependencies = [
"winapi 0.3.9", "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]] [[package]]
name = "num_cpus" name = "num_cpus"
version = "1.13.0" version = "1.13.0"
@ -929,6 +961,17 @@ dependencies = [
"winapi 0.3.9", "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]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "0.3.4" 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" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 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]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.68" version = "0.2.68"
@ -1171,6 +1220,7 @@ checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307"
name = "weatherstat" name = "weatherstat"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"chrono",
"dotenv", "dotenv",
"futures-util", "futures-util",
"reqwest", "reqwest",

View File

@ -5,6 +5,7 @@ authors = ["Rob Watson <rfwatson@users.noreply.github.com>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
chrono = "0.4"
dotenv = "0.15.0" dotenv = "0.15.0"
futures-util = "0.3.6" futures-util = "0.3.6"
reqwest = { version = "0.10.8", features = ["json"] } reqwest = { version = "0.10.8", features = ["json"] }

View File

@ -2,18 +2,20 @@ mod config;
mod slack; mod slack;
mod weather; mod weather;
use chrono::Timelike;
use config::ConfigUser; use config::ConfigUser;
use dotenv::dotenv; use dotenv::dotenv;
use serde::Serialize; use serde::Serialize;
use slack::SlackUser; use slack::SlackUser;
use tokio::prelude::*; use tokio::prelude::*;
use weather::WeatherSummary;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
dotenv().ok(); dotenv().ok();
let config = config::get_config().unwrap(); let config = config::get_config().unwrap();
println!("Got config: {:?}", config); println!("Got config: {:?}", &config);
let slack_users = slack::list_users().await.unwrap(); let slack_users = slack::list_users().await.unwrap();
println!("Got {} users: {:?}", slack_users.len(), slack_users); 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> { 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 sum = weather::get_summary(&config_user.location).await?;
let status = format!("{} {}\u{00b0}C", sum.name, sum.temp_max); match &sum {
let emoji = format!(":{}:", sum.emoji); 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!( println!(
"Set status for user {} to: {} {:?}", "Set status for user {} to: {} {:?}",
&slack_user.id, &emoji, &status &slack_user.id, &emoji, &status
); );
Ok(slack::set_status(&slack_user.id, &status, &emoji, 0).await?) 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()
} }

View File

@ -51,6 +51,8 @@ pub async fn set_status(
value: None, 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) slack_api::users_profile::set(&client, &api_key, &set_req)
.map_err(|e| e.to_string()) .map_err(|e| e.to_string())
.await?; .await?;

View File

@ -25,77 +25,30 @@ struct APIResponse {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct WeatherSummary { pub enum WeatherSummary {
pub name: String, Thunderstorm(i32, i32),
pub emoji: String, Clouds(i32, i32),
pub temp: i32, Snow(i32, i32),
pub temp_min: i32, Rain(i32, i32),
pub temp_max: i32, Drizzle(i32, i32),
Clear(i32, i32),
Atmospheric(i32, i32),
} }
impl From<APIResponse> for WeatherSummary { impl From<APIResponse> for WeatherSummary {
fn from(resp: APIResponse) -> Self { fn from(resp: APIResponse) -> Self {
let temp = resp.main.temp as i32;
let temp_min = resp.main.temp_min as i32; let temp_min = resp.main.temp_min as i32;
let temp_max = resp.main.temp_max as i32; let temp_max = resp.main.temp_max as i32;
match resp.weather[0].main.as_ref() { match resp.weather[0].main.as_ref() {
"Thunderstorm" => Self { "Thunderstorm" => Self::Thunderstorm(temp_min, temp_max),
name: "Thunderstorm".to_string(), "Clouds" => Self::Clouds(temp_min, temp_max),
emoji: "thunder_cloud_and_rain".to_string(), "Snow" => Self::Snow(temp_min, temp_max),
temp, "Rain" => Self::Rain(temp_min, temp_max),
temp_min, "Drizzle" => Self::Drizzle(temp_min, temp_max),
temp_max, "Clear" => Self::Clear(temp_min, temp_max),
}, "Atmosphere" => Self::Atmospheric(temp_min, temp_max),
"Clouds" => Self { _ => panic!("unrecognized weather type"),
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,
},
} }
} }
} }
@ -108,14 +61,19 @@ pub async fn get_summary(loc: &str) -> Result<WeatherSummary, String> {
); );
let resp = reqwest::get(&url).map_err(|e| e.to_string()).await?; 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() { if !resp.status().is_success() {
return Err(format!("Weather API response: {}", resp.status()).into()); return Err(format!("Weather API response: {}", resp.status()).into());
} }
let body = resp let api_response = resp
.json::<APIResponse>() .json::<APIResponse>()
.map_err(|e| e.to_string()) .map_err(|e| e.to_string())
.await?; .await?;
Ok(body.into()) println!("Got API resp: {:?}", api_response);
Ok(api_response.into())
} }