aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJokler <jokler.contact@gmail.com>2018-02-25 18:54:16 +0100
committerJokler <jokler.contact@gmail.com>2018-02-25 18:54:16 +0100
commit92668ad4c53edcc1a317a16aa5ea30ca502f54ee (patch)
tree4a93dad53b06f2b4dccfaf0282e8e9ee94b3a070 /src
parent76461addd2fb7ec383123b1efc4368b7662eea04 (diff)
downloadfrippy-92668ad4c53edcc1a317a16aa5ea30ca502f54ee.tar.gz
frippy-92668ad4c53edcc1a317a16aa5ea30ca502f54ee.zip
Add Mysql as a possible database to the Tell plugin
Diffstat (limited to 'src')
-rw-r--r--src/main.rs10
-rw-r--r--src/plugins/factoids/database.rs15
-rw-r--r--src/plugins/factoids/mod.rs2
-rw-r--r--src/plugins/tell/database.rs143
-rw-r--r--src/plugins/tell/mod.rs (renamed from src/plugins/tell.rs)59
5 files changed, 191 insertions, 38 deletions
diff --git a/src/main.rs b/src/main.rs
index 511fe09..ec04d33 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -19,6 +19,8 @@ extern crate r2d2_diesel;
#[macro_use]
extern crate log;
+#[cfg(feature = "mysql")]
+use std::sync::Arc;
use std::collections::HashMap;
use log::{Level, LevelFilter, Metadata, Record};
@@ -115,7 +117,6 @@ fn main() {
bot.add_plugin(plugins::Emoji::new());
bot.add_plugin(plugins::Currency::new());
bot.add_plugin(plugins::KeepNick::new());
- bot.add_plugin(plugins::Tell::new());
#[cfg(feature = "mysql")]
{
@@ -130,11 +131,14 @@ fn main() {
.expect("Failed to get connection"))
{
Ok(_) => {
- bot.add_plugin(plugins::Factoids::new(pool));
+ let pool = Arc::new(pool);
+ bot.add_plugin(plugins::Factoids::new(pool.clone()));
+ bot.add_plugin(plugins::Tell::new(pool.clone()));
info!("Connected to MySQL server")
}
Err(e) => {
bot.add_plugin(plugins::Factoids::new(HashMap::new()));
+ bot.add_plugin(plugins::Tell::new(HashMap::new()));
error!("Failed to run migrations: {}", e);
}
},
@@ -142,6 +146,7 @@ fn main() {
}
} else {
bot.add_plugin(plugins::Factoids::new(HashMap::new()));
+ bot.add_plugin(plugins::Tell::new(HashMap::new()));
}
}
#[cfg(not(feature = "mysql"))]
@@ -150,6 +155,7 @@ fn main() {
error!("frippy was not built with the mysql feature")
}
bot.add_plugin(plugins::Factoids::new(HashMap::new()));
+ bot.add_plugin(plugins::Tell::new(HashMap::new()));
}
if let Some(disabled_plugins) = disabled_plugins {
diff --git a/src/plugins/factoids/database.rs b/src/plugins/factoids/database.rs
index 386d3f7..88aa0fd 100644
--- a/src/plugins/factoids/database.rs
+++ b/src/plugins/factoids/database.rs
@@ -1,6 +1,8 @@
#[cfg(feature = "mysql")]
extern crate dotenv;
+#[cfg(feature = "mysql")]
+use std::sync::Arc;
use std::collections::HashMap;
#[cfg(feature = "mysql")]
@@ -29,8 +31,6 @@ pub struct Factoid {
pub created: NaiveDateTime,
}
-#[cfg(feature = "mysql")]
-use self::mysql::factoids;
#[cfg_attr(feature = "mysql", derive(Insertable))]
#[cfg_attr(feature = "mysql", table_name = "factoids")]
pub struct NewFactoid<'a> {
@@ -82,10 +82,10 @@ impl Database for HashMap<(String, i32), Factoid> {
}
}
-// Diesel automatically define the factoids module as public.
-// For now this is how we keep it private.
+// Diesel automatically defines the factoids module as public.
+// We create a schema module to keep it private.
#[cfg(feature = "mysql")]
-mod mysql {
+mod schema {
table! {
factoids (name, idx) {
name -> Varchar,
@@ -98,7 +98,10 @@ mod mysql {
}
#[cfg(feature = "mysql")]
-impl Database for Pool<ConnectionManager<MysqlConnection>> {
+use self::schema::factoids;
+
+#[cfg(feature = "mysql")]
+impl Database for Arc<Pool<ConnectionManager<MysqlConnection>>> {
fn insert_factoid(&mut self, factoid: &NewFactoid) -> DbResponse {
use diesel;
diff --git a/src/plugins/factoids/mod.rs b/src/plugins/factoids/mod.rs
index c3d19b0..d6abb1d 100644
--- a/src/plugins/factoids/mod.rs
+++ b/src/plugins/factoids/mod.rs
@@ -49,7 +49,7 @@ impl<T: Database> Factoids<T> {
idx: count,
content: content,
author: author,
- created: NaiveDateTime::from_timestamp(tm.sec, tm.nsec as u32),
+ created: NaiveDateTime::from_timestamp(tm.sec, 0u32),
};
match try_lock!(self.factoids).insert_factoid(&factoid) {
diff --git a/src/plugins/tell/database.rs b/src/plugins/tell/database.rs
new file mode 100644
index 0000000..277847e
--- /dev/null
+++ b/src/plugins/tell/database.rs
@@ -0,0 +1,143 @@
+#[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;
+
+use chrono::NaiveDateTime;
+
+pub enum DbResponse {
+ Success,
+ Failed(&'static str),
+}
+
+#[cfg_attr(feature = "mysql", derive(Queryable))]
+#[derive(PartialEq, Clone, Debug)]
+pub struct TellMessage {
+ pub id: i64,
+ pub sender: String,
+ pub receiver: String,
+ pub time: NaiveDateTime,
+ pub message: String,
+}
+
+#[cfg_attr(feature = "mysql", derive(Insertable))]
+#[cfg_attr(feature = "mysql", table_name = "tells")]
+pub struct NewTellMessage<'a> {
+ pub sender: &'a str,
+ pub receiver: &'a str,
+ pub time: NaiveDateTime,
+ pub message: &'a str,
+}
+
+pub trait Database: Send {
+ fn insert_tell(&mut self, tell: &NewTellMessage) -> DbResponse;
+ fn get_tells(&self, receiver: &str) -> Option<Vec<TellMessage>>;
+ fn delete_tells(&mut self, receiver: &str) -> DbResponse;
+}
+
+// HashMap
+impl Database for HashMap<String, Vec<TellMessage>> {
+ fn insert_tell(&mut self, tell: &NewTellMessage) -> DbResponse {
+ let tell = TellMessage {
+ id: 0,
+ sender: tell.sender.to_string(),
+ receiver: tell.receiver.to_string(),
+ time: tell.time,
+ message: tell.message.to_string(),
+ };
+
+ let receiver = tell.receiver.clone();
+ let tell_messages = self.entry(receiver)
+ .or_insert_with(|| Vec::with_capacity(3));
+ (*tell_messages).push(tell);
+
+ DbResponse::Success
+ }
+
+ fn get_tells(&self, receiver: &str) -> Option<Vec<TellMessage>> {
+ self.get(receiver).cloned()
+ }
+
+ fn delete_tells(&mut self, receiver: &str) -> DbResponse {
+ match self.remove(receiver) {
+ Some(_) => DbResponse::Success,
+ None => DbResponse::Failed("Tells not found"),
+ }
+ }
+}
+
+// Diesel automatically defines the tells module as public.
+// We create a schema module to keep it private.
+#[cfg(feature = "mysql")]
+mod schema {
+ table! {
+ tells (id) {
+ id -> Bigint,
+ sender -> Varchar,
+ receiver -> Varchar,
+ time -> Timestamp,
+ message -> Varchar,
+ }
+ }
+}
+
+#[cfg(feature = "mysql")]
+use self::schema::tells;
+
+#[cfg(feature = "mysql")]
+impl Database for Arc<Pool<ConnectionManager<MysqlConnection>>> {
+ fn insert_tell(&mut self, tell: &NewTellMessage) -> DbResponse {
+ use diesel;
+
+ let conn = &*self.get().expect("Failed to get connection");
+ match diesel::insert_into(tells::table).values(tell).execute(conn) {
+ Ok(_) => DbResponse::Success,
+ Err(e) => {
+ error!("DB failed to insert tell: {}", e);
+ DbResponse::Failed("Failed to save Tell")
+ }
+ }
+ }
+
+ fn get_tells(&self, receiver: &str) -> Option<Vec<TellMessage>> {
+ use self::tells::columns;
+
+ let conn = &*self.get().expect("Failed to get connection");
+ match tells::table
+ .filter(columns::receiver.eq(receiver))
+ .order(columns::time.asc())
+ .load::<TellMessage>(conn)
+ {
+ Ok(f) => Some(f),
+ Err(e) => {
+ error!("DB failed to get tells: {}", e);
+ None
+ }
+ }
+ }
+
+ fn delete_tells(&mut self, receiver: &str) -> DbResponse {
+ use diesel;
+ use self::tells::columns;
+
+ let conn = &*self.get().expect("Failed to get connection");
+ match diesel::delete(tells::table.filter(columns::receiver.eq(receiver))).execute(conn) {
+ Ok(_) => DbResponse::Success,
+ Err(e) => {
+ error!("DB failed to delete tells: {}", e);
+ DbResponse::Failed("Failed to delete tells")
+ }
+ }
+ }
+}
diff --git a/src/plugins/tell.rs b/src/plugins/tell/mod.rs
index 89d91f2..1a1bef1 100644
--- a/src/plugins/tell.rs
+++ b/src/plugins/tell/mod.rs
@@ -1,11 +1,15 @@
use irc::client::prelude::*;
use irc::error::IrcError;
-use std::collections::HashMap;
+use time;
+use chrono::NaiveDateTime;
use std::sync::Mutex;
use plugin::*;
+pub mod database;
+use self::database::{Database, DbResponse};
+
macro_rules! try_lock {
( $m:expr ) => {
match $m.lock() {
@@ -15,22 +19,15 @@ macro_rules! try_lock {
}
}
-#[derive(PluginName, Default, Debug)]
-pub struct Tell {
- tells: Mutex<HashMap<String, Vec<TellMessage>>>,
-}
-
-#[derive(Default, Debug)]
-struct TellMessage {
- sender: String,
- // TODO Add time
- message: String,
+#[derive(PluginName, Default)]
+pub struct Tell<T: Database> {
+ tells: Mutex<T>,
}
-impl Tell {
- pub fn new() -> Tell {
+impl<T: Database> Tell<T> {
+ pub fn new(db: T) -> Tell<T> {
Tell {
- tells: Mutex::new(HashMap::new()),
+ tells: Mutex::new(db),
}
}
@@ -54,24 +51,24 @@ impl Tell {
}
}
+ let tm = time::now().to_timespec();
let message = command.tokens[1..].join(" ");
- let tell = TellMessage {
- sender: sender,
- message: message,
+ let tell = database::NewTellMessage {
+ sender: &sender,
+ receiver: &receiver,
+ time: NaiveDateTime::from_timestamp(tm.sec, 0u32),
+ message: &message,
};
- let mut tells = try_lock!(self.tells);
- let tell_messages = tells
- .entry(receiver)
- .or_insert_with(|| Vec::with_capacity(3));
- (*tell_messages).push(tell);
-
- Ok("Got it!")
+ match try_lock!(self.tells).insert_tell(&tell) {
+ DbResponse::Success => Ok("Got it!"),
+ DbResponse::Failed(e) => Err(e.to_string()),
+ }
}
fn send_tell(&self, client: &IrcClient, receiver: &str) -> ExecutionStatus {
let mut tells = try_lock!(self.tells);
- if let Some(tell_messages) = tells.get_mut(receiver) {
+ if let Some(tell_messages) = tells.get_tells(receiver) {
for tell in tell_messages {
if let Err(e) = client.send_notice(
receiver,
@@ -85,7 +82,7 @@ impl Tell {
);
}
}
- tells.remove(receiver);
+ tells.delete_tells(receiver);
ExecutionStatus::Done
}
@@ -106,7 +103,7 @@ impl Tell {
}
}
-impl Plugin for Tell {
+impl<T: Database> Plugin for Tell<T> {
fn execute(&self, client: &IrcClient, message: &Message) -> ExecutionStatus {
match message.command {
Command::JOIN(_, _, _) | Command::PRIVMSG(_, _) => {
@@ -139,5 +136,9 @@ impl Plugin for Tell {
}
}
-#[cfg(test)]
-mod tests {}
+use std::fmt;
+impl<T: Database> fmt::Debug for Tell<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "Tell {{ ... }}")
+ }
+}