diff --git a/src/bot/modules/weather.rs b/src/bot/modules/weather.rs index 0f92267..4acbe8d 100644 --- a/src/bot/modules/weather.rs +++ b/src/bot/modules/weather.rs @@ -110,7 +110,7 @@ impl Store { } fn fetch_weather(location: &str) -> Result { - let path = format!("/{}?format=%l:+%C+%t+feels+%f+%w+%h&m", url_encode(location)); + let path = format!("/{}?format=%l|%c|%C|%t|%f|%w|%h&m", url_encode(location)); let mut last = "no response".to_string(); for attempt in 0..3 { if attempt > 0 { @@ -118,16 +118,9 @@ fn fetch_weather(location: &str) -> Result { } match https_get("wttr.in", &path) { Ok(body) => { - let line: String = body - .lines() - .map(str::trim) - .find(|l| !l.is_empty()) - .unwrap_or("") - .chars() - .take(250) - .collect(); - if !line.is_empty() { - return Ok(line); + let raw = body.lines().map(str::trim).find(|l| !l.is_empty()).unwrap_or(""); + if !raw.is_empty() { + return Ok(format_weather(raw, location)); } last = "empty response".to_string(); } @@ -143,6 +136,24 @@ fn fetch_weather(location: &str) -> Result { Err(last) } +pub fn format_weather(raw: &str, location: &str) -> String { + let f: Vec<&str> = raw.split('|').map(str::trim).collect(); + if f.len() >= 7 { + let temp = f[3].trim_start_matches('+'); + let feels = f[4].trim_start_matches('+'); + let msg = format!( + "{} {}: {}, {} (feels like {}) · 💨 {} · 💧 {}", + f[1], f[0], f[2], temp, feels, f[5], f[6] + ); + return msg.chars().take(250).collect(); + } + let low = raw.to_lowercase(); + if low.contains("not found") || low.contains("unknown location") { + return format!("couldn't find '{location}' — try a city name or postcode"); + } + raw.chars().take(250).collect() +} + struct HttpErr { msg: String, retry: bool, diff --git a/tests/weather.rs b/tests/weather.rs index 2b4c303..9744f06 100644 --- a/tests/weather.rs +++ b/tests/weather.rs @@ -2,7 +2,7 @@ use std::collections::BTreeMap; use std::path::PathBuf; use rustbot::bot::module::{Action, Command, Module}; -use rustbot::bot::modules::weather::{parse_db, to_json, Weather}; +use rustbot::bot::modules::weather::{format_weather, parse_db, to_json, Weather}; fn cmd<'a>(sender: &'a str, name: &'a str, args: &'a [&'a str]) -> Command<'a> { Command { sender, reply_to: "#chan", name, args, prefix: ">", network: "test" } @@ -65,3 +65,19 @@ fn w_without_saved_location_hints_to_add() { let r = reply(&w.on_command(&cmd("nobody", "w", &[]))).to_string(); assert!(r.contains("add"), "reply: {r:?}"); } + +#[test] +fn format_weather_builds_friendly_line() { + let out = format_weather("73700|☀️ |Sunny|+23°C|+18°C|↘8km/h|45%", "73700"); + assert!(out.contains("☀️"), "{out}"); + assert!(out.contains("73700: Sunny"), "{out}"); + assert!(out.contains("23°C") && !out.contains("+23"), "{out}"); + assert!(out.contains("feels like 18°C"), "{out}"); + assert!(out.contains("45%"), "{out}"); +} + +#[test] +fn format_weather_friendly_not_found() { + let out = format_weather("location not found: upstream error", "zzz"); + assert!(out.contains("couldn't find 'zzz'"), "{out}"); +}