buscemi/src/commands.rs

143 lines
4.0 KiB
Rust

use std::{collections::HashMap, sync::Mutex};
use rand::seq::SliceRandom;
/// this is where we keep data for the bot
// can we eventually serialize this to disk for cheap persistence?
#[derive(Default)]
pub struct Data {
facts: Mutex<HashMap<String, String>>,
}
type Error = Box<dyn std::error::Error + Send + Sync>;
type Context<'a> = poise::Context<'a, Data, Error>;
/// all of the commands supported by `buscemi`
pub fn commands() -> Vec<poise::Command<Data, Error>> {
vec![help(), ping(), eight_ball(), know(), what()]
}
/// print helpful information
#[poise::command(prefix_command, slash_command)]
async fn help(
ctx: Context<'_>,
#[description = "optional command to show help about"]
#[autocomplete = "poise::builtins::autocomplete_command"]
command: Option<String>,
) -> Result<(), Error> {
poise::builtins::help(ctx, command.as_deref(), Default::default()).await?;
Ok(())
}
/// check that the bot is working
#[poise::command(prefix_command, slash_command)]
async fn ping(ctx: Context<'_>) -> Result<(), Error> {
let vowel = {
let mut rng = rand::thread_rng();
let vowels = ['a', 'e', 'o', 'u', 'y'];
vowels.choose(&mut rng).copied().unwrap()
};
ctx.say(format!("p{vowel}ng")).await?;
Ok(())
}
/// leave your important decisions up to chance
#[poise::command(prefix_command, slash_command, rename = "8ball")]
async fn eight_ball(
ctx: Context<'_>,
#[rest]
#[rename = "question"]
#[description = "a yes or no question"]
_question: String,
) -> Result<(), Error> {
let answer = {
let mut rng = rand::thread_rng();
let answers = [
"It is certain.",
"It is decidedly so.",
"Without a doubt.",
"Yes definitely.",
"You may rely on it.",
"As I see it, yes.",
"Most likely.",
"Outlook good.",
"Yes.",
"Signs point to yes.",
"Reply hazy, try again.",
"Ask again later.",
"Better not tell you now.",
"Cannot predict now.",
"Concentrate and ask again.",
"Don't count on it.",
"My reply is no.",
"My sources say no.",
"Outlook not so good.",
"Very doubtful.",
];
answers.choose(&mut rng).copied().unwrap()
};
ctx.say(answer).await?;
Ok(())
}
/// commit some important knowledge to memory
///
/// usage:
/// ```
/// @buscemi (remember|know) [that] <OBJECT> [is] <DEFINITION>
/// ```
/// examples:
/// ```
/// @buscemi remember that x is y
/// @buscemi know foo is bar
/// ```
/// technically works but confusing:
/// ```
/// @buscemi remember 1 2
/// ```
#[poise::command(prefix_command, slash_command, aliases("remember"))]
async fn know(
ctx: Context<'_>,
#[description = "the thing you want to define"] that: String,
#[rest]
#[description = "the definition of the thing"]
is: String,
) -> Result<(), Error> {
let (that, is) = if that.as_str() == "that" {
is.split_once(' ').unwrap()
} else {
(that.as_str(), is.as_str())
};
let is = is.strip_prefix("is ").unwrap_or(is);
let old = {
let mut facts = ctx.data().facts.lock().unwrap();
facts.insert(that.to_string(), is.to_string())
};
let message = if let Some(old) = old {
format!("ok, {that} is {is} (changed from {old})")
} else {
format!("ok, {that} is {is}")
};
ctx.say(message).await?;
Ok(())
}
/// recall some important knowledge
#[poise::command(prefix_command, slash_command)]
async fn what(
ctx: Context<'_>,
#[rest]
#[description = "the thing you want to look up"]
is: String,
) -> Result<(), Error> {
let message = {
let facts = ctx.data().facts.lock().unwrap();
let is = is.strip_prefix("is ").unwrap_or(is.as_str());
facts.get(is).map_or_else(
|| format!("i don't know what {is} is"),
|definition| format!("{is} is {definition}"),
)
};
ctx.say(message).await?;
Ok(())
}