pacbot/src/commands/mod.rs
2020-06-09 20:34:31 +02:00

130 lines
3.9 KiB
Rust

use serde::de::DeserializeOwned;
use serenity::model::channel::Message;
use serenity::model::gateway::{Activity, Ready};
use serenity::model::id::ChannelId;
use serenity::prelude::*;
use std::fmt;
mod apt;
mod aur;
mod dnf;
mod man;
mod nix;
mod pacman;
mod xbps;
extern crate reqwest;
pub struct Handler;
pub type CommandHandler = dyn Fn(Context, Message, Vec<&str>);
struct Command {
trigger: String,
handler: Box<CommandHandler>,
}
unsafe impl Sync for Command {}
const PREFIX: char = '!';
impl EventHandler for Handler {
fn message(&self, ctx: Context, msg: Message) {
if msg.content.starts_with(PREFIX) {
if let Some(command) = COMMANDS
.iter()
.find(|&c| msg.content.starts_with(&c.trigger))
{
let content = msg.content.clone();
let mut args = content.split_whitespace().collect::<Vec<_>>();
// the prefix + trigger
args.remove(0);
if args.is_empty() {
send(
msg.channel_id,
"Error: expected at least 1 additional argument",
&ctx,
);
return;
}
(*command.handler)(ctx, msg, args);
};
}
}
fn ready(&self, ctx: Context, bot_data: Ready) {
println!("Bot running and serving {} servers", bot_data.guilds.len());
ctx.reset_presence();
ctx.set_activity(Activity::listening(&format!("{}pb help", PREFIX)));
}
}
fn help(ctx: Context, msg: Message, _args: Vec<&str>) {
let command_triggers = COMMANDS
.iter()
.filter(|&c| !c.trigger.contains("help"))
.map(|c| c.trigger.clone())
.collect::<Vec<_>>()
.join(", ");
send(msg.channel_id,
&format!("Usage: `<command> <search term>`\nSupported commands: `{}`\nSource code & information: https://git.kageru.moe/kageru/pacbot",
command_triggers), &ctx)
}
lazy_static! {
static ref COMMANDS: Vec<Command> = {
let mut command_list = Vec::new();
command_list.push(Command::new("pacman", pacman::query_pacman));
command_list.push(Command::new("apt", apt::query_apt));
// disabled because they seem to no longer have stable json urls
// command_list.push(Command::new("nix", nix::query_nix));
command_list.push(Command::new("dnf", dnf::query_dnf));
command_list.push(Command::new("aur", aur::query_aur));
command_list.push(Command::new("xbps", xbps::query_xbps));
command_list.push(Command::new("man", man::query_man));
command_list.push(Command::new("pacbot help", help));
command_list.push(Command::new("pb help", help));
command_list
};
}
impl Command {
pub fn new(trigger: &str, handler: fn(Context, Message, Vec<&str>)) -> Self {
let mut trigger_with_prefix = trigger.to_string();
trigger_with_prefix.insert(0, PREFIX);
Self {
trigger: trigger_with_prefix,
handler: Box::new(handler),
}
}
}
pub fn send(target: ChannelId, message: &str, ctx: &Context) {
if let Err(cause) = target.say(&ctx.http, message) {
println!("Could not send message: {}", cause);
}
}
pub fn respond_with_results<T: fmt::Display>(target: ChannelId, results: &[T], ctx: &Context) {
send(
target,
&format!(
"```{}```",
results
.iter()
.take(5)
.map(|p| format!("{}\n", p))
.collect::<String>()
),
ctx,
);
}
pub fn search<T: DeserializeOwned>(url: &str, fallback: impl Fn(reqwest::Error) -> T) -> T {
search_inner(url).unwrap_or_else(|e| {
println!("{:?}", e);
fallback(e)
})
}
fn search_inner<T: DeserializeOwned>(url: &str) -> Result<T, reqwest::Error> {
let resp = reqwest::get(url)?.json::<T>()?;
Ok(resp)
}