1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
//! Definitions required for every `Plugin`
use std::fmt;
use irc::client::prelude::*;
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.
pub trait Plugin: PluginName + Send + Sync + fmt::Debug {
/// 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: &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`](trait.Plugin.html).
///
/// To implement it simply add `#[derive(PluginName)]`
/// above the definition of the struct.
///
/// # Examples
/// ```ignore
/// #[macro_use] extern crate frippy_derive;
///
/// #[derive(PluginName)]
/// struct Foo;
/// ```
pub trait PluginName: Send + Sync + fmt::Debug {
/// Returns the name of the `Plugin`.
fn name(&self) -> &str;
}
/// Represents a command sent by a user to the bot.
#[derive(Clone, Debug)]
pub struct PluginCommand {
/// The sender of the command.
pub source: String,
/// If the command was sent to a channel, this will be that channel
/// otherwise it is the same as `source`.
pub target: String,
/// The remaining part of the message that has not been processed yet - split by spaces.
pub tokens: Vec<String>,
}
impl PluginCommand {
/// 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();
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
}
}
}
|