2017-05-16 21:35:05 -05:00

337 lines
12 KiB
Rust

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::Event;
use event::Envelope;
use std::sync::Arc;
use std::sync::mpsc;
use std::sync::mpsc::Sender;
use transformable_channels::mpsc::TransformableSender;
mod helpers;
#[macro_use]
extern crate log;
pub struct Tenquestionmarks {
subscriptions: BTreeMap<String, Subscription>
}
impl Tenquestionmarks {
pub fn with_modules (modules: BTreeMap<String, Module>) -> 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<Tenquestionmarks, ModuleLoaderError> {
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<Arc<Envelope>>> = 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
}
});
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);
if let Some(subscription) = self.subscriptions.get::<String>(&from_this) {
if subscription.can_handle_event(&arc_envelope) {
if let Err(err) = sender.send(arc_envelope.clone()) {
debug!("Failed to dispatch event to module \"{}\": {:?}", key, err);
}
}
} else {
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<Channel>
}
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<MessageSender>
}
impl Channel {
pub fn send (&self, message: &str) {
self.sender.send_message(message);
}
}
#[derive(Debug)]
pub struct User {
name: String,
sender: Box<MessageSender>
}
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) {}
}
trait EventFilter: Sync + Send {
fn accept (&self, envelope: &Envelope) -> bool;
}
struct Subscription {
pub module: Module,
pub name: String,
pub filters: Vec<Box<EventFilter>>
}
impl Subscription {
pub fn new (name: String, module: Module) -> Subscription {
let filters: Vec<Box<EventFilter>> = module.config.get("filters")
.and_then(|value| value.as_slice())
.map(|value| value.to_vec())
.unwrap_or(vec![])
.into_iter()
.map(|value| {
match value.as_table() {
Some(table) => Some(Box::new(AttributeEventFilter::new(table)) as Box<EventFilter>),
None => None
}
})
.filter(|possible_filter| possible_filter.is_some())
.map(|possible_filter| possible_filter.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() {
for filter in &self.filters {
if filter.accept(envelope) {
return true;
}
}
debug!(
"Refusing to transmit envelope from {:?} to {:?} since envelope was filtered out",
envelope.from,
self.name
);
return false;
}
true
}
}
struct AttributeEventFilter {
// Attributes that can be filtered out
event_type: Option<String>,
username: Option<String>,
channel: Option<String>,
message: Option<String>
}
impl AttributeEventFilter {
pub fn new (attributes: &Table) -> AttributeEventFilter {
AttributeEventFilter {
event_type: attributes.get("type").and_then(|value| value.as_str()).map(|value| String::from(value)),
message: attributes.get("message").and_then(|value| value.as_str()).map(|value| String::from(value)),
username: attributes.get("username").and_then(|value| value.as_str()).map(|value| String::from(value)),
channel: attributes.get("channel").and_then(|value| value.as_str()).map(|value| String::from(value)),
}
}
}
impl EventFilter for AttributeEventFilter {
fn accept (&self, envelope: &Envelope) -> bool {
let mut result = true;
match &envelope.event {
&Event::Message { ref message } => {
match self.event_type {
Some(ref event_type) => result = result && event_type == "message",
None => {}
}
match self.channel {
Some(ref channel_name) => {
match message.channel {
Some(ref channel) => result = result && channel_name == &channel.name,
None => result = false
}
},
None => {}
}
match self.username {
Some(ref username) => result = result && &message.author.name == username,
None => {}
}
},
&Event::SelfJoin { ref channel } => {
match self.event_type {
Some(ref event_type) => result = result && event_type == "selfjoin",
None => {}
}
match self.channel {
Some(ref channel_name) => result = result && channel_name == &channel.name,
None => {}
}
},
&Event::SelfQuit { ref channel } => {
match self.event_type {
Some(ref event_type) => result = result && event_type == "selfquit",
None => {}
}
match self.channel {
Some(ref channel_name) => result = result && channel_name == &channel.name,
None => {}
}
},
&Event::UserJoin { ref channel, ref user } => {
match self.event_type {
Some(ref event_type) => result = result && event_type == "userjoin",
None => {}
}
match self.channel {
Some(ref channel_name) => result = result && channel_name == &channel.name,
None => {}
}
match self.username {
Some(ref username) => result = result && &user.name == username,
None => {}
}
},
&Event::UserQuit { ref channel, ref user } => {
match self.event_type {
Some(ref event_type) => result = result && event_type == "userquit",
None => {}
}
match self.channel {
Some(ref channel_name) => result = result && channel_name == &channel.name,
None => {}
}
match self.username {
Some(ref username) => result = result && &user.name == username,
None => {}
}
}
}
result
}
}