rustfmt, some minor logging additions

This commit is contained in:
famfo 2022-01-02 21:58:54 +01:00
parent a7b9487c38
commit 7490df1c91
7 changed files with 120 additions and 58 deletions

View file

@ -1,6 +1,9 @@
use arrayvec::{ArrayString, CapacityError}; use arrayvec::{ArrayString, CapacityError};
use rand::Rng; use rand::Rng;
use std::{error::Error, fmt::{Debug, Display}}; use std::{
error::Error,
fmt::{Debug, Display},
};
#[derive(Debug)] #[derive(Debug)]
pub struct LeekCapacityError(CapacityError); pub struct LeekCapacityError(CapacityError);
@ -19,7 +22,6 @@ impl<T> From<CapacityError<T>> for LeekCapacityError {
} }
} }
type LeekResult = Result<ArrayString<512>, LeekCapacityError>; type LeekResult = Result<ArrayString<512>, LeekCapacityError>;
pub fn mock(input: &str) -> LeekResult { pub fn mock(input: &str) -> LeekResult {
@ -99,4 +101,3 @@ pub fn owoify(input: &str) -> LeekResult {
builder.try_push_str("~~")?; builder.try_push_str("~~")?;
Ok(builder) Ok(builder)
} }

View file

@ -1,9 +1,16 @@
use std::collections::HashMap;
use arrayvec::ArrayString; use arrayvec::ArrayString;
use meval::Context; use meval::Context;
use serde_json::Value; use serde_json::Value;
use std::collections::HashMap;
use std::fmt::Write; use std::fmt::Write;
#[derive(Debug)]
pub enum LeekCommand {
Owo,
Leet,
Mock,
}
pub async fn get_waifu_pic(category: &str) -> anyhow::Result<Option<String>> { pub async fn get_waifu_pic(category: &str) -> anyhow::Result<Option<String>> {
let api_resp = reqwest::get(format!("https://api.waifu.pics/sfw/{}", category)) let api_resp = reqwest::get(format!("https://api.waifu.pics/sfw/{}", category))
.await? .await?
@ -15,16 +22,47 @@ pub async fn get_waifu_pic(category: &str) -> anyhow::Result<Option<String>> {
Ok(url) Ok(url)
} }
pub fn mathbot(author: String, expr: Option<&str>, last_evals: &mut HashMap<String, f64>) -> anyhow::Result<ArrayString<256>> { pub fn mathbot(
author: String,
expr: Option<&str>,
last_evals: &mut HashMap<String, f64>,
) -> anyhow::Result<ArrayString<256>> {
if let Some(expr) = expr { if let Some(expr) = expr {
let last_eval = last_evals.entry(author).or_insert(0.0); let last_eval = last_evals.entry(author).or_insert(0.0);
let mut meval_ctx = Context::new(); let mut meval_ctx = Context::new();
let mut result = ArrayString::new(); let mut result = ArrayString::new();
let value = meval::eval_str_with_context(expr, meval_ctx.var("x", *last_eval))?; let value = meval::eval_str_with_context(expr, meval_ctx.var("x", *last_eval))?;
*last_eval = value; *last_eval = value;
tracing::debug!("{} = {}", expr, value);
write!(result, "{} = {}", expr, value)?; write!(result, "{} = {}", expr, value)?;
Ok(result) Ok(result)
} else { } else {
Ok(ArrayString::from("No expression to evaluate")?) Ok(ArrayString::from("No expression to evaluate")?)
} }
} }
pub async fn execute_leek(
state: &mut crate::AppState,
cmd: LeekCommand,
channel: &str,
nick: &str,
) -> anyhow::Result<()> {
match state.last_msgs.get(nick) {
Some(msg) => {
tracing::debug!("Executing {:?} on {:?}", cmd, msg);
let output = match cmd {
LeekCommand::Owo => super::leek::owoify(msg)?,
LeekCommand::Leet => super::leek::leetify(msg)?,
LeekCommand::Mock => super::leek::mock(msg)?,
};
state.client.privmsg(channel, &output).await?;
}
None => {
state
.client
.privmsg(channel, "No last messages found.")
.await?;
}
}
Ok(())
}

View file

@ -1,4 +1,4 @@
pub mod leek; pub mod leek;
pub mod title;
pub mod misc; pub mod misc;
pub mod sed; pub mod sed;
pub mod title;

View file

@ -1,4 +1,4 @@
use std::{fmt::Display, error::Error}; use std::{error::Error, fmt::Display};
use arrayvec::{ArrayString, CapacityError}; use arrayvec::{ArrayString, CapacityError};
use fancy_regex::Regex; use fancy_regex::Regex;
@ -9,7 +9,7 @@ use sedregex::find_and_replace;
pub enum SedError { pub enum SedError {
Capacity(CapacityError), Capacity(CapacityError),
Regex(fancy_regex::Error), Regex(fancy_regex::Error),
SedRegex(sedregex::ErrorKind) SedRegex(sedregex::ErrorKind),
} }
impl Display for SedError { impl Display for SedError {
@ -56,8 +56,8 @@ pub fn resolve(prev_msg: &str, cmd: &str) -> SedResult {
Ok(Some(ArrayString::from(&formatted)?)) Ok(Some(ArrayString::from(&formatted)?))
} else { } else {
Ok(None) Ok(None)
} };
} }
Ok(None) Ok(None)
} }

View file

@ -23,7 +23,8 @@ async fn resolve_spotify(
// } // }
tracing::debug!( tracing::debug!(
"Resolving Spotify resource '{}' with id '{}'", "Resolving Spotify resource '{}' with id '{}'",
resource_type, resource_id resource_type,
resource_id
); );
match resource_type { match resource_type {
"track" => { "track" => {
@ -89,6 +90,7 @@ impl Titlebot {
r"(?:https?|spotify):(?://open\.spotify\.com/)?(track|artist|album|playlist)[/:]([a-zA-Z0-9]*)", r"(?:https?|spotify):(?://open\.spotify\.com/)?(track|artist|album|playlist)[/:]([a-zA-Z0-9]*)",
)?; )?;
let mut spotify = ClientCredsSpotify::new(spotify_creds); let mut spotify = ClientCredsSpotify::new(spotify_creds);
spotify.request_token().await?; spotify.request_token().await?;
Ok(Self { Ok(Self {
url_regex, url_regex,
@ -103,6 +105,7 @@ impl Titlebot {
tracing::debug!("{}", message); tracing::debug!("{}", message);
let tp_group = m.get(1).unwrap(); let tp_group = m.get(1).unwrap();
let id_group = m.get(2).unwrap(); let id_group = m.get(2).unwrap();
return Ok(Some( return Ok(Some(
resolve_spotify( resolve_spotify(
&mut self.spotify, &mut self.spotify,
@ -114,6 +117,7 @@ impl Titlebot {
} else if let Some(m) = self.url_regex.find(&message)? { } else if let Some(m) = self.url_regex.find(&message)? {
let url = &message[m.start()..m.end()]; let url = &message[m.start()..m.end()];
tracing::debug!("url: {}", url); tracing::debug!("url: {}", url);
let response = reqwest::get(url).await?; let response = reqwest::get(url).await?;
if let Some(header) = response.headers().get("Content-Type") { if let Some(header) = response.headers().get("Content-Type") {
tracing::debug!("response header: {}", header.to_str()?); tracing::debug!("response header: {}", header.to_str()?);
@ -121,6 +125,7 @@ impl Titlebot {
return Ok(None); return Ok(None);
} }
} }
let body = response.text().await?; let body = response.text().await?;
if let Some(tm) = self.title_regex.find(&body)? { if let Some(tm) = self.title_regex.find(&body)? {
let title_match = &body[tm.start()..tm.end()]; let title_match = &body[tm.start()..tm.end()];

View file

@ -1,5 +1,8 @@
use rusqlite::{OptionalExtension, params}; use rusqlite::{params, OptionalExtension};
use tokio::sync::{mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, oneshot}; use tokio::sync::{
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
oneshot,
};
#[derive(Debug)] #[derive(Debug)]
enum Task { enum Task {
@ -17,8 +20,11 @@ impl DbExecutor {
pub fn create(dbpath: &str) -> rusqlite::Result<(Self, ExecutorConnection)> { pub fn create(dbpath: &str) -> rusqlite::Result<(Self, ExecutorConnection)> {
let (tx, rx) = unbounded_channel(); let (tx, rx) = unbounded_channel();
let db = rusqlite::Connection::open(dbpath)?; let db = rusqlite::Connection::open(dbpath)?;
db.execute("create table if not exists quotes(id integer primary key,\ db.execute(
username text not null, quote text not null)", [])?; "create table if not exists quotes(id integer primary key,\
username text not null, quote text not null)",
[],
)?;
tracing::debug!("Database connected ({})", dbpath); tracing::debug!("Database connected ({})", dbpath);
Ok((Self { rx, db }, ExecutorConnection { tx })) Ok((Self { rx, db }, ExecutorConnection { tx }))
} }
@ -28,7 +34,9 @@ impl DbExecutor {
match task { match task {
Task::AddQuote(tx, quote, author) => { Task::AddQuote(tx, quote, author) => {
if let Err(e) = self.db.execute( if let Err(e) = self.db.execute(
"insert into quotes(quote,username) values(?,?)", params![quote,author]) { "insert into quotes(quote,username) values(?,?)",
params![quote, author],
) {
tracing::error!("A database error has occurred: {}", e); tracing::error!("A database error has occurred: {}", e);
tx.send(false).unwrap(); tx.send(false).unwrap();
} else { } else {
@ -57,7 +65,9 @@ pub struct ExecutorConnection {
impl Clone for ExecutorConnection { impl Clone for ExecutorConnection {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { tx: self.tx.clone() } Self {
tx: self.tx.clone(),
}
} }
} }

View file

@ -1,20 +1,20 @@
mod bots; mod bots;
mod database; mod database;
use crate::database::{DbExecutor, ExecutorConnection};
use arrayvec::ArrayString; use arrayvec::ArrayString;
use async_circe::{commands::Command, Client, Config}; use async_circe::{commands::Command, Client, Config};
use bots::title::Titlebot; use bots::title::Titlebot;
use bots::{leek, misc, sed}; use bots::{misc, misc::LeekCommand, sed};
use rspotify::Credentials; use rspotify::Credentials;
use serde::Deserialize; use serde::Deserialize;
use std::fmt::Write;
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
use std::thread;
use std::{collections::HashMap, env}; use std::{collections::HashMap, env};
use tokio::select; use tokio::select;
use tracing_subscriber::EnvFilter; use tracing_subscriber::EnvFilter;
use std::fmt::Write;
use std::thread;
use crate::database::{DbExecutor, ExecutorConnection};
// this will be displayed when the help command is used // this will be displayed when the help command is used
const HELP: &[&str] = &[ const HELP: &[&str] = &[
@ -45,13 +45,13 @@ async fn terminate_signal() {
let _ = ctrlc.recv().await; let _ = ctrlc.recv().await;
} }
struct AppState { pub struct AppState {
prefix: String, prefix: String,
client: Client, client: Client,
last_msgs: HashMap<String, String>, last_msgs: HashMap<String, String>,
last_eval: HashMap<String, f64>, last_eval: HashMap<String, f64>,
titlebot: Titlebot, titlebot: Titlebot,
db: ExecutorConnection db: ExecutorConnection,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -65,7 +65,7 @@ struct ClientConf {
spotify_client_id: String, spotify_client_id: String,
spotify_client_secret: String, spotify_client_secret: String,
prefix: String, prefix: String,
db_path: Option<String> db_path: Option<String>,
} }
#[tokio::main(flavor = "current_thread")] #[tokio::main(flavor = "current_thread")]
@ -74,13 +74,15 @@ async fn main() -> anyhow::Result<()> {
.with_env_filter(EnvFilter::from_env("UBERBOT_LOG")) .with_env_filter(EnvFilter::from_env("UBERBOT_LOG"))
.init(); .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(); let mut client_conf = String::new();
file.read_to_string(&mut client_conf)?; file.read_to_string(&mut client_conf)?;
let client_config: ClientConf = toml::from_str(&client_conf)?; let client_config: ClientConf = toml::from_str(&client_conf)?;
let (db_exec, db_conn) = DbExecutor::create(client_config.db_path.as_deref().unwrap_or("uberbot.db3"))?; let (db_exec, db_conn) =
DbExecutor::create(client_config.db_path.as_deref().unwrap_or("uberbot.db3"))?;
let exec_thread = thread::spawn(move || { let exec_thread = thread::spawn(move || {
db_exec.run(); db_exec.run();
tracing::info!("Database executor has been shut down"); tracing::info!("Database executor has been shut down");
@ -99,6 +101,7 @@ async fn main() -> anyhow::Result<()> {
client_config.port, client_config.port,
client_config.username, client_config.username,
); );
let mut client = Client::new(config).await?; let mut client = Client::new(config).await?;
client.identify().await?; client.identify().await?;
@ -108,14 +111,16 @@ async fn main() -> anyhow::Result<()> {
last_msgs: HashMap::new(), last_msgs: HashMap::new(),
last_eval: HashMap::new(), last_eval: HashMap::new(),
titlebot: Titlebot::create(spotify_creds).await?, titlebot: Titlebot::create(spotify_creds).await?,
db: db_conn db: db_conn,
}; };
if let Err(e) = executor(state).await { if let Err(e) = executor(state).await {
tracing::error!("Error in message loop: {}", e); tracing::error!("Error in message loop: {}", e);
} }
exec_thread.join(); if let Err(e) = exec_thread.join() {
tracing::error!("Error while shutting down the database: {:?}", e);
}
tracing::info!("Shutting down"); tracing::info!("Shutting down");
Ok(()) Ok(())
@ -146,28 +151,6 @@ async fn message_loop(state: &mut AppState) -> anyhow::Result<()> {
Ok(()) Ok(())
} }
#[derive(Debug)]
enum LeekCommand {
Owo, Leet, Mock
}
async fn execute_leek(state: &mut AppState, cmd: LeekCommand, channel: &str, nick: &str) -> anyhow::Result<()> {
match state.last_msgs.get(nick) {
Some(msg) => {
tracing::debug!("Executing {:?} on {:?}", cmd, msg);
let output = match cmd {
LeekCommand::Owo => leek::owoify(msg)?,
LeekCommand::Leet => leek::leetify(msg)?,
LeekCommand::Mock => leek::mock(msg)?
};
state.client.privmsg(channel, &output).await?;
}
None => {
state.client.privmsg(channel, "No last messages found.").await?;
}
}
Ok(())
}
fn separate_to_space(str: &str, prefix_len: usize) -> (&str, Option<&str>) { fn separate_to_space(str: &str, prefix_len: usize) -> (&str, Option<&str>) {
if let Some(o) = str.find(' ') { if let Some(o) = str.find(' ') {
(&str[prefix_len..o], Some(&str[o + 1..])) (&str[prefix_len..o], Some(&str[o + 1..]))
@ -219,13 +202,26 @@ async fn handle_privmsg(
state.client.privmsg(&channel, response).await?; state.client.privmsg(&channel, response).await?;
} }
"mock" => { "mock" => {
execute_leek(state, LeekCommand::Mock, channel, remainder.unwrap_or(&nick)).await?; misc::execute_leek(
state,
LeekCommand::Mock,
channel,
remainder.unwrap_or(&nick),
)
.await?;
} }
"leet" => { "leet" => {
execute_leek(state, LeekCommand::Leet, channel, remainder.unwrap_or(&nick)).await?; misc::execute_leek(
state,
LeekCommand::Leet,
channel,
remainder.unwrap_or(&nick),
)
.await?;
} }
"owo" => { "owo" => {
execute_leek(state, LeekCommand::Owo, channel, remainder.unwrap_or(&nick)).await?; misc::execute_leek(state, LeekCommand::Owo, channel, remainder.unwrap_or(&nick))
.await?;
} }
"ev" => { "ev" => {
let result = misc::mathbot(nick, remainder, &mut state.last_eval)?; let result = misc::mathbot(nick, remainder, &mut state.last_eval)?;
@ -234,20 +230,32 @@ async fn handle_privmsg(
"grab" => { "grab" => {
if let Some(target) = remainder { if let Some(target) = remainder {
if target == nick { if target == nick {
state.client.privmsg(&channel, "You can't grab yourself").await?; state
return Ok(()) .client
.privmsg(&channel, "You can't grab yourself")
.await?;
return Ok(());
} }
if let Some(prev_msg) = state.last_msgs.get(target) { if let Some(prev_msg) = state.last_msgs.get(target) {
if state.db.add_quote(prev_msg.clone(), target.into()).await { if state.db.add_quote(prev_msg.clone(), target.into()).await {
state.client.privmsg(&channel, "Quote added").await?; state.client.privmsg(&channel, "Quote added").await?;
} else { } else {
state.client.privmsg(&channel, "A database error has occurred").await?; state
.client
.privmsg(&channel, "A database error has occurred")
.await?;
} }
} else { } else {
state.client.privmsg(&channel, "No previous messages to grab").await?; state
.client
.privmsg(&channel, "No previous messages to grab")
.await?;
} }
} else { } else {
state.client.privmsg(&channel, "No nickname to grab").await?; state
.client
.privmsg(&channel, "No nickname to grab")
.await?;
} }
} }
"quot" => { "quot" => {