vortaroboto

Log | Files | Refs | README

commit 5b01eca43f2ad39ab9e8e65396452bf9aa5e4d63
parent ee309c70c1f443379c91af0ad2e8ba649c0a7d1b
Author: tomvig38@gmail.com <tomvig38@gmail.com>
Date:   Tue, 19 Oct 2021 08:11:15 +0000

Pliboniĝu la elektoj
Diffstat:
Msrc/main.rs | 158+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
1 file changed, 114 insertions(+), 44 deletions(-)

diff --git a/src/main.rs b/src/main.rs @@ -1,6 +1,7 @@ use futures::prelude::*; use irc::client::prelude::*; use serde::Deserialize; +use std::str::FromStr; use log::{debug, info}; use simple_logger::SimpleLogger; @@ -8,6 +9,7 @@ use simple_logger::SimpleLogger; const NICK: &'static str = "vortaroboto"; const TROVI_URL: &'static str = "http://www.simplavortaro.org/api/v1/trovi"; const VORTO_URL: &'static str = "http://www.simplavortaro.org/api/v1/vorto"; +const LINEO_SEP: &'static str = "\r\n"; #[derive(Deserialize, Debug)] struct Vortpartoj { @@ -56,6 +58,28 @@ struct Vorto { vorto: String, } +enum NumerSelektilo { + Cxiuj, + Numero(usize), +} + +impl FromStr for NumerSelektilo { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "*" => Ok(Self::Cxiuj), + s => Ok(Self::Numero(s.parse::<usize>().map_err(|e| e.to_string())?)), + } + } +} + +impl Default for NumerSelektilo { + fn default() -> Self { + Self::Numero(0) + } +} + async fn trovu(vorto: &str) -> Result<Vorto, String> { reqwest::get(format!("{}/{}", VORTO_URL, vorto)) .and_then(|j| j.json::<Vorto>()) @@ -72,13 +96,17 @@ async fn difinu(vorto: &str) -> Result<Trovo, String> { #[tokio::main] async fn main() -> irc::error::Result<()> { - - SimpleLogger::new().with_level(log::LevelFilter::Debug).init().unwrap(); + SimpleLogger::new() + .with_level(log::LevelFilter::Debug) + .init() + .unwrap(); let config = Config { nickname: Some(NICK.to_owned()), server: Some("irc.libera.chat".to_owned()), channels: vec!["##esperanto".to_owned()], + max_messages_in_burst: Some(5), + burst_window_length: Some(1), ..Config::default() }; @@ -121,6 +149,25 @@ async fn main() -> irc::error::Result<()> { Ok(()) } +macro_rules! parse_or_default { + ($t:ty, $arg:ident) => { + if let Some(s) = $arg.next() { + s.parse::<$t>().unwrap_or_default() + } else { + <$t>::default() + } + }; +} + +macro_rules! wrap_handler { + ($f:ident; $w:expr; $($args:expr),+) => { + match $f($w, $($args),+).await { + Ok(r) => Some(r), + Err(e) => Some(format!("Nenio trovata pri: {} ({})", $w, e.to_string())), + } + }; +} + async fn handle_command(cmd: &str) -> Option<String> { let mut splitted = cmd.split_ascii_whitespace(); match splitted.next() { @@ -129,40 +176,23 @@ async fn handle_command(cmd: &str) -> Option<String> { )), Some("difinu" | "d") => { if let Some(w) = splitted.next() { - let index = if let Some(s) = splitted.next() { - s.parse::<usize>().ok() - } else { - None - }; - match define_word(w, index).await { - Ok(r) => Some(r), - Err(e) => Some(format!("Nenio trovata pri: {} ({})", w, e.to_string())), - } + let index = parse_or_default!(NumerSelektilo, splitted); + wrap_handler!(define_word; w; index) } else { Some(String::from("Uzo: difinu [vorto] [numero]")) } } Some("traduku" | "trad" | "t") => { if let Some(w) = splitted.next() { - match traduki(w, splitted.next()).await { - Ok(r) => Some(r), - Err(e) => Some(format!("Nenio trovata pri: {} ({})", w, e.to_string())), - } + wrap_handler!(traduki; w; splitted.next()) } else { Some(String::from("Uzo: traduku [vorto] [lingv'kodo]")) } } Some("vortfarado" | "v" | "vf") => { if let Some(w) = splitted.next() { - let index = if let Some(s) = splitted.next() { - s.parse::<usize>().ok() - } else { - None - }; - match vortfarado(w, index).await { - Ok(r) => Some(r), - Err(e) => Some(format!("Nenio trovata pri: {} ({})", w, e.to_string())), - } + let index = parse_or_default!(NumerSelektilo, splitted); + wrap_handler!(vortfarado; w; index) } else { Some(String::from("Uzo: vortfarado [vorto]")) } @@ -172,9 +202,23 @@ async fn handle_command(cmd: &str) -> Option<String> { } } -async fn define_word(vorto: &str, difino: Option<usize>) -> Result<String, String> { +async fn define_word(vorto: &str, difino: NumerSelektilo) -> Result<String, String> { let res: Vorto = trovu(vorto).await?; + fn format_difino(d: &Difino, index: usize, len: usize, vorto: &str, por: bool) -> String { + if por { + format!( + "Difino {} el {} por {}: {}", + index + 1, + len, + vorto, + d.difino + ) + } else { + format!("Difino {} el {}: {}", index + 1, len, d.difino) + } + } + if res.difinoj.is_empty() { return Err(format!("{} ne havas difino.", vorto)); } @@ -187,17 +231,23 @@ async fn define_word(vorto: &str, difino: Option<usize>) -> Result<String, Strin } } - let index = difino.unwrap_or(1).clamp(1, res.difinoj.len()) - 1; - if let Some(d) = res.difinoj.get(index) { - Ok(format!( - "Difino {} el {} por {}: {}", - index + 1, - res.difinoj.len(), - vorto, - d.difino - )) + if let NumerSelektilo::Numero(i) = difino { + let index = i.clamp(1, res.difinoj.len()) - 1; + if let Some(d) = res.difinoj.get(index) { + Ok(format_difino(d, index, res.difinoj.len(), vorto, true)) + } else { + unreachable!(); + } } else { - unreachable!(); + let difj = res + .difinoj + .iter() + .enumerate() + .map(|(index, d)| format_difino(d, index, res.difinoj.len(), vorto, false)) + .collect::<Vec<String>>() + .join(LINEO_SEP); + + Ok(format!("Difinoj por {}:\r\n{}", vorto, difj)) } } @@ -238,7 +288,7 @@ async fn traduki(vorto: &str, fonto: Option<&str>) -> Result<String, String> { Ok(tradukoj.join("\r\n")) } -async fn vortfarado(vorto: &str, index: Option<usize>) -> Result<String, String> { +async fn vortfarado(vorto: &str, index: NumerSelektilo) -> Result<String, String> { let res: Trovo = difinu(vorto).await?; let vf = res.vortfarado; @@ -247,11 +297,8 @@ async fn vortfarado(vorto: &str, index: Option<usize>) -> Result<String, String> return Err(format!("{} ne havas vort'faradon.", vorto)); } - let index = index.unwrap_or(1).clamp(1, vf.len()) - 1; - - if let Some(v) = vf.get(index) { - let v = v - .partoj + fn make_partoj(v: &Vortfarado) -> String { + v.partoj .iter() .map(|p| { if let Some(pv) = &p.vorto { @@ -261,10 +308,33 @@ async fn vortfarado(vorto: &str, index: Option<usize>) -> Result<String, String> } }) .collect::<Vec<String>>() - .join(" + "); + .join(" + ") + } - Ok(format!("Vortfarado {} el {} por \"{}\":\r\n{}", index + 1, vf.len(), vorto, v)) + if let NumerSelektilo::Numero(i) = index { + let index = i.clamp(1, vf.len()) - 1; + + if let Some(v) = vf.get(index) { + let v = make_partoj(v); + Ok(format!( + "Vortfarado {} el {} por \"{}\":\r\n{}", + index + 1, + vf.len(), + vorto, + v + )) + } else { + unreachable!(); + } } else { - unreachable!(); + // Donu cxiuj trancxajxoj + let vj = vf.iter().enumerate().map(|(index, v)| { + format!( + "{}: {}", + index + 1, + make_partoj(v) + ) + }).collect::<Vec<String>>().join(LINEO_SEP); + Ok(format!("Vortfaradoj por \"{}\":\r\n{}", vorto, vj)) } }