use crate::bot::{Command, Context}; use arrayvec::ArrayString; use async_trait::async_trait; use rand::Rng; use std::{ error::Error, fmt::{Debug, Display}, }; #[derive(Debug)] pub struct CapacityError(arrayvec::CapacityError); impl Display for CapacityError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Display::fmt(&self.0, f) } } impl Error for CapacityError {} impl From> for CapacityError { fn from(e: arrayvec::CapacityError) -> Self { Self(e.simplify()) } } type LeekResult = Result, CapacityError>; fn mock(input: &str) -> ArrayString<512> { let mut builder = ArrayString::<512>::new(); for ch in input.chars() { if rand::random() { builder.push(ch.to_ascii_uppercase()); } else { builder.push(ch.to_ascii_lowercase()); } } builder } fn leetify(input: &str) -> ArrayString<512> { let mut builder = ArrayString::<512>::new(); for ch in input.chars() { builder.push(match ch.to_ascii_lowercase() { 'a' => '4', 'e' => '3', 'i' => '1', 'o' => '0', 'g' => '6', 's' => '5', 't' => '7', 'b' => '8', _ => ch, }); } builder } fn owoify(input: &str) -> LeekResult { let mut builder: ArrayString<512> = ArrayString::from("\x1d")?; let mut rng = rand::thread_rng(); let mut last_char = '\0'; for byte in input.bytes() { let mut ch = char::from(byte); if !ch.is_ascii() { continue; } // owoify character ch = match ch.to_ascii_lowercase() { 'r' | 'l' => 'w', _ => ch, }; // stutter (e.g. "o-ohayou gozaimasu!") if last_char == ' ' && rng.gen_bool(0.2) { builder.try_push(ch)?; builder.try_push('-')?; } match ch { // nya-ify 'a' | 'e' | 'i' | 'o' | 'u' if last_char == 'n' => { builder.try_push('y')?; } // textmoji '.' => { builder.try_push_str(match rng.gen_range(0..6) { 1 => " >~<", 2 => " (◕ᴗ◕✿)", 3 => " >w<", 4 => " >_<", 5 => " OwO", _ => " ^^", })?; } _ => {} } builder.try_push(ch)?; last_char = ch; } builder.try_push_str("~~")?; Ok(builder) } #[derive(Debug, Clone, Copy)] enum LeekCommand { Owo, Leet, Mock, } async fn execute_leek(cmd: LeekCommand, msg: &Context<'_>) -> anyhow::Result { let nick = msg.content.unwrap_or(msg.author); match msg.history.last_msg(nick).await { Some(msg) => Ok(match cmd { LeekCommand::Owo => owoify(&msg)?, LeekCommand::Leet => leetify(&msg), LeekCommand::Mock => mock(&msg), } .to_string()), None => Ok("No previous messages found.".into()), } } pub struct Owo; pub struct Leet; pub struct Mock; #[async_trait] impl Command for Owo { async fn execute(&mut self, msg: Context<'_>) -> anyhow::Result { execute_leek(LeekCommand::Owo, &msg).await } } #[async_trait] impl Command for Leet { async fn execute(&mut self, msg: Context<'_>) -> anyhow::Result { execute_leek(LeekCommand::Leet, &msg).await } } #[async_trait] impl Command for Mock { async fn execute(&mut self, msg: Context<'_>) -> anyhow::Result { execute_leek(LeekCommand::Mock, &msg).await } }