aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/tell/mod.rs
diff options
context:
space:
mode:
authorJokler <jokler.contact@gmail.com>2019-06-22 15:51:21 +0200
committerJokler <jokler.contact@gmail.com>2019-06-22 15:51:21 +0200
commit3592c7b6fb2522ff57c7f312b8927eb680d6dc5c (patch)
treed484a367c205afe43ba7327a888b06844fd24c0c /src/plugins/tell/mod.rs
parent237f6ebe59c90d4ceddd9af6a8a19e562d304aaa (diff)
parenta92e622a0d42911e8e46239c3bde17169ed60c92 (diff)
downloadfrippy-3592c7b6fb2522ff57c7f312b8927eb680d6dc5c.tar.gz
frippy-3592c7b6fb2522ff57c7f312b8927eb680d6dc5c.zip
Merge branch 'dev'HEADv0.5.0master
Diffstat (limited to 'src/plugins/tell/mod.rs')
-rw-r--r--src/plugins/tell/mod.rs222
1 files changed, 119 insertions, 103 deletions
diff --git a/src/plugins/tell/mod.rs b/src/plugins/tell/mod.rs
index bdfb55c..3c0dc3d 100644
--- a/src/plugins/tell/mod.rs
+++ b/src/plugins/tell/mod.rs
@@ -1,97 +1,116 @@
-use irc::client::prelude::*;
+use std::marker::PhantomData;
-use std::time::Duration;
-use std::sync::Mutex;
+use antidote::RwLock;
+use irc::client::data::User;
+use irc::client::prelude::*;
-use time;
use chrono::NaiveDateTime;
use humantime::format_duration;
+use std::time::Duration;
+use time;
use plugin::*;
+use FrippyClient;
-use failure::Fail;
-use failure::ResultExt;
+use self::error::*;
use error::ErrorKind as FrippyErrorKind;
use error::FrippyError;
-use self::error::*;
+use failure::Fail;
+use failure::ResultExt;
+use log::{debug, trace};
pub mod database;
use self::database::Database;
-macro_rules! try_lock {
- ( $m:expr ) => {
- match $m.lock() {
- Ok(guard) => guard,
- Err(poisoned) => poisoned.into_inner(),
- }
- }
-}
-
-#[derive(PluginName, Default)]
-pub struct Tell<T: Database> {
- tells: Mutex<T>,
+#[derive(PluginName)]
+pub struct Tell<T: Database, C> {
+ tells: RwLock<T>,
+ phantom: PhantomData<C>,
}
-impl<T: Database> Tell<T> {
- pub fn new(db: T) -> Tell<T> {
+impl<T: Database, C: FrippyClient> Tell<T, C> {
+ pub fn new(db: T) -> Self {
Tell {
- tells: Mutex::new(db),
+ tells: RwLock::new(db),
+ phantom: PhantomData,
}
}
- fn tell_command(
- &self,
- client: &IrcClient,
- command: PluginCommand,
- ) -> Result<String, TellError> {
+ fn tell_command(&self, client: &C, command: PluginCommand) -> Result<String, TellError> {
if command.tokens.len() < 2 {
- return Ok(self.invalid_command(client));
+ return Ok(self.invalid_command().to_owned());
}
- let receiver = &command.tokens[0];
+ let mut online = Vec::new();
+
+ let receivers = command.tokens[0].split(',').filter(|&s| !s.is_empty());
let sender = command.source;
- if receiver.eq_ignore_ascii_case(client.current_nickname()) {
- return Ok(String::from("I am right here!"));
- }
+ let mut no_receiver = true;
+ for receiver in receivers {
+ if receiver.eq_ignore_ascii_case(client.current_nickname())
+ || receiver.eq_ignore_ascii_case(&sender)
+ {
+ if !online.contains(&receiver) {
+ online.push(receiver);
+ }
+ continue;
+ }
- if receiver.eq_ignore_ascii_case(&sender) {
- return Ok(String::from("That's your name!"));
- }
+ let channels = client
+ .list_channels()
+ .expect("The irc crate should not be compiled with the \"nochanlists\" feature");
+
+ let find_receiver = |option: Option<Vec<User>>| {
+ option.and_then(|users| {
+ users
+ .into_iter()
+ .find(|user| user.get_nickname().eq_ignore_ascii_case(&receiver))
+ })
+ };
- if let Some(channels) = client.list_channels() {
- for channel in channels {
- if let Some(users) = client.list_users(&channel) {
- if users
- .iter()
- .any(|u| u.get_nickname().eq_ignore_ascii_case(&receiver))
- {
- return Ok(format!("{} is currently online.", receiver));
- }
+ if channels
+ .iter()
+ .map(|channel| client.list_users(&channel))
+ .map(find_receiver)
+ .any(|option| option.is_some())
+ {
+ if !online.contains(&receiver) {
+ // online.push(receiver);
}
+ // TODO Change this when https://github.com/aatxe/irc/issues/136 gets resolved
+ //continue;
}
- }
- let tm = time::now().to_timespec();
- let message = command.tokens[1..].join(" ");
- let tell = database::NewTellMessage {
- sender: &sender,
- receiver: &receiver.to_lowercase(),
- time: NaiveDateTime::from_timestamp(tm.sec, 0u32),
- message: &message,
- };
-
- try_lock!(self.tells).insert_tell(&tell)?;
+ let tm = time::now().to_timespec();
+ let message = command.tokens[1..].join(" ");
+ let tell = database::NewTellMessage {
+ sender: &sender,
+ receiver: &receiver.to_lowercase(),
+ time: NaiveDateTime::from_timestamp(tm.sec, 0u32),
+ message: &message,
+ };
+
+ debug!("Saving tell for {:?}", receiver);
+ self.tells.write().insert_tell(&tell)?;
+ no_receiver = false;
+ }
- Ok(String::from("Got it!"))
+ Ok(if no_receiver && online.is_empty() {
+ String::from("Invalid receiver.")
+ } else {
+ match online.len() {
+ 0 => format!("Got it!"),
+ 1 => format!("{} is currently online.", online[0]),
+ _ => format!("{} are currently online.", online.join(", ")),
+ }
+ })
}
- fn on_namelist(
- &self,
- client: &IrcClient,
- channel: &str,
- ) -> Result<(), FrippyError> {
- let receivers = try_lock!(self.tells)
+ fn on_namelist(&self, client: &C, channel: &str) -> Result<(), FrippyError> {
+ let receivers = self
+ .tells
+ .read()
.get_receivers()
.context(FrippyErrorKind::Tell)?;
@@ -111,12 +130,15 @@ impl<T: Database> Tell<T> {
Ok(())
}
}
- fn send_tells(&self, client: &IrcClient, receiver: &str) -> Result<(), FrippyError> {
+
+ fn send_tells(&self, client: &C, receiver: &str) -> Result<(), FrippyError> {
+ trace!("Checking {} for tells", receiver);
+
if client.current_nickname() == receiver {
return Ok(());
}
- let mut tells = try_lock!(self.tells);
+ let mut tells = self.tells.write();
let tell_messages = match tells.get_tells(&receiver.to_lowercase()) {
Ok(t) => t,
@@ -135,14 +157,13 @@ impl<T: Database> Tell<T> {
let dur = now - Duration::new(tell.time.timestamp() as u64, 0);
let human_dur = format_duration(dur);
+ let message = format!(
+ "Tell from {} {} ago: {}",
+ tell.sender, human_dur, tell.message
+ );
+
client
- .send_notice(
- receiver,
- &format!(
- "Tell from {} {} ago: {}",
- tell.sender, human_dur, tell.message
- ),
- )
+ .send_notice(receiver, &message)
.context(FrippyErrorKind::Connection)?;
debug!(
@@ -158,36 +179,30 @@ impl<T: Database> Tell<T> {
Ok(())
}
- fn invalid_command(&self, client: &IrcClient) -> String {
- format!(
- "Incorrect Command. \
- Send \"{} tell help\" for help.",
- client.current_nickname()
- )
+ fn invalid_command(&self) -> &str {
+ "Incorrect Command. \
+ Send \"tell help\" for help."
}
- fn help(&self, client: &IrcClient) -> String {
- format!(
- "usage: {} tell user message\r\n\
- example: {0} tell Foobar Hello!",
- client.current_nickname()
- )
+ fn help(&self) -> &str {
+ "Used to send messages to offline users which they will receive when they come online.\r\n
+ usage: tell user message\r\n\
+ example: tell Foobar Hello!"
}
}
-impl<T: Database> Plugin for Tell<T> {
- fn execute(&self, client: &IrcClient, message: &Message) -> ExecutionStatus {
+impl<T: Database, C: FrippyClient> Plugin for Tell<T, C> {
+ type Client = C;
+ fn execute(&self, client: &Self::Client, message: &Message) -> ExecutionStatus {
let res = match message.command {
Command::JOIN(_, _, _) => self.send_tells(client, message.source_nickname().unwrap()),
Command::NICK(ref nick) => self.send_tells(client, nick),
+ Command::PRIVMSG(_, _) => self.send_tells(client, message.source_nickname().unwrap()),
Command::Response(resp, ref chan_info, _) => {
if resp == Response::RPL_NAMREPLY {
debug!("NAMREPLY info: {:?}", chan_info);
- self.on_namelist(
- client,
- &chan_info[chan_info.len() - 1],
- )
+ self.on_namelist(client, &chan_info[chan_info.len() - 1])
} else {
Ok(())
}
@@ -201,43 +216,44 @@ impl<T: Database> Plugin for Tell<T> {
}
}
- fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), FrippyError> {
+ fn execute_threaded(&self, _: &Self::Client, _: &Message) -> Result<(), FrippyError> {
panic!("Tell should not use threading")
}
- fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), FrippyError> {
+ fn command(&self, client: &Self::Client, command: PluginCommand) -> Result<(), FrippyError> {
if command.tokens.is_empty() {
- return Ok(client
- .send_notice(&command.source, &self.invalid_command(client))
- .context(FrippyErrorKind::Connection)?);
+ client
+ .send_notice(&command.source, &self.invalid_command())
+ .context(FrippyErrorKind::Connection)?;
+ return Ok(());
}
let sender = command.source.to_owned();
- Ok(match command.tokens[0].as_ref() {
+ match command.tokens[0].as_ref() {
"help" => client
- .send_notice(&command.source, &self.help(client))
- .context(FrippyErrorKind::Connection)
- .into(),
+ .send_notice(&command.source, &self.help())
+ .context(FrippyErrorKind::Connection),
_ => match self.tell_command(client, command) {
Ok(msg) => client
.send_notice(&sender, &msg)
.context(FrippyErrorKind::Connection),
Err(e) => client
.send_notice(&sender, &e.to_string())
- .context(FrippyErrorKind::Connection)
- .into(),
+ .context(FrippyErrorKind::Connection),
},
- }?)
+ }?;
+
+ Ok(())
}
- fn evaluate(&self, _: &IrcClient, _: PluginCommand) -> Result<String, String> {
+ fn evaluate(&self, _: &Self::Client, _: PluginCommand) -> Result<String, String> {
Err(String::from("This Plugin does not implement any commands."))
}
}
use std::fmt;
-impl<T: Database> fmt::Debug for Tell<T> {
+impl<T: Database, C: FrippyClient> fmt::Debug for Tell<T, C> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Tell {{ ... }}")
}