use serenity::model::channel::Message; use std::fmt; use serenity::model::id::ChannelId; use serde::de::DeserializeOwned; use serenity::prelude::*; mod apt; mod pacman; extern crate reqwest; pub struct Handler; pub type CommandHandler = dyn Fn(Context, Message, Vec<&str>); struct Command { trigger: String, handler: Box, } 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() .filter(|&c| msg.content.starts_with(&c.trigger)) .next() { let content = msg.content.clone(); let mut args = content.split_whitespace().collect::>(); // the prefix + trigger args.remove(0); if args.len() == 0 { send( msg.channel_id, "Error: expected at least 1 additional argument", &ctx, ); return; } (*command.handler)(ctx, msg, args); }; } } } lazy_static! { static ref COMMANDS: Vec = { let mut command_list = Vec::new(); command_list.push(Command::new("pacman", pacman::query_pacman)); command_list.push(Command::new("apt", apt::query_apt)); 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(target: ChannelId, results: &Vec, ctx: &Context) { send( target, &format!( "```{}```", results .into_iter() .take(5) .map(|p| format!("{}\n", p)) .collect::() ), ctx, ); } pub fn search(url: &str, fallback: T) -> T { return search_inner(url).unwrap_or(fallback); } fn search_inner(url: &str) -> Result { let resp = reqwest::get(url)?.json::()?; Ok(resp) }