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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//!
//! An extensible platform for writing chatbots.
//!
//! Chatbot is extensible in both adapters (services it connects to like IRC or Slack) and handlers
//! (the bits of code processing messages). Several adapters are provided out of the box including a
//! [Command-line Adapter][], an [IRC Adapter][], and a [Slack Adapter][]. There's also several
//! handlers built in which can be viewed in the [implementors][Messagehandler implementors] section
//! of the handler module.
//!
//! `cargo run` will get you a working command line bot immediately after cloning. The `main.rs`
//! file is the default binary and is a good starting place to hack on your own bot.
//!
//! [Command-line Adapter]: adapter/struct.CliAdapter.html
//! [IRC Adapter]: adapter/struct.CliAdapter.html
//! [Slack Adapter]: adapter/struct.SlackAdapter.html
//! [MessageHandler implementors]: handler/trait.MessageHandler.html#implementors
//!
//! ## Adapters
//!
//! An adapter is a wrapper around some service like Slack, IRC, or just the command line. When the
//! bot starts up, it passes a Sender<IncomingMessage> into the handler `process_events` method.
//! The main loop owns the receiver and thus gets messages from all of the adapters. When the
//! adapter gets a message from the underlying service, it must create an IncomingMessage and `send`
//! it using that Sender. The IncomingMessage must be populated with a `Sender<OutgoingMessage>`
//! which the adapter calls `recv` on. It is up to the adapter what to do with these
//! OutgoingMessages. Generally they should be directed to whence they came.
//!
//! ## Handlers
//!
//! Handlers provide a `Regex` which the main loop uses to check whether the handler is interested.
//! If the regex matches, `handle` is called on the handler with the IncomingMessage. The handler
//! can then do some work and call `reply` on the incoming message to send its response. The adapter
//! which created the incoming message will decide how to route the message back to the service.
//!
//! Handlers are not sandboxed and can thus bring the bot down in flames if they decide to panic. It
//! may be worth sandboxing in threads in the future (maybe make handlers `Runnable` and send to a
//! worker pool). The built in handlers are written with care as to not panic the bot.
//!
//! For very simple handlers, there is a `handler!` macro which lets you pass a regex and a closure
//! without having to implement the MessageHandler trait. The example below contains an example of
//! this.
//!
//! ## Chatbot
//!
//! The Chatbot is the central data structure of the chatbot platform. It contains a `run` method
//! which listens for messages from adapters and routes them to handlers. Any program which uses
//! chatbot will need to minimally create a Chatbot, add an adapter, add a handler, and call Chatbot
//! [`run`](chatbot/struct.Chatbot.html#method.run).
//!
//! For example, setting up a simple echo server that responds to CLI input:
//!
//! # Examples
//!
//! ```no_run
//! # #[macro_use(handler)]
//! # extern crate chatbot;
//! # fn main() {
//! use chatbot::Chatbot;
//! use chatbot::adapter::CliAdapter;
//!
//! let mut bot = Chatbot::new("echobot");
//!
//! let echo = handler!("EchoHandler", r"echo .+", |_, msg| {
//! Some(msg.to_owned())
//! });
//!
//! bot.add_handler(echo);
//! bot.add_adapter(CliAdapter::new());
//!
//! bot.run();
//! # }
//! ```
//!
//! Sometimes you might want the bot to react only when it is addressed,
//! this is what
//! [`add_addressed_handler`](chatbot/struct.Chatbot.html#method.add_addressed_handler) is for.
//!
//! An example would be a bot that responses to pings, only you don't want the bot to respond
//! everytime there is any form of "ping" in a sentence.
//!
//! ```no_run
//! # #[macro_use(handler)]
//! # extern crate chatbot;
//! # fn main() {
//! use chatbot::Chatbot;
//! use chatbot::adapter::CliAdapter;
//!
//! let mut bot = Chatbot::new("pingbot");
//!
//! let ping = handler!("PingHandler", r"ping", |_, _| Some("pong".to_owned()));
//!
//! bot.add_addressed_handler(ping);
//! bot.add_adapter(CliAdapter::new());
//!
//! bot.run();
//! # }
//! ```
//!
extern crate abort_on_panic;
extern crate regex;
extern crate hyper;
extern crate rustc_serialize;
extern crate slack;
extern crate irc;
extern crate startuppong;
/// Shorthand for creating a `Regex` as suggested by the regex crate. You probably don't need to
/// `macro_use` this unless you're creating handlers in an external module.
;
/// The `handler!` macro is shorthand for creating simple chat handlers. It
/// accepts a name, a string used to build a regex for testing the incoming
/// message and for collecting captures, and a closure which should return a
/// `String` to be sent as the outgoing message.
///
/// # Examples
///
/// ```
/// # #[macro_use(handler)]
/// # extern crate chatbot;
/// # fn main() {
/// let ping = handler!("Ping", r"ping", |_, _| Some("pong".to_owned()) );
/// # }
/// ```
///
pub use Chatbot;