add a rudimentary irc module

This commit is contained in:
Adrian Malacoda 2018-02-25 04:47:04 -06:00
parent 906b5709d0
commit c6dc3f15b8
6 changed files with 176 additions and 11 deletions

View File

@ -16,6 +16,8 @@ time = "0.1"
regex = "0.2"
multimap = "0.4.0"
notify = "4.0.0"
irc = "0.11.8" # latest version which supports old openssl version (required by discord)
thread_local = "0.3"
pvn = { git = "http://gitlab.monarch-pass.net/malacoda/pvn.git" }
echobox = { git = "http://gitlab.monarch-pass.net/malacoda/echobox.git" }
stc = { git = "http://gitlab.monarch-pass.net/malacoda/stc.git" }

View File

@ -8,6 +8,8 @@ extern crate transformable_channels;
extern crate stc;
extern crate regex;
extern crate multimap;
extern crate irc;
extern crate thread_local;
#[macro_use]
extern crate hlua;

154
src/modules/irc.rs Normal file
View File

@ -0,0 +1,154 @@
use irc::client::prelude::*;
use irc::client::data::command::Command;
use modules::EventLoop;
use toml::value::Table;
use event;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::Receiver;
use transformable_channels::mpsc::ExtSender;
use std::fmt;
use std::fmt::{Debug, Formatter};
use crossbeam;
use thread_local::CachedThreadLocal;
use {MessageSender, Message, User, Channel};
pub struct IrcHandler {
config: Config
}
const DEFAULT_NICK: &'static str = "tenquestionmarks";
impl IrcHandler {
pub fn new (_: &Table, configuration: &Table) -> Box<EventLoop> {
Box::new(IrcHandler {
config: Config {
nickname: Some(configuration.get("nickname")
.and_then(|value| value.as_str())
.unwrap_or(DEFAULT_NICK)
.to_owned()),
server: Some(configuration.get("server")
.and_then(|value| value.as_str())
.expect("Expected server in IRC config")
.to_owned()),
channels: Some(configuration.get("channels")
.and_then(|value| value.as_array())
.map(|value| value.to_vec())
.unwrap_or(vec![])
.into_iter()
.map(|value| { String::from(value.as_str().unwrap()) })
.collect()),
.. Default::default()
}
})
}
}
pub struct IrcMessageSender {
irc: IrcServerWrapper,
channel: String
}
impl MessageSender for IrcMessageSender {
fn send_message (&self, message: &str) {
debug!("Send message to channel {:?}: {:?}", self.channel, message);
match self.irc.get().send_privmsg(&self.channel, message) {
Ok(_) => { debug!("Send message succeeded"); },
Err(err) => { error!("Send message failed: {:?}", err) }
}
}
}
impl Debug for IrcMessageSender {
fn fmt (&self, formatter: &mut Formatter) -> fmt::Result {
write!(formatter, "IrcMessageSender {{ channel: {:?} }}", self.channel)
}
}
fn make_user (prefix: &Option<String>, server: &IrcServer) -> Option<User> {
prefix.as_ref().and_then(|prefix| prefix.split("!").next()).map(|name| User {
name: name.to_owned(),
sender: Box::new(IrcMessageSender {
irc: IrcServerWrapper::new(&server),
channel: name.to_owned()
})
})
}
struct IrcServerWrapper {
server: IrcServer,
thread_local: CachedThreadLocal<IrcServer>
}
impl IrcServerWrapper {
pub fn new (server: &IrcServer) -> IrcServerWrapper {
IrcServerWrapper {
server: server.clone(),
thread_local: CachedThreadLocal::new()
}
}
pub fn get (&self) -> &IrcServer {
self.thread_local.get_or(|| Box::new(self.server.clone()))
}
}
unsafe impl Sync for IrcServerWrapper {}
impl EventLoop for IrcHandler {
fn run (&self, sender: Box<ExtSender<event::Event>>, receiver: Receiver<Arc<event::Envelope>>) {
let server = IrcServer::from_config(self.config.clone()).unwrap();
server.identify().unwrap();
crossbeam::scope(|scope| {
let server_sender = server.clone();
scope.spawn(move || {
loop {
if let Ok(envelope) = receiver.recv() {
if let event::Event::Message { ref message } = envelope.event {
if let Some(ref channel) = message.channel {
server_sender.send_privmsg(&channel.name, &message.content);
}
}
} else {
break;
}
}
});
for server_message in server.iter() {
if let Ok(irc_message) = server_message {
match irc_message.command {
Command::PRIVMSG(channel, message) => {
if let Some(author) = make_user(&irc_message.prefix, &server) {
let message = Message {
author: author,
content: message,
channel: Some(Channel {
name: channel.clone(),
description: "".to_owned(),
topic: "".to_owned(),
sender: Box::new(IrcMessageSender {
irc: IrcServerWrapper::new(&server),
channel: channel.clone()
})
})
};
if let Err(err) = sender.send(event::Event::Message { message: message }) {
error!("Error sending message event: {:?}", err)
}
}
},
_ => {}
}
}
}
});
}
}

View File

@ -14,6 +14,7 @@ use modules::pvn::PvnModule;
use modules::echobox::EchoboxModule;
use modules::autolink::AutolinkModule;
use modules::logger::LoggerModule;
use modules::irc::IrcHandler;
use std::sync::{Arc, Mutex};
@ -33,6 +34,7 @@ impl ModuleLoader {
types.insert("echobox", EchoboxModule::new as fn(&Table, &Table) -> Box<EventLoop>);
types.insert("autolink", AutolinkModule::new as fn(&Table, &Table) -> Box<EventLoop>);
types.insert("logger", LoggerModule::new as fn(&Table, &Table) -> Box<EventLoop>);
types.insert("irc", IrcHandler::new as fn(&Table, &Table) -> Box<EventLoop>);
ModuleLoader {
types: types
}

View File

@ -7,6 +7,7 @@ pub mod pvn;
pub mod echobox;
pub mod autolink;
pub mod logger;
pub mod irc;
pub mod loader;

View File

@ -1,18 +1,22 @@
[general]
foo = "bar"
[irc]
server = "irc.rizon.net"
channels = ["#eightbar"]
[discord]
token = "your token here"
[stdin]
[echo]
parents = ["stdin", "discord"]
parents = ["stdin", "discord", "irc"]
prefix = "?echo"
[no]
type = "random"
parents = ["stdin", "discord"]
parents = ["stdin", "discord", "irc"]
prefix = "?no"
responses = [
"https://www.youtube.com/watch?v=WWaLxFIVX1s", # Darth Vader
@ -38,7 +42,7 @@ responses = [
[yes]
type = "random"
parents = ["stdin", "discord"]
parents = ["stdin", "discord", "irc"]
prefix = "?yes"
responses = [
"https://www.youtube.com/watch?v=JPVaDaynNKM", # Captain Falcon
@ -61,15 +65,15 @@ responses = [
[chk]
type = "random"
parents = ["stdin", "discord"]
parents = ["stdin", "discord", "irc"]
prefix = "?chk"
responses = ["ack"]
[pvn]
parents = ["stdin", "discord"]
parents = ["stdin", "discord", "irc"]
[echobox]
parents = ["stdin", "discord"]
parents = ["stdin", "discord", "irc"]
[lua]
parents = ["stdin", "discord"]
@ -91,20 +95,20 @@ end
"""
[autolink]
parents = ["stdin", "discord"]
parents = ["stdin", "discord", "irc"]
[logger]
parents = ["stdin", "discord"]
filters = [{ username = "Dave" }, { username = "Kevin" }]
parents = ["stdin", "discord", "irc"]
#filters = [{ username = "Dave" }, { username = "Kevin" }]
[icced]
type = "random"
parents = ["stdin", "discord"]
parents = ["stdin", "discord", "irc"]
pattern = "\\bicc?ed?\\b"
responses = ["Did some carbon-based lifeform just say **I C E**?"]
[trout]
type = "random"
parents = ["stdin", "discord"]
parents = ["stdin", "discord", "irc"]
pattern = "^?slap (.*)"
responses = ["/me slaps $1 around a bit with a large trout", "/me slaps $1 around a bit with a large brick"]