Optimize executor and add mathbot
This commit is contained in:
parent
68c15be79a
commit
4a27aa42a3
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,3 +2,4 @@
|
|||
uberbot_*.toml
|
||||
uberbot.toml
|
||||
/Cargo.lock
|
||||
.idea
|
||||
|
|
|
@ -20,4 +20,5 @@ toml = "0.5"
|
|||
serde = "1.0"
|
||||
arrayvec = "0.7"
|
||||
rand = "0.8"
|
||||
meval = "0.2"
|
||||
async-circe = { git = "https://git.karx.xyz/circe/async-circe" }
|
||||
|
|
|
@ -27,9 +27,9 @@ pub fn mock(input: &str) -> LeekResult {
|
|||
|
||||
for ch in input.chars() {
|
||||
if rand::random() {
|
||||
builder.try_push(ch.to_ascii_uppercase())?;
|
||||
builder.push(ch.to_ascii_uppercase());
|
||||
} else {
|
||||
builder.try_push(ch.to_ascii_lowercase())?;
|
||||
builder.push(ch.to_ascii_lowercase());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ pub fn leetify(input: &str) -> LeekResult {
|
|||
let mut builder = ArrayString::<512>::new();
|
||||
|
||||
for ch in input.chars() {
|
||||
builder.try_push(match ch {
|
||||
builder.push(match ch {
|
||||
'a' => '4',
|
||||
'e' => '3',
|
||||
'i' => '1',
|
||||
|
@ -50,14 +50,14 @@ pub fn leetify(input: &str) -> LeekResult {
|
|||
't' => '7',
|
||||
'b' => '8',
|
||||
_ => ch,
|
||||
})?;
|
||||
});
|
||||
}
|
||||
|
||||
Ok(builder)
|
||||
}
|
||||
|
||||
pub fn owoify(input: &str) -> LeekResult {
|
||||
let mut builder: ArrayString<512> = ArrayString::from(input)?;
|
||||
let mut builder: ArrayString<512> = ArrayString::from("\x1d")?;
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut last_char = '\0';
|
||||
for byte in input.bytes() {
|
||||
|
@ -84,7 +84,7 @@ pub fn owoify(input: &str) -> LeekResult {
|
|||
'.' => {
|
||||
builder.try_push_str(match rng.gen_range(0..6) {
|
||||
1 => " OwO",
|
||||
2 => " :3",
|
||||
2 => " (◕ᴗ◕✿)",
|
||||
3 => " >w<",
|
||||
4 => " >_<",
|
||||
5 => " ^•ﻌ•^",
|
||||
|
@ -96,5 +96,7 @@ pub fn owoify(input: &str) -> LeekResult {
|
|||
builder.try_push(ch)?;
|
||||
last_char = ch;
|
||||
}
|
||||
builder.try_push_str("~~")?;
|
||||
Ok(builder)
|
||||
}
|
||||
|
||||
|
|
31
src/bots/misc.rs
Normal file
31
src/bots/misc.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
use std::collections::HashMap;
|
||||
use arrayvec::ArrayString;
|
||||
use meval::Context;
|
||||
use serde_json::Value;
|
||||
use std::fmt::Write;
|
||||
|
||||
pub async fn get_waifu_pic(category: &str) -> anyhow::Result<Option<String>> {
|
||||
let api_resp = reqwest::get(format!("https://api.waifu.pics/sfw/{}", category))
|
||||
.await?
|
||||
.text()
|
||||
.await?;
|
||||
let api_resp = api_resp.trim();
|
||||
tracing::debug!("API response: {}", api_resp);
|
||||
let value: Value = serde_json::from_str(&api_resp)?;
|
||||
let url = value["url"].as_str().map(|v| v.to_string());
|
||||
Ok(url)
|
||||
}
|
||||
|
||||
pub fn mathbot(author: String, expr: Option<&str>, last_evals: &mut HashMap<String, f64>) -> anyhow::Result<ArrayString<256>> {
|
||||
if let Some(expr) = expr {
|
||||
let last_eval = last_evals.entry(author).or_insert(0.0);
|
||||
let mut meval_ctx = Context::new();
|
||||
let mut result = ArrayString::new();
|
||||
let value = meval::eval_str_with_context(expr, meval_ctx.var("x", *last_eval))?;
|
||||
*last_eval = value;
|
||||
write!(result, "{} = {}", expr, value)?;
|
||||
Ok(result)
|
||||
} else {
|
||||
Ok(ArrayString::from("No expression to evaluate")?)
|
||||
}
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
pub mod leek;
|
||||
pub mod title;
|
||||
pub mod weeb;
|
||||
pub mod misc;
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
use serde_json::Value;
|
||||
|
||||
pub async fn get_waifu_pic(category: &str) -> anyhow::Result<Option<String>> {
|
||||
let api_resp = reqwest::get(format!("https://api.waifu.pics/sfw/{}", category))
|
||||
.await?
|
||||
.text()
|
||||
.await?;
|
||||
let api_resp = api_resp.trim();
|
||||
tracing::debug!("API response: {}", api_resp);
|
||||
let value: Value = serde_json::from_str(&api_resp)?;
|
||||
let url = value["url"].as_str().map(|v| v.to_string());
|
||||
Ok(url)
|
||||
}
|
||||
|
105
src/main.rs
105
src/main.rs
|
@ -1,6 +1,8 @@
|
|||
mod bots;
|
||||
|
||||
use async_circe::{commands::Command, Client, Config};
|
||||
use bots::title::Titlebot;
|
||||
use bots::{leek, weeb};
|
||||
use bots::{leek, misc};
|
||||
use rspotify::Credentials;
|
||||
use serde::Deserialize;
|
||||
use std::fs::File;
|
||||
|
@ -9,19 +11,21 @@ use std::{collections::HashMap, env};
|
|||
use tokio::select;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
mod bots;
|
||||
|
||||
const HELP: &str = concat!(
|
||||
"=- \x1d\x02Ü\x02berbot\x0f ",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
" -="
|
||||
);
|
||||
// this will be displayed when the help command is used
|
||||
const HELP: &[&str] = &[
|
||||
concat!("=- \x1d\x02Ü\x02berbot\x0f ", env!("CARGO_PKG_VERSION"), " -="),
|
||||
" * waifu <category>",
|
||||
" * owo/mock/leet [user]",
|
||||
" * ev <math expression>",
|
||||
" - This bot also provides titles of URLs and details for Spotify URIs/links."
|
||||
];
|
||||
|
||||
#[cfg(unix)]
|
||||
async fn terminate_signal() {
|
||||
use tokio::signal::unix::{signal, SignalKind};
|
||||
let mut sigterm = signal(SignalKind::terminate()).unwrap();
|
||||
let mut sigint = signal(SignalKind::interrupt()).unwrap();
|
||||
tracing::debug!("Installed ctrl+c handler");
|
||||
select! {
|
||||
_ = sigterm.recv() => return,
|
||||
_ = sigint.recv() => return
|
||||
|
@ -32,6 +36,7 @@ async fn terminate_signal() {
|
|||
async fn terminate_signal() {
|
||||
use tokio::signal::windows::ctrl_c;
|
||||
let mut ctrlc = ctrl_c().unwrap();
|
||||
tracing::debug!("Installed ctrl+c handler");
|
||||
let _ = ctrlc.recv().await;
|
||||
}
|
||||
|
||||
|
@ -39,6 +44,7 @@ struct AppState {
|
|||
prefix: String,
|
||||
client: Client,
|
||||
last_msgs: HashMap<String, String>,
|
||||
last_eval: HashMap<String, f64>,
|
||||
titlebot: Titlebot,
|
||||
}
|
||||
|
||||
|
@ -61,8 +67,7 @@ async fn main() -> anyhow::Result<()> {
|
|||
.with_env_filter(EnvFilter::from_env("UBERBOT_LOG"))
|
||||
.init();
|
||||
|
||||
let mut file =
|
||||
File::open(env::var("UBERBOT_CONFIG").unwrap_or_else(|_| "uberbot.toml".to_string()))?;
|
||||
let mut file = File::open(env::var("UBERBOT_CONFIG").unwrap_or_else(|_| "uberbot.toml".to_string()))?;
|
||||
let mut client_conf = String::new();
|
||||
file.read_to_string(&mut client_conf)?;
|
||||
|
||||
|
@ -81,19 +86,18 @@ async fn main() -> anyhow::Result<()> {
|
|||
client_config.port,
|
||||
client_config.username,
|
||||
);
|
||||
tracing::debug!("Creating circe client");
|
||||
let mut client = Client::new(config).await?;
|
||||
tracing::debug!("Identifying with IRC");
|
||||
client.identify().await?;
|
||||
|
||||
let state = AppState {
|
||||
prefix: client_config.prefix,
|
||||
client,
|
||||
last_msgs: HashMap::new(),
|
||||
last_eval: HashMap::new(),
|
||||
titlebot: Titlebot::create(spotify_creds).await?,
|
||||
};
|
||||
|
||||
if let Err(e) = message_loop(state).await {
|
||||
if let Err(e) = executor(state).await {
|
||||
tracing::error!("Error in message loop: {}", e);
|
||||
}
|
||||
|
||||
|
@ -102,24 +106,24 @@ async fn main() -> anyhow::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn message_loop(mut state: AppState) -> anyhow::Result<()> {
|
||||
loop {
|
||||
select! {
|
||||
r = state.client.read() => {
|
||||
if let Ok(command) = r {
|
||||
handle_message(&mut state, command).await?;
|
||||
}
|
||||
},
|
||||
_ = terminate_signal() => {
|
||||
tracing::info!("Sending QUIT message");
|
||||
state.client.quit(Some("überbot shutting down")).await?;
|
||||
break;
|
||||
}
|
||||
async fn executor(mut state: AppState) -> anyhow::Result<()> {
|
||||
select! {
|
||||
r = message_loop(&mut state) => r?,
|
||||
_ = terminate_signal() => {
|
||||
tracing::info!("Sending QUIT message");
|
||||
state.client.quit(Some("überbot shutting down")).await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn message_loop(state: &mut AppState) -> anyhow::Result<()> {
|
||||
while let Some(cmd) = state.client.read().await? {
|
||||
handle_message(state, cmd).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_message(state: &mut AppState, command: Command) -> anyhow::Result<()> {
|
||||
// change this to a match when more commands are handled
|
||||
if let Command::PRIVMSG(nick, channel, message) = command {
|
||||
|
@ -134,30 +138,20 @@ async fn handle_message(state: &mut AppState, command: Command) -> anyhow::Resul
|
|||
}
|
||||
|
||||
enum LeekCommand {
|
||||
Owo,
|
||||
Leet,
|
||||
Mock,
|
||||
Owo, Leet, Mock
|
||||
}
|
||||
async fn execute_leek(
|
||||
state: &mut AppState,
|
||||
cmd: LeekCommand,
|
||||
channel: &str,
|
||||
nick: &str,
|
||||
) -> anyhow::Result<()> {
|
||||
async fn execute_leek(state: &mut AppState, cmd: LeekCommand, channel: &str, nick: &str) -> anyhow::Result<()> {
|
||||
match state.last_msgs.get(nick) {
|
||||
Some(msg) => {
|
||||
let output = match cmd {
|
||||
LeekCommand::Owo => leek::owoify(msg)?,
|
||||
LeekCommand::Leet => leek::leetify(msg)?,
|
||||
LeekCommand::Mock => leek::mock(msg)?,
|
||||
LeekCommand::Mock => leek::mock(msg)?
|
||||
};
|
||||
state.client.privmsg(channel, &output).await?;
|
||||
}
|
||||
None => {
|
||||
state
|
||||
.client
|
||||
.privmsg(channel, "No last messages found.")
|
||||
.await?;
|
||||
state.client.privmsg(channel, "No last messages found.").await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -170,11 +164,10 @@ async fn handle_privmsg(
|
|||
message: String,
|
||||
) -> anyhow::Result<()> {
|
||||
if !message.starts_with(state.prefix.as_str()) {
|
||||
state.last_msgs.insert(nick, message.clone());
|
||||
|
||||
if let Some(titlebot_msg) = state.titlebot.resolve(&message).await? {
|
||||
state.client.privmsg(&channel, &titlebot_msg).await?;
|
||||
}
|
||||
state.last_msgs.insert(nick, message);
|
||||
return Ok(());
|
||||
}
|
||||
let space_index = message.find(' ');
|
||||
|
@ -183,15 +176,17 @@ async fn handle_privmsg(
|
|||
} else {
|
||||
(&message[state.prefix.len()..], None)
|
||||
};
|
||||
tracing::debug!("Command received ({}; {:?})", command, remainder);
|
||||
tracing::debug!("Command received {:?} -> ({:?}; {:?})", message, command, remainder);
|
||||
|
||||
match command {
|
||||
"help" => {
|
||||
state.client.privmsg(&channel, HELP).await?;
|
||||
for help_line in HELP {
|
||||
state.client.privmsg(&channel, help_line).await?;
|
||||
}
|
||||
}
|
||||
"waifu" => {
|
||||
let category = remainder.unwrap_or("waifu");
|
||||
let url = weeb::get_waifu_pic(category).await?;
|
||||
let url = misc::get_waifu_pic(category).await?;
|
||||
let response = url
|
||||
.as_ref()
|
||||
.map(|v| v.as_str())
|
||||
|
@ -199,26 +194,18 @@ async fn handle_privmsg(
|
|||
state.client.privmsg(&channel, response).await?;
|
||||
}
|
||||
"mock" => {
|
||||
execute_leek(
|
||||
state,
|
||||
LeekCommand::Mock,
|
||||
channel,
|
||||
remainder.unwrap_or(&nick),
|
||||
)
|
||||
.await?;
|
||||
execute_leek(state, LeekCommand::Mock, channel, remainder.unwrap_or(&nick)).await?;
|
||||
}
|
||||
"leet" => {
|
||||
execute_leek(
|
||||
state,
|
||||
LeekCommand::Leet,
|
||||
channel,
|
||||
remainder.unwrap_or(&nick),
|
||||
)
|
||||
.await?;
|
||||
execute_leek(state, LeekCommand::Leet, channel, remainder.unwrap_or(&nick)).await?;
|
||||
}
|
||||
"owo" => {
|
||||
execute_leek(state, LeekCommand::Owo, channel, remainder.unwrap_or(&nick)).await?;
|
||||
}
|
||||
"ev" => {
|
||||
let result = misc::mathbot(nick, remainder, &mut state.last_eval)?;
|
||||
state.client.privmsg(&channel, &result).await?;
|
||||
}
|
||||
_ => {
|
||||
state.client.privmsg(&channel, "Unknown command").await?;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue