Simplify module trait by combining produce/consume event methods into a single run method that runs in the module's own thread and can produce and/or consume events. Introduce an Envelope struct that encapsulates event + to/from so we can (eventually) tag every event and also limit where events are sent (e.g. you can have a specific module configured to talk or listen only to a certain other module).
This commit is contained in:
parent
442b617f31
commit
37a9645f5b
@ -1,5 +1,7 @@
|
|||||||
use {Message, Channel, User};
|
use {Message, Channel, User};
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Message { message: Message }, // A user sends a message
|
Message { message: Message }, // A user sends a message
|
||||||
|
|
||||||
@ -9,3 +11,19 @@ pub enum Event {
|
|||||||
UserJoin { channel: Channel, user: User }, // A user joins a channel
|
UserJoin { channel: Channel, user: User }, // A user joins a channel
|
||||||
UserQuit { channel: Channel, user: User } // A user quits a channel
|
UserQuit { channel: Channel, user: User } // A user quits a channel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Envelope {
|
||||||
|
pub from: Option<String>,
|
||||||
|
pub event: Arc<Event>,
|
||||||
|
pub to: Vec<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Envelope {
|
||||||
|
pub fn new (event: Event) -> Envelope {
|
||||||
|
Envelope {
|
||||||
|
from: None,
|
||||||
|
event: Arc::new(event),
|
||||||
|
to: vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
36
src/lib.rs
36
src/lib.rs
@ -13,7 +13,7 @@ use modules::Module;
|
|||||||
use modules::loader::{ModuleLoader, ModuleLoaderError};
|
use modules::loader::{ModuleLoader, ModuleLoaderError};
|
||||||
|
|
||||||
mod event;
|
mod event;
|
||||||
use event::Event;
|
use event::Envelope;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
@ -34,7 +34,7 @@ impl Tenquestionmarks {
|
|||||||
modules: modules
|
modules: modules
|
||||||
};
|
};
|
||||||
|
|
||||||
for (key, module) in &tqm.modules {
|
for (_, module) in &tqm.modules {
|
||||||
module.register(&tqm);
|
module.register(&tqm);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,39 +53,31 @@ impl Tenquestionmarks {
|
|||||||
// Modules push events to tenquestionmarks using this channel.
|
// Modules push events to tenquestionmarks using this channel.
|
||||||
let (ref sender, ref receiver) = mpsc::channel();
|
let (ref sender, ref receiver) = mpsc::channel();
|
||||||
|
|
||||||
// Module event consumer threads.
|
// Module threads.
|
||||||
|
// Each Module will produce events which tenquestionmarks will push
|
||||||
|
// into all other Modules.
|
||||||
// tenquestionmarks propagates all events to each Module through these
|
// tenquestionmarks propagates all events to each Module through these
|
||||||
// channels.
|
// channels.
|
||||||
let senders: BTreeMap<&str, Sender<Arc<Event>>> = self.modules.iter().map(|(key, module)| {
|
let senders: BTreeMap<&str, Sender<Arc<Envelope>>> = self.modules.iter().map(|(key, module)| {
|
||||||
|
let module_sender = sender.clone();
|
||||||
let (sender, receiver) = mpsc::channel();
|
let (sender, receiver) = mpsc::channel();
|
||||||
info!("Spawning consumer thread for \"{}\"", key);
|
info!("Spawning thread for \"{}\"", key);
|
||||||
scope.spawn(move || {
|
scope.spawn(move || {
|
||||||
module.consume_events(receiver);
|
module.run(module_sender, receiver);
|
||||||
info!("Consumer thread for \"{}\" is exiting", key);
|
info!("Thread for \"{}\" is exiting", key);
|
||||||
});
|
});
|
||||||
(&key[..], sender)
|
(&key[..], sender)
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
// Module event producer threads.
|
|
||||||
// Each Module will produce events which tenquestionmarks will push
|
|
||||||
// into all other Modules.
|
|
||||||
for (key, module) in self.modules.iter() {
|
|
||||||
let module_sender = sender.clone();
|
|
||||||
info!("Spawning producer thread for \"{}\"", key);
|
|
||||||
scope.spawn(move || {
|
|
||||||
module.produce_events(module_sender);
|
|
||||||
info!("Producer thread for \"{}\" is exiting", key);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// tenquestionmarks main event loop.
|
// tenquestionmarks main event loop.
|
||||||
// tenquestionmarks receives events produced by Modules and pushes them
|
// tenquestionmarks receives events produced by Modules and pushes them
|
||||||
// into all other Modules
|
// into all other Modules
|
||||||
loop {
|
loop {
|
||||||
match receiver.recv() {
|
match receiver.recv() {
|
||||||
Ok(event) => {
|
Ok(envelope) => {
|
||||||
|
let arc_envelope = Arc::new(envelope);
|
||||||
for (key, sender) in &senders {
|
for (key, sender) in &senders {
|
||||||
match sender.send(event.clone()) {
|
match sender.send(arc_envelope.clone()) {
|
||||||
Err(err) => debug!("Failed to dispatch event to module \"{}\": {:?}", key, err),
|
Err(err) => debug!("Failed to dispatch event to module \"{}\": {:?}", key, err),
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
}
|
}
|
||||||
@ -138,5 +130,5 @@ impl User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait MessageSender : Sync + Send {
|
pub trait MessageSender : Sync + Send {
|
||||||
fn send_message (&self, message: &str) {}
|
fn send_message (&self, _: &str) {}
|
||||||
}
|
}
|
||||||
|
12
src/main.rs
12
src/main.rs
@ -15,16 +15,16 @@ extern crate env_logger;
|
|||||||
fn main () {
|
fn main () {
|
||||||
env_logger::init().unwrap();
|
env_logger::init().unwrap();
|
||||||
|
|
||||||
let configFileName = env::args().nth(1).unwrap_or("tenquestionmarks.toml".into());
|
let config_file_name = env::args().nth(1).unwrap_or("tenquestionmarks.toml".into());
|
||||||
match File::open(&configFileName) {
|
match File::open(&config_file_name) {
|
||||||
Ok(mut file) => {
|
Ok(mut file) => {
|
||||||
let mut contents = String::new();
|
let mut contents = String::new();
|
||||||
match file.read_to_string(&mut contents) {
|
match file.read_to_string(&mut contents) {
|
||||||
Ok(value) => {
|
Ok(_) => {
|
||||||
let mut parser = Parser::new(&contents);
|
let mut parser = Parser::new(&contents);
|
||||||
match parser.parse() {
|
match parser.parse() {
|
||||||
Some(configuration) => {
|
Some(configuration) => {
|
||||||
info!("Loaded configuration from: {}", configFileName);
|
info!("Loaded configuration from: {}", config_file_name);
|
||||||
match Tenquestionmarks::from_configuration(configuration) {
|
match Tenquestionmarks::from_configuration(configuration) {
|
||||||
Ok(tqm) => {
|
Ok(tqm) => {
|
||||||
info!("tenquestionmarks initialized successfully");
|
info!("tenquestionmarks initialized successfully");
|
||||||
@ -34,12 +34,12 @@ fn main () {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
error!("Failed to parse config file {}: {:?}. Config file must be a valid TOML file.", configFileName, parser.errors);
|
error!("Failed to parse config file {}: {:?}. Config file must be a valid TOML file.", config_file_name, parser.errors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Failed to open config file {}: {:?}", configFileName, e);
|
error!("Failed to open config file {}: {:?}", config_file_name, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -8,7 +8,7 @@ use toml::Table;
|
|||||||
use event;
|
use event;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::{Sender, Receiver};
|
||||||
|
|
||||||
use {MessageSender, Message, User, Channel};
|
use {MessageSender, Message, User, Channel};
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ impl MessageSender for DiscordMessageSender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Module for DiscordModule {
|
impl Module for DiscordModule {
|
||||||
fn produce_events<'a>(&'a self, sender: Sender<Arc<event::Event>>) {
|
fn run (&self, sender: Sender<event::Envelope>, _: Receiver<Arc<event::Envelope>>) {
|
||||||
let discord = Arc::new(Discord::from_bot_token(&self.token[..]).expect("Discord module: Login failed"));
|
let discord = Arc::new(Discord::from_bot_token(&self.token[..]).expect("Discord module: Login failed"));
|
||||||
let (mut connection, _) = discord.connect().expect("Discord module: Connection failed");
|
let (mut connection, _) = discord.connect().expect("Discord module: Connection failed");
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ impl Module for DiscordModule {
|
|||||||
channel: Some(channel)
|
channel: Some(channel)
|
||||||
};
|
};
|
||||||
|
|
||||||
match sender.send(Arc::new(event::Event::Message { message: message })) {
|
match sender.send(event::Envelope::new(event::Event::Message { message: message })) {
|
||||||
Err(err) => error!("Error sending message event: {:?}", err),
|
Err(err) => error!("Error sending message event: {:?}", err),
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,10 @@ use modules::Module;
|
|||||||
use toml::Table;
|
use toml::Table;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::mpsc::Receiver;
|
use std::sync::mpsc::{Sender, Receiver};
|
||||||
|
|
||||||
use helpers::command::split_command;
|
use helpers::command::split_command;
|
||||||
use event::Event;
|
use event::{Event, Envelope};
|
||||||
|
|
||||||
pub struct EchoModule {
|
pub struct EchoModule {
|
||||||
prefix: String
|
prefix: String
|
||||||
@ -24,11 +24,11 @@ impl EchoModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Module for EchoModule {
|
impl Module for EchoModule {
|
||||||
fn consume_events (&self, receiver: Receiver<Arc<Event>>) {
|
fn run(&self, _: Sender<Envelope>, receiver: Receiver<Arc<Envelope>>) {
|
||||||
loop {
|
loop {
|
||||||
match receiver.recv() {
|
match receiver.recv() {
|
||||||
Ok(event) => {
|
Ok(envelope) => {
|
||||||
match *event {
|
match *envelope.event {
|
||||||
Event::Message { ref message } => {
|
Event::Message { ref message } => {
|
||||||
debug!("Received message... {:?}", message.content);
|
debug!("Received message... {:?}", message.content);
|
||||||
match split_command(&message.content) {
|
match split_command(&message.content) {
|
||||||
|
@ -2,10 +2,10 @@ use modules::Module;
|
|||||||
use toml::Table;
|
use toml::Table;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::mpsc::Receiver;
|
use std::sync::mpsc::{Sender, Receiver};
|
||||||
|
|
||||||
use helpers::command::split_command;
|
use helpers::command::split_command;
|
||||||
use event::Event;
|
use event::{Event, Envelope};
|
||||||
use echobox::Echobox;
|
use echobox::Echobox;
|
||||||
|
|
||||||
pub struct EchoboxModule {
|
pub struct EchoboxModule {
|
||||||
@ -31,13 +31,13 @@ impl EchoboxModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Module for EchoboxModule {
|
impl Module for EchoboxModule {
|
||||||
fn consume_events (&self, receiver: Receiver<Arc<Event>>) {
|
fn run(&self, _: Sender<Envelope>, receiver: Receiver<Arc<Envelope>>) {
|
||||||
let echobox = Echobox::with_file(&self.file).unwrap();
|
let echobox = Echobox::with_file(&self.file).unwrap();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match receiver.recv() {
|
match receiver.recv() {
|
||||||
Ok(event) => {
|
Ok(envelope) => {
|
||||||
match *event {
|
match *envelope.event {
|
||||||
Event::Message { ref message } => {
|
Event::Message { ref message } => {
|
||||||
debug!("Received message... {:?}", message.content);
|
debug!("Received message... {:?}", message.content);
|
||||||
match split_command(&message.content) {
|
match split_command(&message.content) {
|
||||||
|
@ -6,7 +6,7 @@ pub struct LuaModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl LuaModule {
|
impl LuaModule {
|
||||||
pub fn new (configuration: &Table) -> Box<Module> {
|
pub fn new (_: &Table) -> Box<Module> {
|
||||||
Box::new(LuaModule {})
|
Box::new(LuaModule {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,12 @@ pub mod echobox;
|
|||||||
pub mod loader;
|
pub mod loader;
|
||||||
|
|
||||||
use Tenquestionmarks;
|
use Tenquestionmarks;
|
||||||
use event::Event;
|
use event::Envelope;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::mpsc::{Sender, Receiver};
|
use std::sync::mpsc::{Sender, Receiver};
|
||||||
|
|
||||||
pub trait Module : Sync {
|
pub trait Module : Sync {
|
||||||
fn register (&self, tenquestionmarks: &Tenquestionmarks) {}
|
fn register (&self, _: &Tenquestionmarks) {}
|
||||||
fn consume_events (&self, receiver: Receiver<Arc<Event>>) {}
|
fn run (&self, _: Sender<Envelope>, _: Receiver<Arc<Envelope>>) {}
|
||||||
fn produce_events<'a>(&'a self, sender: Sender<Arc<Event>>) {}
|
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,12 @@ use modules::Module;
|
|||||||
use toml::Table;
|
use toml::Table;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::mpsc::Receiver;
|
use std::sync::mpsc::{Sender, Receiver};
|
||||||
use event::Event;
|
use event::{Event, Envelope};
|
||||||
|
|
||||||
use {Message, Channel, User};
|
use Message;
|
||||||
use helpers::command::split_command;
|
use helpers::command::split_command;
|
||||||
|
|
||||||
use pvn::Error;
|
|
||||||
use pvn::Fighter;
|
use pvn::Fighter;
|
||||||
use pvn::pirates::{Pirate, Pirates};
|
use pvn::pirates::{Pirate, Pirates};
|
||||||
use pvn::ninjas::{Ninja, Ninjas};
|
use pvn::ninjas::{Ninja, Ninjas};
|
||||||
@ -16,7 +15,7 @@ use pvn::ninjas::{Ninja, Ninjas};
|
|||||||
pub struct PvnModule {}
|
pub struct PvnModule {}
|
||||||
|
|
||||||
impl PvnModule {
|
impl PvnModule {
|
||||||
pub fn new (configuration: &Table) -> Box<Module> {
|
pub fn new (_: &Table) -> Box<Module> {
|
||||||
Box::new(PvnModule {})
|
Box::new(PvnModule {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -133,18 +132,10 @@ impl PirateVsNinja {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pirate (&mut self, name: &str) -> Result<&Pirate, Error> {
|
|
||||||
self.pirates.get(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_ninja (&mut self, name: &str) -> Result<&Ninja, Error> {
|
|
||||||
self.ninjas.get(name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Module for PvnModule {
|
impl Module for PvnModule {
|
||||||
fn consume_events (&self, receiver: Receiver<Arc<Event>>) {
|
fn run(&self, _: Sender<Envelope>, receiver: Receiver<Arc<Envelope>>) {
|
||||||
let mut pvn = PirateVsNinja {
|
let mut pvn = PirateVsNinja {
|
||||||
pirates: Pirates::new(),
|
pirates: Pirates::new(),
|
||||||
ninjas: Ninjas::new()
|
ninjas: Ninjas::new()
|
||||||
@ -152,8 +143,8 @@ impl Module for PvnModule {
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
match receiver.recv() {
|
match receiver.recv() {
|
||||||
Ok(event) => {
|
Ok(envelope) => {
|
||||||
match *event {
|
match *envelope.event {
|
||||||
Event::Message { ref message } => {
|
Event::Message { ref message } => {
|
||||||
let command = split_command(&message.content);
|
let command = split_command(&message.content);
|
||||||
debug!("Received message... {:?}", &message.content);
|
debug!("Received message... {:?}", &message.content);
|
||||||
|
@ -2,10 +2,10 @@ use modules::Module;
|
|||||||
use toml::Table;
|
use toml::Table;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::mpsc::Receiver;
|
use std::sync::mpsc::{Sender, Receiver};
|
||||||
|
|
||||||
use helpers::command::split_command;
|
use helpers::command::split_command;
|
||||||
use event::Event;
|
use event::{Event, Envelope};
|
||||||
use rand;
|
use rand;
|
||||||
|
|
||||||
pub struct RandomModule {
|
pub struct RandomModule {
|
||||||
@ -35,13 +35,13 @@ impl RandomModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Module for RandomModule {
|
impl Module for RandomModule {
|
||||||
fn consume_events (&self, receiver: Receiver<Arc<Event>>) {
|
fn run(&self, _: Sender<Envelope>, receiver: Receiver<Arc<Envelope>>) {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match receiver.recv() {
|
match receiver.recv() {
|
||||||
Ok(event) => {
|
Ok(envelope) => {
|
||||||
match *event {
|
match *envelope.event {
|
||||||
Event::Message { ref message } => {
|
Event::Message { ref message } => {
|
||||||
debug!("Received message... {:?}", message.content);
|
debug!("Received message... {:?}", message.content);
|
||||||
match split_command(&message.content) {
|
match split_command(&message.content) {
|
||||||
|
@ -6,8 +6,8 @@ use toml::Table;
|
|||||||
use {MessageSender, Message, User};
|
use {MessageSender, Message, User};
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::{Sender, Receiver};
|
||||||
use event::Event;
|
use event::{Event, Envelope};
|
||||||
|
|
||||||
pub struct StdinModule {}
|
pub struct StdinModule {}
|
||||||
|
|
||||||
@ -23,17 +23,17 @@ impl MessageSender for StdinMessageSender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl StdinModule {
|
impl StdinModule {
|
||||||
pub fn new (configuration: &Table) -> Box<Module> {
|
pub fn new (_: &Table) -> Box<Module> {
|
||||||
Box::new(StdinModule {})
|
Box::new(StdinModule {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Module for StdinModule {
|
impl Module for StdinModule {
|
||||||
fn produce_events<'a>(&'a self, sender: Sender<Arc<Event>>) {
|
fn run(&self, sender: Sender<Envelope>, _: Receiver<Arc<Envelope>>) {
|
||||||
loop {
|
loop {
|
||||||
let mut input = String::new();
|
let mut input = String::new();
|
||||||
match io::stdin().read_line(&mut input) {
|
match io::stdin().read_line(&mut input) {
|
||||||
Ok(n) => {
|
Ok(_) => {
|
||||||
let message = Message {
|
let message = Message {
|
||||||
author: User {
|
author: User {
|
||||||
name: String::from("Dave"),
|
name: String::from("Dave"),
|
||||||
@ -45,7 +45,7 @@ impl Module for StdinModule {
|
|||||||
channel: None
|
channel: None
|
||||||
};
|
};
|
||||||
|
|
||||||
match sender.send(Arc::new(Event::Message { message: message })) {
|
match sender.send(Envelope::new(Event::Message { message: message })) {
|
||||||
Err(err) => error!("Error sending message event: {:?}", err),
|
Err(err) => error!("Error sending message event: {:?}", err),
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user