Give access to the database to every command
This commit is contained in:
parent
7b38776255
commit
aaa3d1bc8a
27
src/bot.rs
27
src/bot.rs
|
@ -4,29 +4,31 @@ use fancy_regex::{Captures, Regex};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use tokio::sync::{Mutex, RwLock};
|
use tokio::sync::{Mutex, RwLock};
|
||||||
|
|
||||||
fn separate_to_space(str: &str, prefix_len: usize) -> (&str, Option<&str>) {
|
fn dissect<'a>(prefix: &str, str: &'a str) -> Option<(&'a str, Option<&'a str>)> {
|
||||||
|
let str = str.strip_prefix(prefix)?;
|
||||||
if let Some(o) = str.find(' ') {
|
if let Some(o) = str.find(' ') {
|
||||||
(&str[prefix_len..o], Some(&str[o + 1..]))
|
Some((&str[..o], Some(&str[o + 1..])))
|
||||||
} else {
|
} else {
|
||||||
(&str[prefix_len..], None)
|
Some((str, None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait Trigger {
|
pub trait Trigger {
|
||||||
async fn execute<'a>(&mut self, msg: Message<'a>, captures: Captures<'a>) -> anyhow::Result<String>;
|
async fn execute<'a>(&mut self, msg: Context<'a>, captures: Captures<'a>) -> anyhow::Result<String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait Command {
|
pub trait Command {
|
||||||
async fn execute(&mut self, msg: Message<'_>) -> anyhow::Result<String>;
|
async fn execute(&mut self, msg: Context<'_>) -> anyhow::Result<String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Message<'a> {
|
pub struct Context<'a> {
|
||||||
pub last_msg: &'a RwLock<HashMap<String, String>>,
|
pub last_msg: &'a RwLock<HashMap<String, String>>,
|
||||||
pub author: &'a str,
|
pub author: &'a str,
|
||||||
// in case of triggers, this is always Some(...)
|
// in case of triggers, this is always Some(...)
|
||||||
pub content: Option<&'a str>,
|
pub content: Option<&'a str>,
|
||||||
|
pub db: &'a ExecutorConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Bot<SF: Fn(String, String) -> anyhow::Result<()>> {
|
pub struct Bot<SF: Fn(String, String) -> anyhow::Result<()>> {
|
||||||
|
@ -64,13 +66,13 @@ impl<SF: Fn(String, String) -> anyhow::Result<()>> Bot<SF> {
|
||||||
author: &str,
|
author: &str,
|
||||||
content: &str,
|
content: &str,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
if content.starts_with(&self.prefix) {
|
if let Some((command, remainder)) = dissect(&self.prefix, content) {
|
||||||
let (command, remainder) = separate_to_space(content, self.prefix.len());
|
|
||||||
if let Some(handler) = self.commands.get(command) {
|
if let Some(handler) = self.commands.get(command) {
|
||||||
let msg = Message {
|
let msg = Context {
|
||||||
last_msg: &self.last_msg,
|
last_msg: &self.last_msg,
|
||||||
author,
|
author,
|
||||||
content: remainder
|
content: remainder,
|
||||||
|
db: &self.db
|
||||||
};
|
};
|
||||||
return (self.sendmsg)(origin.into(), handler.lock().await.execute(msg).await?)
|
return (self.sendmsg)(origin.into(), handler.lock().await.execute(msg).await?)
|
||||||
}
|
}
|
||||||
|
@ -79,10 +81,11 @@ impl<SF: Fn(String, String) -> anyhow::Result<()>> Bot<SF> {
|
||||||
for trigger in &self.triggers {
|
for trigger in &self.triggers {
|
||||||
let captures = trigger.0.captures(content)?;
|
let captures = trigger.0.captures(content)?;
|
||||||
if let Some(captures) = captures {
|
if let Some(captures) = captures {
|
||||||
let msg = Message {
|
let msg = Context {
|
||||||
last_msg: &self.last_msg,
|
last_msg: &self.last_msg,
|
||||||
author,
|
author,
|
||||||
content: Some(content)
|
content: Some(content),
|
||||||
|
db: &self.db
|
||||||
};
|
};
|
||||||
return (self.sendmsg)(origin.into(), trigger.1.lock().await.execute(msg, captures).await?)
|
return (self.sendmsg)(origin.into(), trigger.1.lock().await.execute(msg, captures).await?)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use crate::bot::{Command, Message};
|
use crate::bot::{Command, Context};
|
||||||
|
|
||||||
pub struct LastMsg;
|
pub struct LastMsg;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Command for LastMsg {
|
impl Command for LastMsg {
|
||||||
//noinspection RsNeedlessLifetimes
|
//noinspection RsNeedlessLifetimes
|
||||||
async fn execute<'a>(&mut self, msg: Message<'a>) -> anyhow::Result<String> {
|
async fn execute<'a>(&mut self, msg: Context<'a>) -> anyhow::Result<String> {
|
||||||
let nick = msg.content.unwrap_or(msg.author);
|
let nick = msg.content.unwrap_or(msg.author);
|
||||||
let lastmsg = msg.last_msg.read().await;
|
let lastmsg = msg.last_msg.read().await;
|
||||||
Ok(format!("{}: {:?}", nick, lastmsg.get(nick)))
|
Ok(format!("{}: {:?}", nick, lastmsg.get(nick)))
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use meval::Context;
|
use crate::bot::{Command, Context};
|
||||||
use crate::bot::{Command, Message};
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Eval {
|
pub struct Eval {
|
||||||
|
@ -10,10 +9,10 @@ pub struct Eval {
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Command for Eval {
|
impl Command for Eval {
|
||||||
async fn execute(&mut self, msg: Message<'_>) -> anyhow::Result<String> {
|
async fn execute(&mut self, msg: Context<'_>) -> anyhow::Result<String> {
|
||||||
if let Some(expr) = msg.content {
|
if let Some(expr) = msg.content {
|
||||||
let last_eval = self.last_eval.entry(msg.author.into()).or_insert(0.0);
|
let last_eval = self.last_eval.entry(msg.author.into()).or_insert(0.0);
|
||||||
let mut meval_ctx = Context::new();
|
let mut meval_ctx = meval::Context::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;
|
||||||
Ok(format!("{} = {}", expr, value))
|
Ok(format!("{} = {}", expr, value))
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::bot::{Message, Command};
|
use crate::bot::{Context, Command};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
const HELP: &str = concat!(
|
const HELP: &str = concat!(
|
||||||
|
@ -13,7 +13,7 @@ pub struct Help;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Command for Help {
|
impl Command for Help {
|
||||||
async fn execute(&mut self, _msg: Message<'_>) -> anyhow::Result<String> {
|
async fn execute(&mut self, _msg: Context<'_>) -> anyhow::Result<String> {
|
||||||
Ok(HELP.into())
|
Ok(HELP.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::bot::{Command, Message};
|
use crate::bot::{Command, Context};
|
||||||
use arrayvec::ArrayString;
|
use arrayvec::ArrayString;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
@ -111,7 +111,7 @@ enum LeekCommand {
|
||||||
Mock,
|
Mock,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn execute_leek(cmd: LeekCommand, msg: &Message<'_>) -> anyhow::Result<String> {
|
async fn execute_leek(cmd: LeekCommand, msg: &Context<'_>) -> anyhow::Result<String> {
|
||||||
let nick = msg.content.unwrap_or(msg.author);
|
let nick = msg.content.unwrap_or(msg.author);
|
||||||
match msg.last_msg.read().await.get(nick) {
|
match msg.last_msg.read().await.get(nick) {
|
||||||
Some(msg) => Ok(match cmd {
|
Some(msg) => Ok(match cmd {
|
||||||
|
@ -130,21 +130,21 @@ pub struct Mock;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Command for Owo {
|
impl Command for Owo {
|
||||||
async fn execute(&mut self, msg: Message<'_>) -> anyhow::Result<String> {
|
async fn execute(&mut self, msg: Context<'_>) -> anyhow::Result<String> {
|
||||||
execute_leek(LeekCommand::Owo, &msg).await
|
execute_leek(LeekCommand::Owo, &msg).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Command for Leet {
|
impl Command for Leet {
|
||||||
async fn execute(&mut self, msg: Message<'_>) -> anyhow::Result<String> {
|
async fn execute(&mut self, msg: Context<'_>) -> anyhow::Result<String> {
|
||||||
execute_leek(LeekCommand::Leet, &msg).await
|
execute_leek(LeekCommand::Leet, &msg).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Command for Mock {
|
impl Command for Mock {
|
||||||
async fn execute(&mut self, msg: Message<'_>) -> anyhow::Result<String> {
|
async fn execute(&mut self, msg: Context<'_>) -> anyhow::Result<String> {
|
||||||
execute_leek(LeekCommand::Mock, &msg).await
|
execute_leek(LeekCommand::Mock, &msg).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use fancy_regex::Captures;
|
use fancy_regex::Captures;
|
||||||
use crate::bot::{Message, Trigger};
|
use crate::bot::{Context, Trigger};
|
||||||
|
|
||||||
pub struct Sed;
|
pub struct Sed;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Trigger for Sed {
|
impl Trigger for Sed {
|
||||||
async fn execute<'a>(&mut self, msg: Message<'a>, captures: Captures<'a>) -> anyhow::Result<String> {
|
async fn execute<'a>(&mut self, msg: Context<'a>, captures: Captures<'a>) -> anyhow::Result<String> {
|
||||||
let foreign_author;
|
let foreign_author;
|
||||||
let author = if let Some(author) = captures.name("u").map(|m| m.as_str()) {
|
let author = if let Some(author) = captures.name("u").map(|m| m.as_str()) {
|
||||||
foreign_author = true;
|
foreign_author = true;
|
||||||
|
|
|
@ -3,7 +3,7 @@ use async_trait::async_trait;
|
||||||
use fancy_regex::Captures;
|
use fancy_regex::Captures;
|
||||||
use rspotify::clients::BaseClient;
|
use rspotify::clients::BaseClient;
|
||||||
use rspotify::model::{Id, PlayableItem};
|
use rspotify::model::{Id, PlayableItem};
|
||||||
use crate::bot::{Message, Trigger};
|
use crate::bot::{Context, Trigger};
|
||||||
|
|
||||||
pub struct Spotify {
|
pub struct Spotify {
|
||||||
spotify: ClientCredsSpotify,
|
spotify: ClientCredsSpotify,
|
||||||
|
@ -21,7 +21,7 @@ impl Spotify {
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Trigger for Spotify {
|
impl Trigger for Spotify {
|
||||||
async fn execute<'a>(&mut self, msg: Message<'a>, captures: Captures<'a>) -> anyhow::Result<String> {
|
async fn execute<'a>(&mut self, msg: Context<'a>, captures: Captures<'a>) -> anyhow::Result<String> {
|
||||||
let tp_group = captures.get(1).unwrap();
|
let tp_group = captures.get(1).unwrap();
|
||||||
let id_group = captures.get(2).unwrap();
|
let id_group = captures.get(2).unwrap();
|
||||||
resolve_spotify(
|
resolve_spotify(
|
||||||
|
|
|
@ -2,7 +2,7 @@ use async_trait::async_trait;
|
||||||
use fancy_regex::{Captures, Regex};
|
use fancy_regex::{Captures, Regex};
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use htmlescape::decode_html;
|
use htmlescape::decode_html;
|
||||||
use crate::bot::{Message, Trigger};
|
use crate::bot::{Context, Trigger};
|
||||||
|
|
||||||
pub struct Title {
|
pub struct Title {
|
||||||
http: Client,
|
http: Client,
|
||||||
|
@ -20,7 +20,7 @@ impl Title {
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Trigger for Title {
|
impl Trigger for Title {
|
||||||
async fn execute<'a>(&mut self, _msg: Message<'a>, captures: Captures<'a>) -> anyhow::Result<String> {
|
async fn execute<'a>(&mut self, _msg: Context<'a>, captures: Captures<'a>) -> anyhow::Result<String> {
|
||||||
let url = captures.get(0).unwrap().as_str();
|
let url = captures.get(0).unwrap().as_str();
|
||||||
tracing::debug!("url: {}", url);
|
tracing::debug!("url: {}", url);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::bot::{Message, Command};
|
use crate::bot::{Context, Command};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
@ -10,7 +10,7 @@ pub struct Waifu {
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Command for Waifu {
|
impl Command for Waifu {
|
||||||
async fn execute(&mut self, msg: Message<'_>) -> anyhow::Result<String> {
|
async fn execute(&mut self, msg: Context<'_>) -> anyhow::Result<String> {
|
||||||
let category = msg.content.unwrap_or("waifu");
|
let category = msg.content.unwrap_or("waifu");
|
||||||
let request = self.http.get(format!("https://api.waifu.pics/sfw/{}", category)).build()?;
|
let request = self.http.get(format!("https://api.waifu.pics/sfw/{}", category)).build()?;
|
||||||
let response = self.http.execute(request)
|
let response = self.http.execute(request)
|
||||||
|
|
20
src/main.rs
20
src/main.rs
|
@ -51,12 +51,6 @@ async fn terminate_signal() {
|
||||||
let _ = ctrlc.recv().await;
|
let _ = ctrlc.recv().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AppState<SF: Fn(String, String) -> anyhow::Result<()>> {
|
|
||||||
client: Arc<Client>,
|
|
||||||
stream: ClientStream,
|
|
||||||
bot: Bot<SF>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
tracing_subscriber::fmt()
|
tracing_subscriber::fmt()
|
||||||
|
@ -119,13 +113,8 @@ async fn main() -> anyhow::Result<()> {
|
||||||
bot.add_command("lastmsg".into(), LastMsg);
|
bot.add_command("lastmsg".into(), LastMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let state = AppState {
|
|
||||||
client: client.clone(),
|
|
||||||
stream,
|
|
||||||
bot,
|
|
||||||
};
|
|
||||||
let message_loop_task = tokio::spawn(async move {
|
let message_loop_task = tokio::spawn(async move {
|
||||||
if let Err(e) = message_loop(state).await {
|
if let Err(e) = message_loop(stream, bot).await {
|
||||||
let _err = etx.send(e);
|
let _err = etx.send(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -160,16 +149,17 @@ async fn main() -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn message_loop<SF: Fn(String, String) -> anyhow::Result<()>>(
|
async fn message_loop<SF: Fn(String, String) -> anyhow::Result<()>>(
|
||||||
mut state: AppState<SF>,
|
mut stream: ClientStream,
|
||||||
|
bot: Bot<SF>
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
while let Some(message) = state.stream.next().await.transpose()? {
|
while let Some(message) = stream.next().await.transpose()? {
|
||||||
if let Command::PRIVMSG(ref origin, content) = message.command {
|
if let Command::PRIVMSG(ref origin, content) = message.command {
|
||||||
if origin.is_channel_name() {
|
if origin.is_channel_name() {
|
||||||
if let Some(author) = message.prefix.as_ref().and_then(|p| match p {
|
if let Some(author) = message.prefix.as_ref().and_then(|p| match p {
|
||||||
Prefix::Nickname(name, _, _) => Some(&name[..]),
|
Prefix::Nickname(name, _, _) => Some(&name[..]),
|
||||||
_ => None,
|
_ => None,
|
||||||
}) {
|
}) {
|
||||||
state.bot.handle_message(origin, author, &content).await
|
bot.handle_message(origin, author, &content).await
|
||||||
} else {
|
} else {
|
||||||
tracing::warn!("Couldn't get the author for a message");
|
tracing::warn!("Couldn't get the author for a message");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue