extern crate rusqlite;
extern crate serde_json;
extern crate chrono;
use rusqlite::types::{ToSql};
use rusqlite::{Connection, NO_PARAMS};
use chrono::DateTime;
use chrono::offset::Local;
use irc::client::prelude::*;
fn get_usage_string() -> Vec<&'static str> {
return vec![
"!tell <target> <message> # Tells <message> to <target> when <target> joins the channel",
"!tell@ga <target> # Get the alias group for <target>",
"!tell@aa <target_group> <new_alias> # Add alias to <target_group>",
"!tell@da <target_group> <alias> # Delete alias from <target_group>"
]
}
#[derive(Debug, Clone)]
pub struct AliasRow {
id: u32,
aliases: String,
}
impl PartialEq for AliasRow {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
type Aliases = Vec<String>;
#[derive(Debug, Clone)]
pub struct MessageCacheRow {
id: u32,
pub to_name: String,
pub message: String,
pub in_chan: Option<String>,
pub from_name: String,
pub time_stamp: DateTime<Local>,
}
pub trait DaoTrait {
fn get_message_for(&self, who: &str, in_chan: &str) -> Result<Option<Vec<MessageCacheRow>>, &'static str>;
fn add_message_for(&self, who: &str, what: &str, chan: &Option<&str>, from: &str) -> Result<(), &'static str>;
fn del_message(&self, msg: &MessageCacheRow) -> Result<(), &'static str>;
fn get_alias_for(&self, who: &str) -> Result<(Option<AliasRow>, Aliases), &'static str>;
fn add_alias_for(&self, who: &str, to_add: &str) -> Result<Aliases, &'static str>;
fn del_alias_for(&self, who: &str, to_del: &str) -> Result<Aliases, &'static str>;
}
pub struct Dao {
connection: Connection,
}
impl Dao {
pub fn new(conn: Connection) -> Dao {
Dao { connection: conn }
}
fn _get_message_for_single(&self, who: &str, in_chan: &str) -> Result<Option<Vec<MessageCacheRow>>, &'static str> {
let mut stmt = self.connection
.prepare("SELECT id, to_name, message, in_chan, from_name, iso_date
FROM MessageCache WHERE to_name IS ?1 AND in_chan is ?2").unwrap();
let iter = stmt.query_map(&[&who as &ToSql, &in_chan as &ToSql], |row| Ok(MessageCacheRow {
id: row.get(0).unwrap(),
to_name: row.get(1).unwrap(),
message: row.get(2).unwrap(),
in_chan: row.get(3).unwrap(),
from_name: row.get(4).unwrap(),
time_stamp: row.get(5).unwrap(),
}
)).unwrap();
let res: Vec<MessageCacheRow> = iter.map(|a| a.unwrap()).collect();
return Ok(Some(res))
}
}
impl DaoTrait for Dao {
fn del_message(&self, msg: &MessageCacheRow) -> Result<(), &'static str> {
let res = self.connection
.execute("DELETE FROM MessageCache WHERE id = ?1", &[&msg.id]);
match res {
Ok(_) => Ok(()),
Err(e) => { println!("{}", e); Err("SQL error") },
}
}
fn add_message_for(&self, who: &str, what: &str, chan: &Option<&str>, from: &str) -> Result<(), &'static str> {
let now = Local::now();
let result = self.connection
.execute("INSERT INTO MessageCache (to_name, message, in_chan, from_name, iso_date)
VALUES (?1, ?2, ?3, ?4, ?5)",
&[&who as &ToSql, &what as &ToSql, chan, &from as &ToSql, &now]);
match result {
Ok(_) => Ok(()),
Err(e) => { println!("{}", e); Err("SQL error") },
}
}
fn get_message_for(&self, who: &str, in_chan: &str) -> Result<Option<Vec<MessageCacheRow>>, &'static str> {
let aliases = self.get_alias_for(who).unwrap();
let mut results: Vec<MessageCacheRow> = vec!();
for alias in aliases.1.iter() {
let msgs = self._get_message_for_single(&alias, &in_chan);
if let Ok(opt) = msgs {
if let Some(mut data) = opt {
results.append(&mut data);
}
} else if let Err(e) = msgs {
return Err(e)
}
}
if results.len() == 0 {
Ok(None)
} else {
Ok(Some(results))
}
}
fn get_alias_for(&self, who: &str) -> Result<(Option<AliasRow>, Aliases), &'static str> {
let mut stmt = self.connection
.prepare("SELECT id, aliases FROM Aliases").unwrap();
let iter = stmt.query_map(NO_PARAMS, |row| Ok(AliasRow {
id: row.get(0).unwrap(),
aliases: row.get(1).unwrap(),
})).unwrap();
let mapped: Vec<(AliasRow, Aliases)> = iter
.map(|item| {
let temp = item.unwrap();
let als = serde_json::from_str::<Aliases>(&temp.aliases).unwrap();
(temp, als)
})
.filter(|v| { v.1.contains(&who.to_string()) } )
.collect();
if mapped.len() == 0 {
Ok((None, vec!(String::from(who))))
} else {
Ok((Some(mapped.get(0).unwrap().0.clone()), mapped.get(0).unwrap().1.to_vec()))
}
}
fn add_alias_for(&self, who: &str, to_add: &str) -> Result<Aliases, &'static str> {
if who.eq_ignore_ascii_case(to_add) {
return Err("The two aliases are the same")
}
let res = self.get_alias_for(who);
let res_to_add = self.get_alias_for(to_add);
if let Ok((Some(ref row0), _)) = res_to_add {
if let Ok((Some(ref row1), _)) = res {
if row0 == row1 {
return Err("Alias already exists")
}
} else {
return Err("Attempting to assign alias to another group of aliases!")
}
}
if let Ok((Some(row), mut als)) = res {
let mut stmt = self.connection
.prepare("UPDATE Aliases SET aliases = ?1 WHERE id = ?2").unwrap();
als.push(String::from(to_add));
stmt.execute(&[serde_json::to_string(&als).unwrap(), row.id.to_string()]).unwrap();
return Ok(als)
} else if let Ok((None, mut als)) = res {
let mut add_stmt = self.connection
.prepare("INSERT INTO Aliases (aliases) VALUES (?1)").unwrap();
als.push(String::from(to_add));
add_stmt.execute(&[serde_json::to_string(&als).unwrap()]).unwrap();
return Ok(als)
} else {
Err("SQL error")
}
}
fn del_alias_for(&self, who: &str, to_del: &str) -> Result<Aliases, &'static str> {
let res = self.get_alias_for(who);
let res_to_del = self.get_alias_for(to_del);
if let Ok((Some(ref row0), ref al_del)) = res_to_del {
if let Ok((Some(ref row1), _)) = res {
if row0 == row1 {
let new_vec: Aliases = al_del.iter().filter(|e| { **e != String::from(to_del)}).cloned().collect();
let mut stmt = self.connection
.prepare("UPDATE Aliases SET aliases = ?1 WHERE id = ?2").unwrap();
stmt.execute(&[serde_json::to_string(&new_vec).unwrap(), row0.id.to_string()]).unwrap();
return Ok(new_vec)
} else {
return Err("The two are not aliases!");
}
} else {
return Err("Attempting to assign alias to another group of aliases!")
}
}
return Err("The two are not aliases!");
}
}
pub fn get_actual_message(what: &str) -> &str {
let mut counter = 0;
let index = what.find(move|c: char| {
if c.is_whitespace() {
counter = counter + 1;
}
counter >= 2
});
match slice_index(&what, &index) {
Some(s) => s,
None => &what[0..0],
}
}
fn slice_index<'a>(what: &'a str, index: &Option<usize>) -> Option<&'a str> {
match index {
Some(i) => Some(&what[*i+1..]),
None => None,
}
}
pub fn new_config_from_file(file_name: &str) -> Result<irc::client::prelude::Config, irc::error::IrcError> {
irc::client::prelude::Config::load(&file_name)
}
pub fn make_handler_fn<U>(dao: Dao) -> impl (FnMut(&IrcClient, Message) -> Result<(), U>) {
return move|client: &IrcClient, message: Message| {
if let Command::PRIVMSG(ref __, ref msg) = message.command {
let target = message.response_target().unwrap().clone();
let sender = message.source_nickname().unwrap().clone();
if !target.starts_with("#") {
client.send_privmsg(target, "Please don't PM me sir, this is WIP").unwrap();
return Ok(())
}
if msg.starts_with("!tell") {
let trimmed = msg.trim();
let tks: Vec<&str> = trimmed.split(" ").collect();
let tks_len = tks.len();
if tks_len > 2 && tks[0].eq_ignore_ascii_case("!tell@aa") {
let add_res = dao.add_alias_for(tks[1], tks[2]);
if let Ok(als) = add_res {
client.send_privmsg(target, format!("{:?} are now aliases", als)).unwrap();
return Ok(())
} else if let Err(e) = add_res {
client.send_privmsg(target, format!("{:?}", e)).unwrap();
return Ok(())
}
} else if tks_len > 1 && tks[0].eq_ignore_ascii_case("!tell@ga") {
let get_res = dao.get_alias_for(tks[1]);
if let Ok((_, als)) = get_res {
client.send_privmsg(target, format!("{:?} are aliases", als)).unwrap();
return Ok(())
} else if let Err(err) = get_res {
client.send_privmsg(target, err).unwrap();
}
} else if tks_len > 2 && tks[0].eq_ignore_ascii_case("!tell@da") {
let del_res = dao.del_alias_for(tks[1], tks[2]);
if let Ok(ref als) = del_res {
client.send_privmsg(target, format!("{:?} are now aliases", als)).unwrap();
return Ok(())
} else if let Err(err) = del_res {
client.send_privmsg(target, err).unwrap();
}
} else if tks_len > 0 && tks[0].eq_ignore_ascii_case("!tell@quit") {
client.send_privmsg(target, "No! >:(").unwrap();
return Ok(())
} else if tks_len > 1 && tks[0].eq_ignore_ascii_case("!tell@join") {
let chan = tks[1];
match client.send_join(chan) {
Ok(_) => return Ok(()),
Err(e) => client.send_privmsg(target, format!("Cannot join {}, Error: {:#?}", chan, e)).unwrap(),
};
} else if tks_len > 2 && tks[0].eq_ignore_ascii_case("!tell") {
let who = tks[1];
let als = dao.get_alias_for(who).unwrap().1;
if client.list_users(target).unwrap().iter().any(|user| { als.contains(&String::from(user.get_nickname())) } ) {
client.send_privmsg(target, format!("{} is here! Why don't you tell that to them yourself??", who)).unwrap();
return Ok(())
}
let actual_msg = get_actual_message(&msg);
let dao_res = dao.add_message_for(who, actual_msg, &Some(target), &sender);
match dao_res {
Ok(_) => client.send_privmsg(target, format!("{}: Got it, sending \"{}\" to {} in {} upon joining", sender, actual_msg, who, target)).unwrap(),
Err(err) => client.send_privmsg(target, err).unwrap(),
}
} else {
get_usage_string().iter().for_each(|item: &&str| {
client.send_privmsg(sender, item).unwrap();
});
}
}
} else if let Command::JOIN(ref chanlist, ref __, ref ___) = message.command {
let target = message.response_target().unwrap().clone();
let dao_res = dao.get_message_for(&target, chanlist);
match dao_res {
Ok(Some(msgs)) => msgs.iter().for_each(|msg| {
client.send_privmsg(chanlist, format!("{}: {} tells you \"{}\" at {}", target, msg.from_name, msg.message, msg.time_stamp)).unwrap();
dao.del_message(msg).unwrap();
}),
_ => ()
}
return Ok(())
}
Ok(())
};
}