summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs171
-rw-r--r--src/main.rs166
-rw-r--r--src/plugin.rs52
-rw-r--r--src/plugins/currency.rs115
-rw-r--r--src/plugins/emoji.rs51
-rw-r--r--src/plugins/factoids/mod.rs112
-rw-r--r--src/plugins/help.rs22
-rw-r--r--src/plugins/keepnick.rs53
-rw-r--r--src/plugins/mod.rs2
-rw-r--r--src/plugins/tell.rs140
-rw-r--r--src/plugins/url.rs59
11 files changed, 647 insertions, 296 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 54c672e..bd2e302 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,29 +1,27 @@
-#![cfg_attr(feature="clippy", feature(plugin))]
-#![cfg_attr(feature="clippy", plugin(clippy))]
+#![cfg_attr(feature = "clippy", feature(plugin))]
+#![cfg_attr(feature = "clippy", plugin(clippy))]
//! Frippy is an IRC bot that runs plugins on each message
//! received.
//!
//! ## Examples
//! ```no_run
-//! # extern crate tokio_core;
-//! # extern crate futures;
+//! # extern crate irc;
//! # extern crate frippy;
//! # fn main() {
//! use frippy::{plugins, Config, Bot};
-//! use tokio_core::reactor::Core;
-//! use futures::future;
+//! use irc::client::reactor::IrcReactor;
//!
//! let config = Config::load("config.toml").unwrap();
-//! let mut reactor = Core::new().unwrap();
+//! let mut reactor = IrcReactor::new().unwrap();
//! let mut bot = Bot::new();
//!
//! bot.add_plugin(plugins::Help::new());
//! bot.add_plugin(plugins::Emoji::new());
//! bot.add_plugin(plugins::Currency::new());
//!
-//! bot.connect(&mut reactor, &config);
-//! reactor.run(future::empty::<(), ()>()).unwrap();
+//! bot.connect(&mut reactor, &config).unwrap();
+//! reactor.run().unwrap();
//! # }
//! ```
//!
@@ -36,16 +34,13 @@
extern crate diesel;
#[macro_use]
-extern crate log;
+extern crate frippy_derive;
#[macro_use]
extern crate lazy_static;
#[macro_use]
-extern crate frippy_derive;
+extern crate log;
extern crate irc;
-extern crate futures;
-extern crate tokio_core;
-extern crate regex;
extern crate chrono;
extern crate time;
@@ -58,9 +53,8 @@ use std::fmt;
use std::thread::spawn;
use std::sync::Arc;
-use tokio_core::reactor::Core;
pub use irc::client::prelude::*;
-pub use irc::error::Error as IrcError;
+pub use irc::error::IrcError;
use plugin::*;
@@ -72,7 +66,7 @@ pub struct Bot {
impl Bot {
/// Creates a `Bot`.
- /// By itself the bot only responds to a few simple ctcp commands
+ /// By itself the bot only responds to a few simple CTCP commands
/// defined per config file.
/// Any other functionality has to be provided by plugins
/// which need to implement [`Plugin`](plugin/trait.Plugin.html).
@@ -83,10 +77,12 @@ impl Bot {
/// let mut bot = Bot::new();
/// ```
pub fn new() -> Bot {
- Bot { plugins: ThreadedPlugins::new() }
+ Bot {
+ plugins: ThreadedPlugins::new(),
+ }
}
- /// Adds the plugin.
+ /// Adds the [`Plugin`](plugin/trait.Plugin.html).
/// These plugins will be used to evaluate incoming messages from IRC.
///
/// # Examples
@@ -100,7 +96,7 @@ impl Bot {
self.plugins.add(plugin);
}
- /// Removes a plugin based on its name.
+ /// Removes a [`Plugin`](plugin/trait.Plugin.html) based on its name.
/// The binary currently uses this to disable plugins
/// based on user configuration.
///
@@ -116,66 +112,62 @@ impl Bot {
self.plugins.remove(name)
}
- /// This connects the `Bot` to IRC and adds a task
- /// to the Core that was supplied.
+ /// This connects the `Bot` to IRC and creates a task on the
+ /// [`IrcReactor`](../irc/client/reactor/struct.IrcReactor.html)
+ /// which returns an Ok if the connection was cleanly closed and
+ /// an Err if the connection was lost.
///
- /// You need to run the core, so that frippy
- /// can do its work.
+ /// You need to run the [`IrcReactor`](../irc/client/reactor/struct.IrcReactor.html),
+ /// so that the `Bot`
+ /// can actually do its work.
///
/// # Examples
/// ```no_run
- /// # extern crate tokio_core;
- /// # extern crate futures;
+ /// # extern crate irc;
/// # extern crate frippy;
/// # fn main() {
/// use frippy::{Config, Bot};
- /// use tokio_core::reactor::Core;
- /// use futures::future;
+ /// use irc::client::reactor::IrcReactor;
///
/// let config = Config::load("config.toml").unwrap();
- /// let mut reactor = Core::new().unwrap();
+ /// let mut reactor = IrcReactor::new().unwrap();
/// let mut bot = Bot::new();
///
- /// bot.connect(&mut reactor, &config);
- /// reactor.run(future::empty::<(), ()>()).unwrap();
+ /// bot.connect(&mut reactor, &config).unwrap();
+ /// reactor.run().unwrap();
/// # }
/// ```
- pub fn connect(&self, reactor: &mut Core, config: &Config) {
+ pub fn connect(&self, reactor: &mut IrcReactor, config: &Config) -> Result<(), String> {
info!("Plugins loaded: {}", self.plugins);
- let server =
- match IrcServer::new_future(reactor.handle(), config).and_then(|f| {reactor.run(f)}) {
- Ok(v) => v,
- Err(e) => {
- error!("Failed to connect to IRC server: {}", e);
- return;
- }
- };
+ let client = match reactor.prepare_client_and_connect(config) {
+ Ok(v) => v,
+ Err(e) => return Err(format!("Failed to connect: {}", e)),
+ };
info!("Connected to IRC server");
- match server.identify() {
+ match client.identify() {
Ok(_) => info!("Identified"),
- Err(e) => error!("Failed to identify: {}", e),
+ Err(e) => return Err(format!("Failed to identify: {}", e)),
};
// TODO Verify if we actually need to clone plugins twice
let plugins = self.plugins.clone();
- let task = server
- .stream()
- .for_each(move |message| process_msg(&server, plugins.clone(), message))
- .map_err(|e| error!("Failed to process message: {}", e));
+ reactor.register_client_with_handler(client, move |client, message| {
+ process_msg(client, plugins.clone(), message)
+ });
- reactor.handle().spawn(task);
+ Ok(())
}
}
-fn process_msg(server: &IrcServer,
- mut plugins: ThreadedPlugins,
- message: Message)
- -> Result<(), IrcError> {
-
+fn process_msg(
+ server: &IrcClient,
+ mut plugins: ThreadedPlugins,
+ message: Message,
+) -> Result<(), IrcError> {
// Log any channels we join
if let Command::JOIN(ref channel, _, _) = message.command {
if message.source_nickname().unwrap() == server.current_nickname() {
@@ -205,7 +197,9 @@ struct ThreadedPlugins {
impl ThreadedPlugins {
pub fn new() -> ThreadedPlugins {
- ThreadedPlugins { plugins: HashMap::new() }
+ ThreadedPlugins {
+ plugins: HashMap::new(),
+ }
}
pub fn add<T: Plugin + 'static>(&mut self, plugin: T) {
@@ -219,37 +213,42 @@ impl ThreadedPlugins {
self.plugins.remove(&name.to_lowercase()).map(|_| ())
}
- pub fn execute_plugins(&mut self, server: &IrcServer, message: Message) {
+ pub fn execute_plugins(&mut self, server: &IrcClient, message: Message) {
let message = Arc::new(message);
for (name, plugin) in self.plugins.clone() {
// Send the message to the plugin if the plugin needs it
- if plugin.is_allowed(server, &message) {
-
- debug!("Executing {} with {}",
- name,
- message.to_string().replace("\r\n", ""));
-
- // 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) = plugin.execute(&server, &message) {
- error!("Error in {} - {}", name, e);
- };
- });
+ match plugin.execute(server, &message) {
+ ExecutionStatus::Done => (),
+ ExecutionStatus::Err(e) => error!("Error in {} - {}", name, e),
+ ExecutionStatus::RequiresThread => {
+ debug!(
+ "Spawning thread to execute {} with {}",
+ name,
+ message.to_string().replace("\r\n", "")
+ );
+
+ // 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) = plugin.execute_threaded(&server, &message) {
+ error!("Error in {} - {}", name, e);
+ };
+ });
+ }
}
}
}
- pub fn handle_command(&mut self,
- server: &IrcServer,
- mut command: PluginCommand)
- -> Result<(), IrcError> {
-
+ pub fn handle_command(
+ &mut self,
+ server: &IrcClient,
+ 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);
@@ -257,7 +256,6 @@ impl ThreadedPlugins {
// 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);
@@ -267,18 +265,19 @@ impl ThreadedPlugins {
let server = server.clone();
let plugin = Arc::clone(plugin);
spawn(move || {
- if let Err(e) = plugin.command(&server, command) {
- error!("Error in {} command - {}", name, e);
- };
- });
+ if let Err(e) = 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]);
+ let help = format!(
+ "\"{} {}\" is not a command, \
+ try \"{0} help\" instead.",
+ server.current_nickname(),
+ command.tokens[0]
+ );
server.send_notice(&command.source, &help)
}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..cb4e384
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,166 @@
+#![cfg_attr(feature = "clippy", feature(plugin))]
+#![cfg_attr(feature = "clippy", plugin(clippy))]
+
+extern crate frippy;
+extern crate glob;
+extern crate irc;
+extern crate time;
+
+#[cfg(feature = "mysql")]
+#[macro_use]
+extern crate diesel_migrations;
+#[cfg(feature = "mysql")]
+extern crate diesel;
+
+#[macro_use]
+extern crate log;
+
+use std::collections::HashMap;
+use log::{Level, LevelFilter, Metadata, Record};
+
+use irc::client::reactor::IrcReactor;
+use glob::glob;
+
+use frippy::plugins;
+use frippy::Config;
+
+#[cfg(feature = "mysql")]
+embed_migrations!();
+
+struct Logger;
+
+impl log::Log for Logger {
+ fn enabled(&self, metadata: &Metadata) -> bool {
+ metadata.target().contains("frippy")
+ }
+
+ fn log(&self, record: &Record) {
+ if self.enabled(record.metadata()) {
+ if record.metadata().level() >= Level::Debug {
+ println!(
+ "[{}]({}) {} -> {}",
+ time::now().rfc822(),
+ record.level(),
+ record.target(),
+ record.args()
+ );
+ } else {
+ println!(
+ "[{}]({}) {}",
+ time::now().rfc822(),
+ record.level(),
+ record.args()
+ );
+ }
+ }
+ }
+
+ fn flush(&self) {}
+}
+
+static LOGGER: Logger = Logger;
+
+fn main() {
+ log::set_max_level(if cfg!(debug_assertions) {
+ LevelFilter::Debug
+ } else {
+ LevelFilter::Info
+ });
+
+ log::set_logger(&LOGGER).unwrap();
+
+ // Load all toml files in the configs directory
+ let mut configs = Vec::new();
+ for toml in glob("configs/*.toml").unwrap() {
+ match toml {
+ Ok(path) => {
+ info!("Loading {}", path.to_str().unwrap());
+ match Config::load(path) {
+ Ok(v) => configs.push(v),
+ Err(e) => error!("Incorrect config file {}", e),
+ }
+ }
+ Err(e) => error!("Failed to read path {}", e),
+ }
+ }
+
+ // Without configs the bot would just idle
+ if configs.is_empty() {
+ error!("No config file found");
+ return;
+ }
+
+ // Create an event loop to run the connections on.
+ let mut reactor = IrcReactor::new().unwrap();
+
+ // Open a connection and add work for each config
+ for config in configs {
+ let mut disabled_plugins = None;
+ let mut mysql_url = None;
+ if let Some(ref options) = config.options {
+ if let Some(disabled) = options.get("disabled_plugins") {
+ disabled_plugins = Some(disabled
+ .split(",")
+ .map(|p| p.trim())
+ .collect::<Vec<_>>());
+ }
+
+ mysql_url = options.get("mysql_url");
+ }
+
+ let mut bot = frippy::Bot::new();
+ bot.add_plugin(plugins::Help::new());
+ bot.add_plugin(plugins::Url::new(1024));
+ bot.add_plugin(plugins::Emoji::new());
+ bot.add_plugin(plugins::Currency::new());
+ bot.add_plugin(plugins::KeepNick::new());
+ bot.add_plugin(plugins::Tell::new());
+
+ #[cfg(feature = "mysql")]
+ {
+ if let Some(url) = mysql_url {
+ use diesel;
+ use diesel::Connection;
+ match diesel::mysql::MysqlConnection::establish(url) {
+ Ok(conn) => {
+ match embedded_migrations::run(&conn) {
+ Ok(_) => {
+ bot.add_plugin(plugins::Factoids::new(conn));
+ info!("Connected to MySQL server")
+ }
+ Err(e) => {
+ bot.add_plugin(plugins::Factoids::new(HashMap::new()));
+ error!("Failed to run migrations: {}", e);
+ }
+ }
+ }
+ Err(e) => error!("Failed to connect to database: {}", e),
+ }
+ } else {
+ bot.add_plugin(plugins::Factoids::new(HashMap::new()));
+ }
+ }
+ #[cfg(not(feature = "mysql"))]
+ {
+ if let Some(_) = mysql_url {
+ error!("frippy was not built with the mysql feature")
+ }
+ bot.add_plugin(plugins::Factoids::new(HashMap::new()));
+ }
+
+
+ if let Some(disabled_plugins) = disabled_plugins {
+ for name in disabled_plugins {
+ if bot.remove_plugin(name).is_none() {
+ error!("\"{}\" was not found - could not disable", name);
+ }
+ }
+ }
+
+ bot.connect(&mut reactor, &config)
+ .expect("Failed to connect");
+ }
+
+ // Run the bots until they throw an error - an error could be loss of connection
+ reactor.run().unwrap();
+}
diff --git a/src/plugin.rs b/src/plugin.rs
index d14c129..a67d68f 100644
--- a/src/plugin.rs
+++ b/src/plugin.rs
@@ -2,20 +2,35 @@
use std::fmt;
use irc::client::prelude::*;
-use irc::error::Error as IrcError;
+use irc::error::IrcError;
+
+/// Describes if a [`Plugin`](trait.Plugin.html) is done working on a
+/// [`Message`](../../irc/proto/message/struct.Message.html) or if another thread is required.
+#[derive(Debug)]
+pub enum ExecutionStatus {
+ /// The [`Plugin`](trait.Plugin.html) does not need to do any more work on this [`Message`](../../irc/proto/message/struct.Message.html).
+ Done,
+ /// An error occured during the execution.
+ Err(IrcError),
+ /// The execution needs to be done by [`execute_threaded()`](trait.Plugin.html#tymethod.execute_threaded).
+ RequiresThread,
+}
/// `Plugin` has to be implemented for any struct that should be usable
-/// as a plugin in frippy.
+/// as a `Plugin` in frippy.
pub trait Plugin: PluginName + Send + Sync + fmt::Debug {
- /// This should return true if the `Plugin` wants to do work on the message.
- fn is_allowed(&self, server: &IrcServer, message: &Message) -> bool;
- /// Handles messages which are not commands but still necessary.
- fn execute(&self, server: &IrcServer, message: &Message) -> Result<(), IrcError>;
+ /// Handles messages which are not commands or returns [`RequiresThread`](enum.ExecutionStatus.html#variant.RequiresThread)
+ /// if [`execute_threaded()`](trait.Plugin.html#tymethod.execute_threaded) should be used instead.
+ fn execute(&self, server: &IrcClient, message: &Message) -> ExecutionStatus;
+ /// Handles messages which are not commands in a new thread.
+ fn execute_threaded(&self, server: &IrcClient, message: &Message) -> Result<(), IrcError>;
/// Handles any command directed at this plugin.
- fn command(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError>;
+ fn command(&self, server: &IrcClient, command: PluginCommand) -> Result<(), IrcError>;
+ /// Similar to [`command()`](trait.Plugin.html#tymethod.command) but return a String instead of sending messages directly to IRC.
+ fn evaluate(&self, server: &IrcClient, command: PluginCommand) -> Result<String, String>;
}
-/// `PluginName` is required by `Plugin`.
+/// `PluginName` is required by [`Plugin`](trait.Plugin.html).
///
/// To implement it simply add `#[derive(PluginName)]`
/// above the definition of the struct.
@@ -28,7 +43,7 @@ pub trait Plugin: PluginName + Send + Sync + fmt::Debug {
/// struct Foo;
/// ```
pub trait PluginName: Send + Sync + fmt::Debug {
- /// Returns the name of the plugin.
+ /// Returns the name of the `Plugin`.
fn name(&self) -> &str;
}
@@ -45,28 +60,23 @@ pub struct PluginCommand {
}
impl PluginCommand {
- /// Creates a `PluginCommand` from `Message` if it is a `PRIVMSG`
+ /// Creates a `PluginCommand` from [`Message`](../../irc/proto/message/struct.Message.html)
+ /// if it contains a [`PRIVMSG`](../../irc/proto/command/enum.Command.html#variant.PRIVMSG)
/// that starts with the provided `nick`.
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();
+ tokens[0] = tokens[0].chars().filter(|&c| !":,".contains(c)).collect();
if !tokens[0].is_empty() {
return None;
}
@@ -75,10 +85,10 @@ impl PluginCommand {
tokens.remove(0);
Some(PluginCommand {
- source: message.source_nickname().unwrap().to_string(),
- target: message.response_target().unwrap().to_string(),
- tokens: tokens,
- })
+ source: message.source_nickname().unwrap().to_string(),
+ target: message.response_target().unwrap().to_string(),
+ tokens: tokens,
+ })
} else {
None
}
diff --git a/src/plugins/currency.rs b/src/plugins/currency.rs
index 634faa2..958c8e2 100644
--- a/src/plugins/currency.rs
+++ b/src/plugins/currency.rs
@@ -6,7 +6,7 @@ use std::io::Read;
use std::num::ParseFloatError;
use irc::client::prelude::*;
-use irc::error::Error as IrcError;
+use irc::error::IrcError;
use self::reqwest::Client;
use self::reqwest::header::Connection;
@@ -23,18 +23,8 @@ struct ConvertionRequest<'a> {
target: &'a str,
}
-macro_rules! try_option {
- ($e:expr) => {
- match $e {
- Some(v) => v,
- None => { return None; }
- }
- }
-}
-
impl<'a> ConvertionRequest<'a> {
fn send(&self) -> Option<f64> {
-
let response = Client::new()
.get("https://api.fixer.io/latest")
.form(&[("base", self.source)])
@@ -44,16 +34,14 @@ impl<'a> ConvertionRequest<'a> {
match response {
Ok(mut response) => {
let mut body = String::new();
- try_option!(response.read_to_string(&mut body).ok());
+ response.read_to_string(&mut body).ok()?;
let convertion_rates: Result<Value, _> = serde_json::from_str(&body);
match convertion_rates {
Ok(convertion_rates) => {
-
- let rates: &Value = try_option!(convertion_rates.get("rates"));
- let target_rate: &Value =
- try_option!(rates.get(self.target.to_uppercase()));
- Some(self.value * try_option!(target_rate.as_f64()))
+ let rates: &Value = convertion_rates.get("rates")?;
+ let target_rate: &Value = rates.get(self.target.to_uppercase())?;
+ Some(self.value * target_rate.as_f64()?)
}
Err(_) => None,
}
@@ -68,7 +56,10 @@ impl Currency {
Currency {}
}
- fn eval_command<'a>(&self, tokens: &'a [String]) -> Result<ConvertionRequest<'a>, ParseFloatError> {
+ fn eval_command<'a>(
+ &self,
+ tokens: &'a [String],
+ ) -> Result<ConvertionRequest<'a>, ParseFloatError> {
Ok(ConvertionRequest {
value: tokens[0].parse()?,
source: &tokens[1],
@@ -76,73 +67,89 @@ impl Currency {
})
}
- fn convert(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
-
+ fn convert(&self, client: &IrcClient, command: &mut PluginCommand) -> Result<String, String> {
if command.tokens.len() < 3 {
- return self.invalid_command(server, &command);
+ return Err(self.invalid_command(client));
}
let request = match self.eval_command(&command.tokens) {
Ok(request) => request,
Err(_) => {
- return self.invalid_command(server, &command);
+ return Err(self.invalid_command(client));
}
};
match request.send() {
Some(response) => {
- let response = format!("{} {} => {:.4} {}",
- request.value,
- request.source.to_lowercase(),
- response / 1.00000000,
- request.target.to_lowercase());
-
- server.send_privmsg(&command.target, &response)
+ let response = format!(
+ "{} {} => {:.4} {}",
+ request.value,
+ request.source.to_lowercase(),
+ response / 1.00000000,
+ request.target.to_lowercase()
+ );
+
+ Ok(response)
}
- None => server.send_notice(&command.source, "Error while converting given currency"),
+ None => Err(String::from(
+ "An error occured during the conversion of the given currency",
+ )),
}
}
- fn help(&self, server: &IrcServer, command: &mut PluginCommand) -> Result<(), IrcError> {
- let help = format!("usage: {} currency value from_currency to_currency\r\n\
- example: 1.5 eur usd\r\n\
- available currencies: AUD, BGN, BRL, CAD, \
- CHF, CNY, CZK, DKK, GBP, HKD, HRK, HUF, \
- IDR, ILS, INR, JPY, KRW, MXN, MYR, NOK, \
- NZD, PHP, PLN, RON, RUB, SEK, SGD, THB, \
- TRY, USD, ZAR",
- server.current_nickname());
-
- server.send_notice(&command.source, &help)
+ fn help(&self, client: &IrcClient) -> String {
+ format!(
+ "usage: {} currency value from_currency to_currency\r\n\
+ example: {0} currency 1.5 eur usd\r\n\
+ available currencies: AUD, BGN, BRL, CAD, \
+ CHF, CNY, CZK, DKK, GBP, HKD, HRK, HUF, \
+ IDR, ILS, INR, JPY, KRW, MXN, MYR, NOK, \
+ NZD, PHP, PLN, RON, RUB, SEK, SGD, THB, \
+ TRY, USD, ZAR",
+ client.current_nickname()
+ )
}
- fn invalid_command(&self, server: &IrcServer, command: &PluginCommand) -> Result<(), IrcError> {
- let help = format!("Incorrect Command. \
- Send \"{} currency help\" for help.",
- server.current_nickname());
-
- server.send_notice(&command.source, &help)
+ fn invalid_command(&self, client: &IrcClient) -> String {
+ format!(
+ "Incorrect Command. \
+ Send \"{} currency help\" for help.",
+ client.current_nickname()
+ )
}
}
impl Plugin for Currency {
- fn is_allowed(&self, _: &IrcServer, _: &Message) -> bool {
- false
+ fn execute(&self, _: &IrcClient, _: &Message) -> ExecutionStatus {
+ ExecutionStatus::Done
}
- fn execute(&self, _: &IrcServer, _: &Message) -> Result<(), IrcError> {
+ fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), IrcError> {
panic!("Currency does not implement the execute function!")
}
- fn command(&self, server: &IrcServer, mut command: PluginCommand) -> Result<(), IrcError> {
+ fn command(&self, client: &IrcClient, mut command: PluginCommand) -> Result<(), IrcError> {
+ if command.tokens.is_empty() {
+ return client.send_notice(&command.source, &self.invalid_command(client));
+ }
+
+ match command.tokens[0].as_ref() {
+ "help" => client.send_notice(&command.source, &self.help(client)),
+ _ => match self.convert(client, &mut command) {
+ Ok(msg) => client.send_privmsg(&command.target, &msg),
+ Err(msg) => client.send_notice(&command.source, &msg),
+ },
+ }
+ }
+ fn evaluate(&self, client: &IrcClient, mut command: PluginCommand) -> Result<String, String> {
if command.tokens.is_empty() {
- return self.invalid_command(server, &command);
+ return Err(self.invalid_command(client));
}
match command.tokens[0].as_ref() {
- "help" => self.help(server, &mut command),
- _ => self.convert(server, command),
+ "help" => Ok(self.help(client)),
+ _ => self.convert(client, &mut command),
}
}
}
diff --git a/src/plugins/emoji.rs b/src/plugins/emoji.rs
index 59e2fdd..fcb04d1 100644
--- a/src/plugins/emoji.rs
+++ b/src/plugins/emoji.rs
@@ -3,7 +3,7 @@ extern crate unicode_names;
use std::fmt;
use irc::client::prelude::*;
-use irc::error::Error as IrcError;
+use irc::error::IrcError;
use plugin::*;
@@ -14,7 +14,6 @@ struct EmojiHandle {
impl fmt::Display for EmojiHandle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-
let name = match unicode_names::name(self.symbol) {
Some(sym) => sym.to_string().to_lowercase(),
None => String::from("UNKNOWN"),
@@ -36,13 +35,12 @@ impl Emoji {
Emoji {}
}
- fn emoji(&self, server: &IrcServer, content: &str, target: &str) -> Result<(), IrcError> {
- let names = self.return_emojis(content)
+ fn emoji(&self, content: &str) -> String {
+ self.return_emojis(content)
.iter()
.map(|e| e.to_string())
- .collect::<Vec<String>>();
-
- server.send_privmsg(target, &names.join(", "))
+ .collect::<Vec<String>>()
+ .join(", ")
}
fn return_emojis(&self, string: &str) -> Vec<EmojiHandle> {
@@ -53,7 +51,6 @@ impl Emoji {
count: 0,
};
-
for c in string.chars() {
if !self.is_emoji(&c) {
continue;
@@ -61,7 +58,6 @@ impl Emoji {
if current.symbol == c {
current.count += 1;
-
} else {
if current.count > 0 {
emojis.push(current);
@@ -98,25 +94,36 @@ impl Emoji {
}
impl Plugin for Emoji {
- fn is_allowed(&self, _: &IrcServer, message: &Message) -> bool {
+ fn execute(&self, client: &IrcClient, message: &Message) -> ExecutionStatus {
match message.command {
- Command::PRIVMSG(_, _) => true,
- _ => false,
+ Command::PRIVMSG(_, ref content) => match client
+ .send_privmsg(message.response_target().unwrap(), &self.emoji(content))
+ {
+ Ok(_) => ExecutionStatus::Done,
+ Err(e) => ExecutionStatus::Err(e),
+ },
+ _ => ExecutionStatus::Done,
}
}
- fn execute(&self, server: &IrcServer, message: &Message) -> Result<(), IrcError> {
- match message.command {
- Command::PRIVMSG(_, ref content) => {
- self.emoji(server, content, message.response_target().unwrap())
- }
- _ => Ok(()),
- }
+ fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), IrcError> {
+ panic!("Emoji should not use threading")
+ }
+
+ fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), IrcError> {
+ client.send_notice(
+ &command.source,
+ "This Plugin does not implement any commands.",
+ )
}
- fn command(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
- server.send_notice(&command.source,
- "This Plugin does not implement any commands.")
+ fn evaluate(&self, _: &IrcClient, command: PluginCommand) -> Result<String, String> {
+ let emojis = self.emoji(&command.tokens[0]);
+ if emojis.is_empty() {
+ Ok(emojis)
+ } else {
+ Err(String::from("No emojis were found."))
+ }
}
}
diff --git a/src/plugins/factoids/mod.rs b/src/plugins/factoids/mod.rs
index 08e8f12..49ace10 100644
--- a/src/plugins/factoids/mod.rs
+++ b/src/plugins/factoids/mod.rs
@@ -5,7 +5,7 @@ use std::str::FromStr;
use std::sync::Mutex;
use self::rlua::prelude::*;
use irc::client::prelude::*;
-use irc::error::Error as IrcError;
+use irc::error::IrcError;
use time;
use chrono::NaiveDateTime;
@@ -56,67 +56,67 @@ impl<T: Database> Factoids<T> {
}
}
- fn add(&self, server: &IrcServer, command: &mut PluginCommand) -> Result<(), IrcError> {
+ fn add(&self, client: &IrcClient, command: &mut PluginCommand) -> Result<(), IrcError> {
if command.tokens.len() < 2 {
- return self.invalid_command(server, command);
+ return self.invalid_command(client, command);
}
let name = command.tokens.remove(0);
let content = command.tokens.join(" ");
match self.create_factoid(&name, &content, &command.source) {
- Ok(v) => server.send_notice(&command.source, v),
- Err(e) => server.send_notice(&command.source, e),
+ Ok(v) => client.send_notice(&command.source, v),
+ Err(e) => client.send_notice(&command.source, e),
}
}
- fn from_url(&self, server: &IrcServer, command: &mut PluginCommand) -> Result<(), IrcError> {
+ fn from_url(&self, client: &IrcClient, command: &mut PluginCommand) -> Result<(), IrcError> {
if command.tokens.len() < 2 {
- return self.invalid_command(server, command);
+ return self.invalid_command(client, command);
}
let name = command.tokens.remove(0);
let url = &command.tokens[0];
if let Some(content) = ::utils::download(1024, url) {
match self.create_factoid(&name, &content, &command.source) {
- Ok(v) => server.send_notice(&command.source, v),
- Err(e) => server.send_notice(&command.source, e),
+ Ok(v) => client.send_notice(&command.source, v),
+ Err(e) => client.send_notice(&command.source, e),
}
} else {
- server.send_notice(&command.source, "Failed to download.")
+ client.send_notice(&command.source, "Failed to download.")
}
}
- fn remove(&self, server: &IrcServer, command: &mut PluginCommand) -> Result<(), IrcError> {
+ fn remove(&self, client: &IrcClient, command: &mut PluginCommand) -> Result<(), IrcError> {
if command.tokens.len() < 1 {
- return self.invalid_command(server, command);
+ return self.invalid_command(client, command);
}
let name = command.tokens.remove(0);
let count = match try_lock!(self.factoids).count(&name) {
Ok(c) => c,
- Err(e) => return server.send_notice(&command.source, e),
+ Err(e) => return client.send_notice(&command.source, e),
};
match try_lock!(self.factoids).delete(&name, count - 1) {
- DbResponse::Success => server.send_notice(&command.source, "Successfully removed"),
- DbResponse::Failed(e) => server.send_notice(&command.source, &e),
+ DbResponse::Success => client.send_notice(&command.source, "Successfully removed"),
+ DbResponse::Failed(e) => client.send_notice(&command.source, &e),
}
}
- fn get(&self, server: &IrcServer, command: &PluginCommand) -> Result<(), IrcError> {
+ fn get(&self, client: &IrcClient, command: &PluginCommand) -> Result<(), IrcError> {
let (name, idx) = match command.tokens.len() {
- 0 => return self.invalid_command(server, command),
+ 0 => return self.invalid_command(client, command),
1 => {
let name = &command.tokens[0];
let count = match try_lock!(self.factoids).count(name) {
Ok(c) => c,
- Err(e) => return server.send_notice(&command.source, e),
+ Err(e) => return client.send_notice(&command.source, e),
};
if count < 1 {
- return server.send_notice(&command.source, &format!("{} does not exist", name));
+ return client.send_notice(&command.source, &format!("{} does not exist", name));
}
(name, count - 1)
@@ -125,7 +125,7 @@ impl<T: Database> Factoids<T> {
let name = &command.tokens[0];
let idx = match i32::from_str(&command.tokens[1]) {
Ok(i) => i,
- Err(_) => return server.send_notice(&command.source, "Invalid index"),
+ Err(_) => return client.send_notice(&command.source, "Invalid index"),
};
(name, idx)
@@ -135,36 +135,36 @@ impl<T: Database> Factoids<T> {
let factoid = match try_lock!(self.factoids).get(name, idx) {
Some(v) => v,
None => {
- return server.send_notice(&command.source,
+ return client.send_notice(&command.source,
&format!("{}~{} does not exist", name, idx))
}
};
let message = factoid.content.replace("\n", "|").replace("\r", "");
- server.send_privmsg(&command.target,
+ client.send_privmsg(&command.target,
&format!("{}: {}", factoid.name, message))
}
- fn info(&self, server: &IrcServer, command: &PluginCommand) -> Result<(), IrcError> {
+ fn info(&self, client: &IrcClient, command: &PluginCommand) -> Result<(), IrcError> {
match command.tokens.len() {
- 0 => self.invalid_command(server, command),
+ 0 => self.invalid_command(client, command),
1 => {
let name = &command.tokens[0];
let count = match try_lock!(self.factoids).count(name) {
Ok(c) => c,
- Err(e) => return server.send_notice(&command.source, e),
+ Err(e) => return client.send_notice(&command.source, e),
};
match count {
- 0 => server.send_notice(&command.source, &format!("{} does not exist", name)),
+ 0 => client.send_notice(&command.source, &format!("{} does not exist", name)),
1 => {
- server.send_privmsg(&command.target,
+ client.send_privmsg(&command.target,
&format!("There is 1 version of {}", name))
}
_ => {
- server.send_privmsg(&command.target,
+ client.send_privmsg(&command.target,
&format!("There are {} versions of {}", count, name))
}
}
@@ -173,18 +173,18 @@ impl<T: Database> Factoids<T> {
let name = &command.tokens[0];
let idx = match i32::from_str(&command.tokens[1]) {
Ok(i) => i,
- Err(_) => return server.send_notice(&command.source, "Invalid index"),
+ Err(_) => return client.send_notice(&command.source, "Invalid index"),
};
let factoid = match try_lock!(self.factoids).get(name, idx) {
Some(v) => v,
None => {
- return server.send_notice(&command.source,
+ return client.send_notice(&command.source,
&format!("{}~{} does not exist", name, idx))
}
};
- server.send_privmsg(&command.target,
+ client.send_privmsg(&command.target,
&format!("{}: Added by {} at {} UTC",
name,
factoid.author,
@@ -195,23 +195,23 @@ impl<T: Database> Factoids<T> {
}
fn exec(&self,
- server: &IrcServer,
+ client: &IrcClient,
mut command: PluginCommand,
error: bool)
-> Result<(), IrcError> {
if command.tokens.len() < 1 {
- self.invalid_command(server, &command)
+ self.invalid_command(client, &command)
} else {
let name = command.tokens.remove(0);
let count = match try_lock!(self.factoids).count(&name) {
Ok(c) => c,
- Err(e) => return server.send_notice(&command.source, e),
+ Err(e) => return client.send_notice(&command.source, e),
};
let factoid = match try_lock!(self.factoids).get(&name, count - 1) {
Some(v) => v.content,
- None if error => return self.invalid_command(server, &command),
+ None if error => return self.invalid_command(client, &command),
None => return Ok(()),
};
@@ -230,7 +230,7 @@ impl<T: Database> Factoids<T> {
factoid
};
- server.send_privmsg(&command.target, &value.replace("\n", "|").replace("\r", ""))
+ client.send_privmsg(&command.target, &value.replace("\n", "|").replace("\r", ""))
}
}
@@ -265,20 +265,24 @@ impl<T: Database> Factoids<T> {
Ok(output.join("|"))
}
- fn invalid_command(&self, server: &IrcServer, command: &PluginCommand) -> Result<(), IrcError> {
- server.send_notice(&command.source, "Invalid Command")
+ fn invalid_command(&self, client: &IrcClient, command: &PluginCommand) -> Result<(), IrcError> {
+ client.send_notice(&command.source, "Invalid Command")
}
}
impl<T: Database> Plugin for Factoids<T> {
- fn is_allowed(&self, _: &IrcServer, message: &Message) -> bool {
+ fn execute(&self, _: &IrcClient, message: &Message) -> ExecutionStatus {
match message.command {
- Command::PRIVMSG(_, ref content) => content.starts_with('!'),
- _ => false,
+ Command::PRIVMSG(_, ref content) => if content.starts_with('!') {
+ ExecutionStatus::RequiresThread
+ } else {
+ ExecutionStatus::Done
+ },
+ _ => ExecutionStatus::Done,
}
}
- fn execute(&self, server: &IrcServer, message: &Message) -> Result<(), IrcError> {
+ fn execute_threaded(&self, client: &IrcClient, message: &Message) -> Result<(), IrcError> {
if let Command::PRIVMSG(_, mut content) = message.command.clone() {
content.remove(0);
@@ -290,29 +294,33 @@ impl<T: Database> Plugin for Factoids<T> {
tokens: t,
};
- self.exec(server, c, false)
+ self.exec(client, c, false)
} else {
Ok(())
}
}
- fn command(&self, server: &IrcServer, mut command: PluginCommand) -> Result<(), IrcError> {
+ fn command(&self, client: &IrcClient, mut command: PluginCommand) -> Result<(), IrcError> {
if command.tokens.is_empty() {
- return self.invalid_command(server, &command);
+ return self.invalid_command(client, &command);
}
let sub_command = command.tokens.remove(0);
match sub_command.as_ref() {
- "add" => self.add(server, &mut command),
- "fromurl" => self.from_url(server, &mut command),
- "remove" => self.remove(server, &mut command),
- "get" => self.get(server, &command),
- "info" => self.info(server, &command),
- "exec" => self.exec(server, command, true),
- _ => self.invalid_command(server, &command),
+ "add" => self.add(client, &mut command),
+ "fromurl" => self.from_url(client, &mut command),
+ "remove" => self.remove(client, &mut command),
+ "get" => self.get(client, &command),
+ "info" => self.info(client, &command),
+ "exec" => self.exec(client, command, true),
+ _ => self.invalid_command(client, &command),
}
}
+
+ fn evaluate(&self, _: &IrcClient, _: PluginCommand) -> Result<String, String> {
+ Err(String::from("Evaluation of commands is not implemented for Factoids at this time"))
+ }
}
impl<T: Database> fmt::Debug for Factoids<T> {
diff --git a/src/plugins/help.rs b/src/plugins/help.rs
index 7b987d4..4dd93d7 100644
--- a/src/plugins/help.rs
+++ b/src/plugins/help.rs
@@ -1,5 +1,5 @@
use irc::client::prelude::*;
-use irc::error::Error as IrcError;
+use irc::error::IrcError;
use plugin::*;
@@ -10,23 +10,23 @@ impl Help {
pub fn new() -> Help {
Help {}
}
-
- fn help(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
- server.send_notice(&command.source, "Help has not been added yet.")
- }
}
impl Plugin for Help {
- fn is_allowed(&self, _: &IrcServer, _: &Message) -> bool {
- false
+ fn execute(&self, _: &IrcClient, _: &Message) -> ExecutionStatus {
+ ExecutionStatus::Done
+ }
+
+ fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), IrcError> {
+ panic!("Help should not use threading")
}
- fn execute(&self, _: &IrcServer, _: &Message) -> Result<(), IrcError> {
- panic!("Help does not implement the execute function!")
+ fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), IrcError> {
+ client.send_notice(&command.source, "Help has not been added yet.")
}
- fn command(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
- self.help(server, command)
+ fn evaluate(&self, _: &IrcClient, _: PluginCommand) -> Result<String, String> {
+ Err(String::from("Help has not been added yet."))
}
}
diff --git a/src/plugins/keepnick.rs b/src/plugins/keepnick.rs
index 1d4627d..73f4893 100644
--- a/src/plugins/keepnick.rs
+++ b/src/plugins/keepnick.rs
@@ -1,5 +1,5 @@
use irc::client::prelude::*;
-use irc::error::Error as IrcError;
+use irc::error::IrcError;
use plugin::*;
@@ -11,48 +11,53 @@ impl KeepNick {
KeepNick {}
}
- fn check_nick(&self, server: &IrcServer, leaver: &str) -> Result<(), IrcError> {
- let cfg_nick = match server.config().nickname {
+ fn check_nick(&self, client: &IrcClient, leaver: &str) -> ExecutionStatus {
+ let cfg_nick = match client.config().nickname {
Some(ref nick) => nick.clone(),
- None => return Ok(()),
+ None => return ExecutionStatus::Done,
};
if leaver != cfg_nick {
- return Ok(());
+ return ExecutionStatus::Done;
}
- let server_nick = server.current_nickname();
-
- if server_nick != cfg_nick {
- info!("Trying to switch nick from {} to {}", server_nick, cfg_nick);
- server.send(Command::NICK(cfg_nick))
+ let client_nick = client.current_nickname();
+ if client_nick != cfg_nick {
+ info!("Trying to switch nick from {} to {}", client_nick, cfg_nick);
+ match client.send(Command::NICK(cfg_nick)) {
+ Ok(_) => ExecutionStatus::Done,
+ Err(e) => ExecutionStatus::Err(e),
+ }
} else {
- Ok(())
+ ExecutionStatus::Done
}
}
}
impl Plugin for KeepNick {
- fn is_allowed(&self, _: &IrcServer, message: &Message) -> bool {
- match message.command {
- Command::QUIT(_) => true,
- _ => false,
- }
- }
-
- fn execute(&self, server: &IrcServer, message: &Message) -> Result<(), IrcError> {
+ fn execute(&self, client: &IrcClient, message: &Message) -> ExecutionStatus {
match message.command {
Command::QUIT(ref nick) => {
- self.check_nick(server, &nick.clone().unwrap_or_else(|| String::new()))
+ self.check_nick(client, &nick.clone().unwrap_or_else(String::new))
}
- _ => Ok(()),
+ _ => ExecutionStatus::Done,
}
}
- fn command(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
- server.send_notice(&command.source,
- "This Plugin does not implement any commands.")
+ fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), IrcError> {
+ panic!("Tell should not use threading")
+ }
+
+ fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), IrcError> {
+ client.send_notice(
+ &command.source,
+ "This Plugin does not implement any commands.",
+ )
+ }
+
+ fn evaluate(&self, _: &IrcClient, _: PluginCommand) -> Result<String, String> {
+ Err(String::from("This Plugin does not implement any commands."))
}
}
diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs
index 2e85932..5b32efd 100644
--- a/src/plugins/mod.rs
+++ b/src/plugins/mod.rs
@@ -2,6 +2,7 @@
mod help;
mod url;
mod emoji;
+mod tell;
mod currency;
mod factoids;
mod keepnick;
@@ -9,6 +10,7 @@ mod keepnick;
pub use self::help::Help;
pub use self::url::Url;
pub use self::emoji::Emoji;
+pub use self::tell::Tell;
pub use self::currency::Currency;
pub use self::factoids::Factoids;
pub use self::factoids::database;
diff --git a/src/plugins/tell.rs b/src/plugins/tell.rs
new file mode 100644
index 0000000..34d7cf8
--- /dev/null
+++ b/src/plugins/tell.rs
@@ -0,0 +1,140 @@
+use irc::client::prelude::*;
+use irc::error::IrcError;
+
+use std::collections::HashMap;
+use std::sync::Mutex;
+
+use plugin::*;
+
+macro_rules! try_lock {
+ ( $m:expr ) => {
+ match $m.lock() {
+ Ok(guard) => guard,
+ Err(poisoned) => poisoned.into_inner(),
+ }
+ }
+}
+
+#[derive(PluginName, Default, Debug)]
+pub struct Tell {
+ tells: Mutex<HashMap<String, Vec<TellMessage>>>,
+}
+
+#[derive(Default, Debug)]
+struct TellMessage {
+ sender: String,
+ // TODO Add time
+ message: String,
+}
+
+impl Tell {
+ pub fn new() -> Tell {
+ Tell {
+ tells: Mutex::new(HashMap::new()),
+ }
+ }
+
+ fn tell_command(&self, client: &IrcClient, command: &PluginCommand) -> Result<&str, String> {
+ if command.tokens.len() < 2 {
+ return Err(self.invalid_command(client));
+ }
+
+ let receiver = command.tokens[0].to_string();
+ let sender = command.source.to_owned();
+
+ if receiver == sender {
+ return Err(String::from("That's your name!"));
+ }
+
+ if command.source != command.target {
+ if let Some(users) = client.list_users(&command.target) {
+ if users.iter().any(|u| u.get_nickname() == receiver) {
+ return Err(format!("{} is in this channel.", receiver));
+ }
+ }
+ }
+
+ let message = command.tokens[1..].join(" ");
+ let tell = TellMessage {
+ sender: sender,
+ message: message,
+ };
+
+ let mut tells = try_lock!(self.tells);
+ let tell_messages = tells.entry(receiver).or_insert(Vec::with_capacity(3));
+ (*tell_messages).push(tell);
+
+ Ok("Got it!")
+ }
+
+ fn send_tell(&self, client: &IrcClient, receiver: &str) -> ExecutionStatus {
+ let mut tells = try_lock!(self.tells);
+ if let Some(tell_messages) = tells.get_mut(receiver) {
+ for tell in tell_messages {
+ if let Err(e) = client.send_notice(
+ receiver,
+ &format!("Tell from {}: {}", tell.sender, tell.message),
+ ) {
+ return ExecutionStatus::Err(e);
+ }
+ debug!(
+ "Sent {:?} from {:?} to {:?}",
+ tell.message, tell.sender, receiver
+ );
+ }
+ }
+ tells.remove(receiver);
+ ExecutionStatus::Done
+ }
+
+ fn invalid_command(&self, client: &IrcClient) -> String {
+ format!(
+ "Incorrect Command. \
+ Send \"{} tell help\" for help.",
+ client.current_nickname()
+ )
+ }
+
+ fn help(&self, client: &IrcClient) -> String {
+ format!(
+ "usage: {} tell user message\r\n\
+ example: {0} tell Foobar Hello!",
+ client.current_nickname()
+ )
+ }
+}
+
+impl Plugin for Tell {
+ fn execute(&self, client: &IrcClient, message: &Message) -> ExecutionStatus {
+ match message.command {
+ Command::JOIN(_, _, _) => self.send_tell(client, message.source_nickname().unwrap()),
+ Command::PRIVMSG(_, _) => self.send_tell(client, message.source_nickname().unwrap()),
+ _ => ExecutionStatus::Done,
+ }
+ }
+
+ fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), IrcError> {
+ panic!("Tell should not use threading")
+ }
+
+ fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), IrcError> {
+ if command.tokens.is_empty() {
+ return client.send_notice(&command.source, &self.invalid_command(client));
+ }
+
+ match command.tokens[0].as_ref() {
+ "help" => client.send_notice(&command.source, &self.help(client)),
+ _ => match self.tell_command(client, &command) {
+ Ok(msg) => client.send_notice(&command.source, msg),
+ Err(msg) => client.send_notice(&command.source, &msg),
+ },
+ }
+ }
+
+ fn evaluate(&self, _: &IrcClient, _: PluginCommand) -> Result<String, String> {
+ Err(String::from("This Plugin does not implement any commands."))
+ }
+}
+
+#[cfg(test)]
+mod tests {}
diff --git a/src/plugins/url.rs b/src/plugins/url.rs
index 6f4a68f..df4fdf2 100644
--- a/src/plugins/url.rs
+++ b/src/plugins/url.rs
@@ -2,7 +2,7 @@ extern crate regex;
extern crate select;
use irc::client::prelude::*;
-use irc::error::Error as IrcError;
+use irc::error::IrcError;
use self::regex::Regex;
@@ -24,7 +24,7 @@ pub struct Url {
impl Url {
/// If a file is larger than `max_kib` KiB the download is stopped
pub fn new(max_kib: usize) -> Url {
- Url {max_kib: max_kib}
+ Url { max_kib: max_kib }
}
fn grep_url(&self, msg: &str) -> Option<String> {
@@ -38,56 +38,63 @@ impl Url {
}
}
- fn url(&self, server: &IrcServer, message: &str, target: &str) -> Result<(), IrcError> {
- let url = match self.grep_url(message) {
+ fn url(&self, text: &str) -> Result<String, &str> {
+ let url = match self.grep_url(text) {
Some(url) => url,
- None => {
- return Ok(());
- }
+ None => return Err("No Url was found."),
};
match utils::download(self.max_kib, &url) {
Some(body) => {
-
let doc = Document::from(body.as_ref());
if let Some(title) = doc.find(Name("title")).next() {
- let text = title.children().next().unwrap();
- let message = text.as_text().unwrap().trim().replace("\n", "|");
- debug!("Title: {:?}", text);
- debug!("Message: {:?}", message);
-
- server.send_privmsg(target, &message)
+ let title = title.children().next().unwrap();
+ let title_text = title.as_text().unwrap().trim().replace("\n", "|");
+ debug!("Title: {:?}", title);
+ debug!("Text: {:?}", title_text);
+ Ok(title_text)
} else {
- Ok(())
+ Err("No title was found.")
}
}
- None => Ok(()),
+ None => Err("Failed to download document."),
}
}
}
impl Plugin for Url {
- fn is_allowed(&self, _: &IrcServer, message: &Message) -> bool {
+ fn execute(&self, _: &IrcClient, message: &Message) -> ExecutionStatus {
match message.command {
- Command::PRIVMSG(_, ref msg) => RE.is_match(msg),
- _ => false,
+ Command::PRIVMSG(_, ref msg) => if RE.is_match(msg) {
+ ExecutionStatus::RequiresThread
+ } else {
+ ExecutionStatus::Done
+ },
+ _ => ExecutionStatus::Done,
}
}
- fn execute(&self, server: &IrcServer, message: &Message) -> Result<(), IrcError> {
+ fn execute_threaded(&self, client: &IrcClient, message: &Message) -> Result<(), IrcError> {
match message.command {
- Command::PRIVMSG(_, ref content) => {
- self.url(server, content, message.response_target().unwrap())
- }
+ Command::PRIVMSG(_, ref content) => match self.url(content) {
+ Ok(title) => client.send_privmsg(message.response_target().unwrap(), &title),
+ Err(_) => Ok(()),
+ },
_ => Ok(()),
}
}
- fn command(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
- server.send_notice(&command.source,
- "This Plugin does not implement any commands.")
+ fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), IrcError> {
+ client.send_notice(
+ &command.source,
+ "This Plugin does not implement any commands.",
+ )
+ }
+
+ fn evaluate(&self, _: &IrcClient, command: PluginCommand) -> Result<String, String> {
+ self.url(&command.tokens[0]).map_err(String::from)
}
}