diff options
| author | Jokler <jokler.contact@gmail.com> | 2017-10-27 15:39:06 +0200 |
|---|---|---|
| committer | Jokler <jokler.contact@gmail.com> | 2017-10-27 15:39:06 +0200 |
| commit | 0139861d1e41a765b6ffde7cae37c1e16f9c053c (patch) | |
| tree | 22d6f26b998edeb06e01ad80ada2fd92ff4b4fab /src/plugin.rs | |
| parent | 4c6f2f088521cf0876b67f21a25e524983b7ada7 (diff) | |
| download | frippy-0139861d1e41a765b6ffde7cae37c1e16f9c053c.tar.gz frippy-0139861d1e41a765b6ffde7cae37c1e16f9c053c.zip | |
Refactor plugin management
ThreadedPlugins should take care of anything Plugin related now.
Diffstat (limited to 'src/plugin.rs')
| -rw-r--r-- | src/plugin.rs | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/src/plugin.rs b/src/plugin.rs index 0a4034d..d1a1f3d 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -1,4 +1,8 @@ use std::fmt; +use std::collections::HashMap; +use std::thread::spawn; +use std::sync::{Arc, Mutex}; + use irc::client::prelude::*; use irc::error::Error as IrcError; @@ -18,3 +22,147 @@ pub struct PluginCommand { pub target: String, pub tokens: Vec<String>, } + +impl PluginCommand { + pub fn from(nick: &str, message: &Message) -> Option<PluginCommand> { + + // Get the actual message out of PRIVMSG + if let Command::PRIVMSG(_, ref content) = message.command { + + // Split content by spaces and filter empty tokens + let mut tokens: Vec<String> = content.split(' ').map(ToOwned::to_owned).collect(); + + // Commands start with our name + if tokens[0].to_lowercase().starts_with(nick) { + + // Remove the bot's name from the first token + tokens[0].drain(..nick.len()); + + // We assume that only ':' and ',' are used as suffixes on IRC + // If there are any other chars we assume that it is not ment for the bot + tokens[0] = tokens[0] + .chars() + .filter(|&c| !":,".contains(c)) + .collect(); + if !tokens[0].is_empty() { + return None; + } + + // The first token contained the name of the bot + tokens.remove(0); + + Some(PluginCommand { + source: message.source_nickname().unwrap().to_string(), + target: message.response_target().unwrap().to_string(), + tokens: tokens, + }) + } else { + None + } + } else { + None + } + } +} + +// Lock the mutex and ignore if it is poisoned +macro_rules! lock_plugin { + ($e:expr) => { + match $e.lock() { + Ok(plugin) => plugin, + Err(poisoned) => poisoned.into_inner(), + } + } +} + +#[derive(Clone, Debug)] +pub struct ThreadedPlugins { + plugins: HashMap<String, Arc<Mutex<Plugin>>>, +} + +impl ThreadedPlugins { + pub fn new() -> ThreadedPlugins { + ThreadedPlugins { plugins: HashMap::new() } + } + + pub fn add<T: Plugin + 'static>(&mut self, plugin: T) { + let name = plugin.name().to_lowercase(); + let safe_plugin = Arc::new(Mutex::new(plugin)); + + self.plugins.insert(name, safe_plugin); + } + + pub fn execute_plugins(&mut self, server: &IrcServer, message: Arc<Message>) { + + for (name, plugin) in self.plugins.clone() { + // Send the message to the plugin if the plugin needs it + if lock_plugin!(plugin).is_allowed(server, &message) { + + // Clone everything before the move + // The server uses an Arc internally too + let plugin = Arc::clone(&plugin); + let message = Arc::clone(&message); + let server = server.clone(); + + // Execute the plugin in another thread + spawn(move || { + if let Err(e) = lock_plugin!(plugin).execute(&server, &message) { + error!("Error in {} - {}", name, e); + }; + }); + } + } + } + + pub fn handle_command(&mut self, server: &IrcServer, mut command: PluginCommand) -> Result<(), IrcError> { + + if !command.tokens.iter().any(|s| !s.is_empty()) { + let help = format!("Use \"{} help\" to get help", server.current_nickname()); + return server.send_notice(&command.source, &help); + } + + if &command.tokens[0].to_lowercase() == "help" { + return self.send_help_message(server, &command); + } + + // Check if the command is for this plugin + if let Some(plugin) = self.plugins.get(&command.tokens[0].to_lowercase()) { + + // The first token contains the name of the plugin + let name = command.tokens.remove(0); + + // Clone for the move - the server uses an Arc internally + let server = server.clone(); + let plugin = Arc::clone(plugin); + spawn(move || { + if let Err(e) = lock_plugin!(plugin).command(&server, command) { + error!("Error in {} command - {}", name, e); + }; + }); + + Ok(()) + + } else { + let help = format!("\"{} {}\" is not a command, \ + try \"{0} help\" instead.", + server.current_nickname(), + command.tokens[0]); + + server.send_notice(&command.source, &help) + } + } + + fn send_help_message(&self, server: &IrcServer, command: &PluginCommand) -> Result<(), IrcError> { + server.send_notice(&command.source, "Help has not been added yet.") + } +} + +impl fmt::Display for ThreadedPlugins { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let plugin_names = self.plugins + .iter() + .map(|(_, p)| lock_plugin!(p).name().to_string()) + .collect::<Vec<String>>(); + write!(f, "{}", plugin_names.join(", ")) + } +} |
