diff options
Diffstat (limited to 'src/plugins/factoids')
| -rw-r--r-- | src/plugins/factoids/database.rs | 163 | ||||
| -rw-r--r-- | src/plugins/factoids/mod.rs | 342 | ||||
| -rw-r--r-- | src/plugins/factoids/sandbox.lua | 86 | ||||
| -rw-r--r-- | src/plugins/factoids/utils.rs | 25 |
4 files changed, 0 insertions, 616 deletions
diff --git a/src/plugins/factoids/database.rs b/src/plugins/factoids/database.rs deleted file mode 100644 index b1fe8dd..0000000 --- a/src/plugins/factoids/database.rs +++ /dev/null @@ -1,163 +0,0 @@ -#[cfg(feature = "mysql")] -extern crate dotenv; - -#[cfg(feature = "mysql")] -use std::sync::Arc; -use std::collections::HashMap; - -#[cfg(feature = "mysql")] -use diesel::prelude::*; -#[cfg(feature = "mysql")] -use diesel::mysql::MysqlConnection; -#[cfg(feature = "mysql")] -use r2d2::Pool; -#[cfg(feature = "mysql")] -use r2d2_diesel::ConnectionManager; -#[cfg(feature = "mysql")] -use failure::ResultExt; - -use chrono::NaiveDateTime; - -use super::error::*; - -#[cfg_attr(feature = "mysql", derive(Queryable))] -#[derive(Clone, Debug)] -pub struct Factoid { - pub name: String, - pub idx: i32, - pub content: String, - pub author: String, - pub created: NaiveDateTime, -} - -#[cfg_attr(feature = "mysql", derive(Insertable))] -#[cfg_attr(feature = "mysql", table_name = "factoids")] -pub struct NewFactoid<'a> { - pub name: &'a str, - pub idx: i32, - pub content: &'a str, - pub author: &'a str, - pub created: NaiveDateTime, -} - -pub trait Database: Send { - fn insert_factoid(&mut self, factoid: &NewFactoid) -> Result<(), FactoidsError>; - fn get_factoid(&self, name: &str, idx: i32) -> Result<Factoid, FactoidsError>; - fn delete_factoid(&mut self, name: &str, idx: i32) -> Result<(), FactoidsError>; - fn count_factoids(&self, name: &str) -> Result<i32, FactoidsError>; -} - -// HashMap -impl Database for HashMap<(String, i32), Factoid> { - fn insert_factoid(&mut self, factoid: &NewFactoid) -> Result<(), FactoidsError> { - let factoid = Factoid { - name: String::from(factoid.name), - idx: factoid.idx, - content: factoid.content.to_string(), - author: factoid.author.to_string(), - created: factoid.created, - }; - - let name = factoid.name.clone(); - match self.insert((name, factoid.idx), factoid) { - None => Ok(()), - Some(_) => Err(ErrorKind::Duplicate)?, - } - } - - fn get_factoid(&self, name: &str, idx: i32) -> Result<Factoid, FactoidsError> { - Ok(self.get(&(String::from(name), idx)) - .cloned() - .ok_or(ErrorKind::NotFound)?) - } - - fn delete_factoid(&mut self, name: &str, idx: i32) -> Result<(), FactoidsError> { - match self.remove(&(String::from(name), idx)) { - Some(_) => Ok(()), - None => Err(ErrorKind::NotFound)?, - } - } - - fn count_factoids(&self, name: &str) -> Result<i32, FactoidsError> { - Ok(self.iter().filter(|&(&(ref n, _), _)| n == name).count() as i32) - } -} - -// Diesel automatically defines the factoids module as public. -// We create a schema module to keep it private. -#[cfg(feature = "mysql")] -mod schema { - table! { - factoids (name, idx) { - name -> Varchar, - idx -> Integer, - content -> Text, - author -> Varchar, - created -> Timestamp, - } - } -} - -#[cfg(feature = "mysql")] -use self::schema::factoids; - -#[cfg(feature = "mysql")] -impl Database for Arc<Pool<ConnectionManager<MysqlConnection>>> { - fn insert_factoid(&mut self, factoid: &NewFactoid) -> Result<(), FactoidsError> { - use diesel; - - let conn = &*self.get().context(ErrorKind::NoConnection)?; - diesel::insert_into(factoids::table) - .values(factoid) - .execute(conn) - .context(ErrorKind::MysqlError)?; - - Ok(()) - } - - fn get_factoid(&self, name: &str, idx: i32) -> Result<Factoid, FactoidsError> { - let conn = &*self.get().context(ErrorKind::NoConnection)?; - Ok(factoids::table - .find((name, idx)) - .first(conn) - .context(ErrorKind::MysqlError)?) - } - - fn delete_factoid(&mut self, name: &str, idx: i32) -> Result<(), FactoidsError> { - use diesel; - use self::factoids::columns; - - let conn = &*self.get().context(ErrorKind::NoConnection)?; - match diesel::delete( - factoids::table - .filter(columns::name.eq(name)) - .filter(columns::idx.eq(idx)), - ).execute(conn) - { - Ok(v) => { - if v > 0 { - Ok(()) - } else { - Err(ErrorKind::NotFound)? - } - } - Err(e) => Err(e).context(ErrorKind::MysqlError)?, - } - } - - fn count_factoids(&self, name: &str) -> Result<i32, FactoidsError> { - use diesel; - - let conn = &*self.get().context(ErrorKind::NoConnection)?; - let count: Result<i64, _> = factoids::table - .filter(factoids::columns::name.eq(name)) - .count() - .get_result(conn); - - match count { - Ok(c) => Ok(c as i32), - Err(diesel::NotFound) => Ok(0), - Err(e) => Err(e).context(ErrorKind::MysqlError)?, - } - } -} diff --git a/src/plugins/factoids/mod.rs b/src/plugins/factoids/mod.rs deleted file mode 100644 index 2f3690f..0000000 --- a/src/plugins/factoids/mod.rs +++ /dev/null @@ -1,342 +0,0 @@ -extern crate rlua; - -use std::fmt; -use std::str::FromStr; -use std::sync::Mutex; -use self::rlua::prelude::*; -use irc::client::prelude::*; - -use time; -use chrono::NaiveDateTime; - -use plugin::*; -pub mod database; -use self::database::Database; - -mod utils; -use self::utils::*; - -use failure::ResultExt; -use error::ErrorKind as FrippyErrorKind; -use error::FrippyError; -use self::error::*; - -static LUA_SANDBOX: &'static str = include_str!("sandbox.lua"); - -#[derive(PluginName)] -pub struct Factoids<T: Database> { - factoids: Mutex<T>, -} - -macro_rules! try_lock { - ( $m:expr ) => { - match $m.lock() { - Ok(guard) => guard, - Err(poisoned) => poisoned.into_inner(), - } - } -} - -impl<T: Database> Factoids<T> { - pub fn new(db: T) -> Factoids<T> { - Factoids { - factoids: Mutex::new(db), - } - } - - fn create_factoid( - &self, - name: &str, - content: &str, - author: &str, - ) -> Result<&str, FactoidsError> { - let count = try_lock!(self.factoids).count_factoids(name)?; - let tm = time::now().to_timespec(); - - let factoid = database::NewFactoid { - name: name, - idx: count, - content: content, - author: author, - created: NaiveDateTime::from_timestamp(tm.sec, 0u32), - }; - - Ok(try_lock!(self.factoids) - .insert_factoid(&factoid) - .map(|()| "Successfully added!")?) - } - - fn add(&self, command: &mut PluginCommand) -> Result<&str, FactoidsError> { - if command.tokens.len() < 2 { - Err(ErrorKind::InvalidCommand)?; - } - - let name = command.tokens.remove(0); - let content = command.tokens.join(" "); - - Ok(self.create_factoid(&name, &content, &command.source)?) - } - - fn add_from_url(&self, command: &mut PluginCommand) -> Result<&str, FactoidsError> { - if command.tokens.len() < 2 { - Err(ErrorKind::InvalidCommand)?; - } - - let name = command.tokens.remove(0); - let url = &command.tokens[0]; - let content = ::utils::download(url, Some(1024)).context(ErrorKind::Download)?; - - Ok(self.create_factoid(&name, &content, &command.source)?) - } - - fn remove(&self, command: &mut PluginCommand) -> Result<&str, FactoidsError> { - if command.tokens.len() < 1 { - Err(ErrorKind::InvalidCommand)?; - } - - let name = command.tokens.remove(0); - let count = try_lock!(self.factoids).count_factoids(&name)?; - - match try_lock!(self.factoids).delete_factoid(&name, count - 1) { - Ok(()) => Ok("Successfully removed"), - Err(e) => Err(e)?, - } - } - - fn get(&self, command: &PluginCommand) -> Result<String, FactoidsError> { - let (name, idx) = match command.tokens.len() { - 0 => Err(ErrorKind::InvalidCommand)?, - 1 => { - let name = &command.tokens[0]; - let count = try_lock!(self.factoids).count_factoids(name)?; - - if count < 1 { - Err(ErrorKind::NotFound)?; - } - - (name, count - 1) - } - _ => { - let name = &command.tokens[0]; - let idx = match i32::from_str(&command.tokens[1]) { - Ok(i) => i, - Err(_) => Err(ErrorKind::InvalidCommand)?, - }; - - (name, idx) - } - }; - - let factoid = try_lock!(self.factoids) - .get_factoid(name, idx) - .context(ErrorKind::NotFound)?; - - let message = factoid.content.replace("\n", "|").replace("\r", ""); - - Ok(format!("{}: {}", factoid.name, message)) - } - - fn info(&self, command: &PluginCommand) -> Result<String, FactoidsError> { - match command.tokens.len() { - 0 => Err(ErrorKind::InvalidCommand)?, - 1 => { - let name = &command.tokens[0]; - let count = try_lock!(self.factoids).count_factoids(name)?; - - Ok(match count { - 0 => Err(ErrorKind::NotFound)?, - 1 => format!("There is 1 version of {}", name), - _ => format!("There are {} versions of {}", count, name), - }) - } - _ => { - let name = &command.tokens[0]; - let idx = i32::from_str(&command.tokens[1]).context(ErrorKind::InvalidIndex)?; - let factoid = try_lock!(self.factoids).get_factoid(name, idx)?; - - Ok(format!( - "{}: Added by {} at {} UTC", - name, factoid.author, factoid.created - )) - } - } - } - - fn exec(&self, mut command: PluginCommand) -> Result<String, FactoidsError> { - if command.tokens.len() < 1 { - Err(ErrorKind::InvalidIndex)? - } else { - let name = command.tokens.remove(0); - let count = try_lock!(self.factoids).count_factoids(&name)?; - let factoid = try_lock!(self.factoids).get_factoid(&name, count - 1)?; - - let content = factoid.content; - let value = if content.starts_with('>') { - let content = String::from(&content[1..]); - - if content.starts_with('>') { - content - } else { - match self.run_lua(&name, &content, &command) { - Ok(v) => v, - Err(e) => format!("\"{}\"", e), - } - } - } else { - content - }; - - Ok(value.replace("\n", "|").replace("\r", "")) - } - } - - fn run_lua( - &self, - name: &str, - code: &str, - command: &PluginCommand, - ) -> Result<String, rlua::Error> { - let args = command - .tokens - .iter() - .filter(|x| !x.is_empty()) - .map(ToOwned::to_owned) - .collect::<Vec<String>>(); - - let lua = unsafe { Lua::new_with_debug() }; - let globals = lua.globals(); - - globals.set("factoid", code)?; - globals.set("download", lua.create_function(download)?)?; - globals.set("sleep", lua.create_function(sleep)?)?; - globals.set("args", args)?; - globals.set("input", command.tokens.join(" "))?; - globals.set("user", command.source.clone())?; - globals.set("channel", command.target.clone())?; - globals.set("output", lua.create_table()?)?; - - lua.exec::<()>(LUA_SANDBOX, Some(name))?; - let output: Vec<String> = globals.get::<_, Vec<String>>("output")?; - - Ok(output.join("|")) - } -} - -impl<T: Database> Plugin for Factoids<T> { - fn execute(&self, _: &IrcClient, message: &Message) -> ExecutionStatus { - match message.command { - Command::PRIVMSG(_, ref content) => if content.starts_with('!') { - ExecutionStatus::RequiresThread - } else { - ExecutionStatus::Done - }, - _ => ExecutionStatus::Done, - } - } - - fn execute_threaded(&self, client: &IrcClient, message: &Message) -> Result<(), FrippyError> { - if let Command::PRIVMSG(_, mut content) = message.command.clone() { - content.remove(0); - - let t: Vec<String> = content.split(' ').map(ToOwned::to_owned).collect(); - - let c = PluginCommand { - source: message.source_nickname().unwrap().to_owned(), - target: message.response_target().unwrap().to_owned(), - tokens: t, - }; - - Ok(match self.exec(c) { - Ok(f) => client - .send_privmsg(&message.response_target().unwrap(), &f) - .context(FrippyErrorKind::Connection)?, - Err(_) => (), - }) - } else { - Ok(()) - } - } - - fn command(&self, client: &IrcClient, mut command: PluginCommand) -> Result<(), FrippyError> { - if command.tokens.is_empty() { - return Ok(client - .send_notice(&command.target, "Invalid command") - .context(FrippyErrorKind::Connection)?); - } - - let target = command.target.clone(); - let source = command.source.clone(); - - let sub_command = command.tokens.remove(0); - let result = match sub_command.as_ref() { - "add" => self.add(&mut command).map(|s| s.to_owned()), - "fromurl" => self.add_from_url(&mut command).map(|s| s.to_owned()), - "remove" => self.remove(&mut command).map(|s| s.to_owned()), - "get" => self.get(&command), - "info" => self.info(&command), - "exec" => self.exec(command), - _ => Err(ErrorKind::InvalidCommand.into()), - }; - - Ok(match result { - Ok(v) => client - .send_privmsg(&target, &v) - .context(FrippyErrorKind::Connection)?, - Err(e) => { - let message = e.to_string(); - client - .send_notice(&source, &message) - .context(FrippyErrorKind::Connection)?; - Err(e).context(FrippyErrorKind::Factoids)? - } - }) - } - - fn evaluate(&self, _: &IrcClient, _: PluginCommand) -> Result<String, String> { - Err(String::from( - "Evaluation of commands is not implemented for Factoids at this time", - )) - } -} - -impl<T: Database> fmt::Debug for Factoids<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Factoids {{ ... }}") - } -} - -pub mod error { - #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)] - #[error = "FactoidsError"] - pub enum ErrorKind { - /// Invalid command error - #[fail(display = "Invalid Command")] - InvalidCommand, - - /// Invalid index error - #[fail(display = "Invalid index")] - InvalidIndex, - - /// Download error - #[fail(display = "Download failed")] - Download, - - /// Duplicate error - #[fail(display = "Entry already exists")] - Duplicate, - - /// Not found error - #[fail(display = "Factoid was not found")] - NotFound, - - /// MySQL error - #[cfg(feature = "mysql")] - #[fail(display = "Failed to execute MySQL Query")] - MysqlError, - - /// No connection error - #[cfg(feature = "mysql")] - #[fail(display = "No connection to the database")] - NoConnection, - } -} diff --git a/src/plugins/factoids/sandbox.lua b/src/plugins/factoids/sandbox.lua deleted file mode 100644 index 3fc74cd..0000000 --- a/src/plugins/factoids/sandbox.lua +++ /dev/null @@ -1,86 +0,0 @@ -function send(text) - local text = tostring(text) - local len = #output - if len < 1 then - output = { text } - else - output[len] = output[len] .. text - end -end - -function sendln(text) - send(text) - table.insert(output, "") -end - -local sandbox_env = { - print = send, - println = sendln, - eval = nil, - args = args, - input = input, - user = user, - channel = channel, - request = download, - string = string, - math = math, - table = table, - pairs = pairs, - ipairs = ipairs, - next = next, - select = select, - unpack = unpack, - tostring = tostring, - tonumber = tonumber, - type = type, - assert = assert, - error = error, - pcall = pcall, - xpcall = xpcall, - _VERSION = _VERSION -} - -sandbox_env.os = { - clock = os.clock, - time = os.time, - difftime = os.difftime -} - -sandbox_env.string.rep = nil -sandbox_env.string.dump = nil -sandbox_env.math.randomseed = nil - --- Temporary evaluation function -function eval(code) - local c, e = load(code, nil, nil, sandbox_env) - if c then - return c() - else - error(e) - end -end - -sandbox_env.eval = eval - --- Check if the factoid timed out -function checktime(event, line) - if os.time() - time >= timeout then - error("Timed out after " .. timeout .. " seconds", 0) - else - -- Limit the cpu usage of factoids - sleep(1) - end -end - -local f, e = load(factoid, nil, nil, sandbox_env) - --- Add timeout hook -time = os.time() -timeout = 30 -debug.sethook(checktime, "l") - -if f then - f() -else - error(e) -end diff --git a/src/plugins/factoids/utils.rs b/src/plugins/factoids/utils.rs deleted file mode 100644 index 70ac8a7..0000000 --- a/src/plugins/factoids/utils.rs +++ /dev/null @@ -1,25 +0,0 @@ -extern crate reqwest; - -use std::thread; -use std::time::Duration; - -use utils; -use super::rlua::prelude::*; - -use self::LuaError::RuntimeError; - -pub fn download(_: &Lua, url: String) -> Result<String, LuaError> { - match utils::download(&url, Some(1024)) { - Ok(v) => Ok(v), - Err(e) => Err(RuntimeError(format!( - "Failed to download {} - {}", - url, - e.to_string() - ))), - } -} - -pub fn sleep(_: &Lua, dur: u64) -> Result<(), LuaError> { - thread::sleep(Duration::from_millis(dur)); - Ok(()) -} |
