use regex::Regex;
use std::sync::mpsc::channel;
use adapter::ChatAdapter;
use handler::MessageHandler;
use message::IncomingMessage;
fn dispatch(handlers: &Vec<Box<MessageHandler>>, msg: &IncomingMessage) {
let msg_str = msg.get_contents();
for handler in handlers {
if handler.can_handle(msg_str) {
match handler.handle(msg) {
Err(e) => {
println!("Error in handler `{}`", handler.name());
println!("{:?}", e);
println!("The incoming message was {}", msg_str);
},
_ => ()
}
}
}
}
pub struct Chatbot {
name: String,
adapters: Vec<Box<ChatAdapter>>,
handlers: Vec<Box<MessageHandler>>,
addressed_handlers: Vec<Box<MessageHandler>>,
addresser: Regex
}
impl Chatbot {
pub fn new(name: &str) -> Chatbot {
let addresser_str = format!(r"^\s*@?{}[:,\s]\s*", name);
let addresser = Regex::new(addresser_str.as_ref());
Chatbot {
name: name.to_owned(),
adapters: Vec::new(),
handlers: Vec::new(),
addressed_handlers: Vec::new(),
addresser: addresser.unwrap()
}
}
pub fn get_name(&self) -> &str {
self.name.as_ref()
}
pub fn get_addresser(&self) -> &Regex {
&self.addresser
}
pub fn add_adapter<T>(&mut self, adapter: T)
where T: ChatAdapter + 'static
{
println!("Adding adapter {}", adapter.get_name());
self.adapters.push(Box::new(adapter))
}
pub fn add_handler<T>(&mut self, handler: T)
where T: MessageHandler + 'static
{
println!("Adding handler {}", handler.name());
self.handlers.push(Box::new(handler))
}
pub fn add_addressed_handler<T>(&mut self, handler: T)
where T: MessageHandler + 'static
{
println!("Adding handler {}", handler.name());
self.addressed_handlers.push(Box::new(handler))
}
pub fn run(&self) {
let adapters_len = self.adapters.len();
let handlers_len = self.handlers.len() + self.addressed_handlers.len();
assert!(adapters_len > 0);
assert!(handlers_len > 0);
println!("Chatbot: {} adapters", adapters_len);
println!("Chatbot: {} handlers", handlers_len);
let (incoming_tx, incoming_rx) = channel();
for adapter in &self.adapters {
adapter.process_events(self, incoming_tx.clone());
}
loop {
let msg = match incoming_rx.recv() {
Ok(msg) => msg,
Err(_) => break
};
if self.addresser.is_match(msg.get_contents()) {
dispatch(&self.addressed_handlers, &msg);
}
dispatch(&self.handlers, &msg);
}
println!("chatbot shutting down");
}
}
#[cfg(test)]
mod tests {
use chatbot::Chatbot;
use adapter::CliAdapter;
#[test]
fn test_create_chatbot() {
let name = "testbot";
let bot = Chatbot::new("testbot");
assert_eq!(bot.get_name(), name);
}
#[test]
fn test_chatbot_add_adapter() {
let mut bot = Chatbot::new("testbot");
let cli = CliAdapter::new();
bot.add_adapter(cli);
}
#[test]
fn test_chatbot_add_handler() {
let mut bot = Chatbot::new("testbot");
let echo = handler!("EchoHandler", r"echo .+", |_, msg| {
Some(msg.to_owned())
});
bot.add_handler(echo);
}
#[test]
fn test_chatbot_address_matches() {
let bot = Chatbot::new("testbot");
let addresser = bot.get_addresser();
assert!(!addresser.is_match("testbotping"),
"Shouldn't match bot name without a break (space, colon, comma)");
assert!(!addresser.is_match("@testbotping"),
"Shouldn't match bot name with '@' sign minus a break (space, colon, comma)");
assert!(addresser.is_match("testbot ping"), "Should match bot name with space");
assert!(addresser.is_match("@testbot ping"),
"Should match bot name with '@' sign plus '@' space");
assert!(addresser.is_match("testbot:ping"), "Should match bot name with colon");
assert!(addresser.is_match("testbot: ping"), "Should match bot name with colon and space");
assert!(addresser.is_match("@testbot:ping"),
"Should match bot name with '@' sign plus colon");
assert!(addresser.is_match("@testbot: ping"),
"Should match bot name with '@' sign plus colon and space");
assert!(addresser.is_match("testbot,ping"), "Should match bot name with comma");
assert!(addresser.is_match("testbot, ping"), "Should match bot name with comma and space");
assert!(addresser.is_match("@testbot,ping"),
"Should match bot name with '@' sign plus comma");
assert!(addresser.is_match("@testbot, ping"),
"Should match bot name with '@' sign plus comma and space");
}
}