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
#![feature(await_macro, async_await, futures_api)] use std::rc::Rc; use crate::config::Config; use crate::handler::{Command, CommandHandler, CommandRouter}; use crate::request::Request; use crate::response::{Outcome, Response}; use futures::future::Future; use irc::client::ext::ClientExt; use irc::client::reactor::IrcReactor; use irc::client::IrcClient; use irc::proto::Message; use state::Container; use tokio_async_await::compat::backward; pub use failure::Error; #[doc(hidden)] pub use inventory; pub use nestor_codegen::command; pub mod config; pub mod handler; pub mod request; pub mod response; inventory::collect!(Box<dyn CommandHandler>); pub struct Nestor { state: Container, config: Config, router: CommandRouter, } impl Nestor { pub fn build() -> Self { let config = Config::load("nestor.toml").unwrap(); Nestor { state: Container::new(), config, router: CommandRouter::new(), } } pub fn with_config(config: Config) -> Self { Nestor { state: Container::new(), config, router: CommandRouter::new(), } } pub fn manage<T: Send + Sync + 'static>(self, state: T) -> Self { self.state.set(state); self } pub fn activate(mut self) { let routes = inventory::iter::<Box<dyn CommandHandler>> .into_iter() .map(|route| (route.route_id(), route.as_ref())) .collect(); self.router.add_handlers(routes); let nestor = Rc::new(self); let mut reactor = IrcReactor::new().unwrap(); let handle = reactor.inner_handle(); let client = reactor .prepare_client_and_connect(&nestor.config.irc_config) .unwrap(); client.identify().unwrap(); reactor.register_client_with_handler(client, move |client, message| { let future = handle_message(nestor.clone(), client.clone(), message); handle.spawn(backward::Compat::new(future).map_err(|_| ())); Ok(()) }); reactor.run().unwrap(); } } async fn handle_message( nestor: Rc<Nestor>, client: IrcClient, message: Message, ) -> Result<(), Error> { if let Some((responder, mut request)) = Request::from_message(&nestor, &client, &message) { for _ in 0..nestor.config.bot_settings.alias_depth { let response = await!(nestor.router.route(&request)); let response = match response { Outcome::Forward(c) => { request = Request { config: &nestor.config, state: &nestor.state, command: Command::from_command_str(request.command.source_nick, &c) .ok_or(failure::err_msg("Internal error with command alias"))?, }; continue; } Outcome::Success(response) => response, Outcome::Failure(err) => { println!("{:?}", err); Response::Say("Unexpected error executing command".into()) } }; match response { Response::Say(message) => client.send_privmsg(responder, &message)?, Response::Act(message) => client.send_action(responder, &message)?, Response::Notice(message) => client.send_notice(responder, &message)?, Response::None => {} } return Ok(()); } client.send_notice(responder, "alias depth too deep")?; } Ok(()) }