extern crate toml; extern crate crossbeam; extern crate discord; extern crate rand; extern crate pvn; extern crate echobox; extern crate transformable_channels; extern crate stc; extern crate regex; #[macro_use] extern crate hlua; use std::collections::BTreeMap; use toml::Table; mod modules; use modules::Module; use modules::loader::{ModuleLoader, ModuleLoaderError}; mod event; use event::Envelope; use std::sync::Arc; use std::sync::mpsc; use std::sync::mpsc::Sender; use std::rc::Rc; use std::cell::{Cell, RefCell}; use transformable_channels::mpsc::TransformableSender; mod helpers; use std::collections::btree_set::BTreeSet; #[macro_use] extern crate log; pub struct Tenquestionmarks { subscriptions: BTreeMap } impl Tenquestionmarks { pub fn with_modules (modules: BTreeMap) -> Tenquestionmarks { let tqm = Tenquestionmarks { subscriptions: modules.into_iter().map(|(key, module)| { (key.clone(), Subscription::new(key.clone(), module)) }).collect() }; tqm } pub fn from_configuration (configuration: Table) -> Result { let loader = ModuleLoader::new(); let modules = loader.load_from_configuration(configuration)?; Result::Ok(Tenquestionmarks::with_modules(modules)) } pub fn run (&self) { crossbeam::scope(|scope| { // Our main event channel. // Modules push events to tenquestionmarks using this channel. let (ref main_sender, ref main_receiver) = transformable_channels::mpsc::channel(); // Module threads. // Each Module will produce events which tenquestionmarks will push // into all other Modules. // tenquestionmarks propagates all events to each Module through these // channels. let module_senders: BTreeMap<&str, Sender>> = self.subscriptions.iter().map(|(key, subscription)| { let from = key.clone(); let main_sender_mapped = main_sender.map(move |envelope: Envelope| { Envelope { from: Some(from.clone()), event: envelope.event, to: envelope.to, tags: envelope.tags } }); let (module_sender, module_receiver) = mpsc::channel(); info!("Spawning thread for \"{}\"", key); scope.spawn(move || { subscription.module.run(Box::new(main_sender_mapped), module_receiver); info!("Thread for \"{}\" is exiting", key); }); (&key[..], module_sender) }).collect(); // tenquestionmarks main event loop. // tenquestionmarks receives events produced by Modules and pushes them // into all other Modules loop { match main_receiver.recv() { Ok(envelope) => { /* * Check if the target module is a valid destination for the envelope. * We want to deliver this envelope if all of the following are true: * 1. The target module is not the originator of the envelope * 2. The envelope's list of allowed recipients is empty, or specifically * names the target module. */ let arc_envelope = Arc::new(envelope); for (key, sender) in &module_senders { let from_this = String::from(*key); match self.subscriptions.get::(&from_this) { Some(subscription) => { if subscription.can_handle_event(&arc_envelope) { match sender.send(arc_envelope.clone()) { Err(err) => debug!("Failed to dispatch event to module \"{}\": {:?}", key, err), Ok(_) => {} } } }, None => debug!("Failed to dispatch event to module \"{}\": No such module found!", key) } } }, Err(err) => { error!("Failed to receive event in main event loop: {:?}", err); } } } }); } } #[derive(Debug)] pub struct Message { content: String, author: User, channel: Option } impl Message { fn reply (&self, message: &str) { match self.channel { Some(ref channel) => channel.send(message), None => self.author.send(message) } } } #[derive(Debug)] pub struct Channel { name: String, description: String, topic: String, sender: Box } impl Channel { pub fn send (&self, message: &str) { self.sender.send_message(message); } } #[derive(Debug)] pub struct User { name: String, sender: Box } impl User { pub fn send (&self, message: &str) { self.sender.send_message(message); } } pub trait MessageSender : Sync + Send + std::fmt::Debug { fn send_message (&self, _: &str) {} } struct Subscription { pub module: Module, pub name: String, pub filters: BTreeSet } impl Subscription { pub fn new (name: String, module: Module) -> Subscription { let filters: BTreeSet = module.config.get("filters") .and_then(|value| value.as_slice()) .map(|value| value.to_vec()) .unwrap_or(vec![]) .into_iter() .map(|value| { String::from(value.as_str().unwrap()) }) .collect(); Subscription { module: module, name: name, filters: filters } } pub fn can_handle_event (&self, envelope: &Envelope) -> bool { if Some(&self.name) == envelope.from.as_ref() { debug!("Refusing to transmit event to its originator ({:?})", self.name); return false; } else if !(envelope.to.is_empty() || !envelope.to.contains(&self.name)) { debug!( "Refusing to transmit envelope from {:?} to {:?} since it is not on the list of allowed recipients ({:?})", envelope.from, self.name, envelope.to ); return false; } else if !(self.filters.is_empty() || self.filters.intersection(&envelope.tags).count() > 0) { debug!( "Refusing to transmit envelope from {:?} to {:?} since envelope was filtered out", envelope.from, self.name ); return false; } true } }