Added CTCP feature with automatic responses to a set of CTCP commands

as per #7.
This commit is contained in:
Aaron Weiss 2014-12-22 16:35:56 -05:00
parent 0967ece14a
commit 1b51c69a23
6 changed files with 81 additions and 2 deletions

View file

@ -12,9 +12,15 @@ readme = "README.md"
[features]
ctcp = ["time"]
encode = ["encoding"]
ssl = ["openssl"]
[dependencies.time]
time = "~0.1.3"
optional = true
[dependencies.encoding]
encoding = "~0.2.8"

View file

@ -24,7 +24,7 @@ fn main() {
if message.command[] == "PRIVMSG" {
if let Some(msg) = message.suffix {
if msg.contains("pickles") {
server.send_privmsg(message.args[0][], "Hi!").unwrap();
server.send_action(message.args[0][], "Hi!").unwrap();
}
}
}

View file

@ -35,6 +35,8 @@ pub struct Config {
pub encoding: Option<String>,
/// A list of channels to join on connection.
pub channels: Option<Vec<String>>,
/// The text that'll be sent in response to CTCP USERINFO requests.
pub user_info: Option<String>,
/// A map of additional options to be stored in config.
pub options: Option<HashMap<String, String>>,
}
@ -146,6 +148,13 @@ impl Config {
self.channels.as_ref().map(|v| v.iter().map(|s| s[]).collect()).unwrap_or(vec![])
}
/// Gets the string to be sent in response to CTCP USERINFO requests.
/// This defaults to an empty string when not specified.
#[experimental]
pub fn user_info(&self) -> &str {
self.user_info.as_ref().map(|s| s[]).unwrap_or("")
}
/// Looks up the specified string in the options map.
/// This uses indexing, and thus panics when the string is not present.
/// This will also panic if used and there are no options.

View file

@ -5,6 +5,7 @@
#![warn(missing_docs)]
#![feature(slicing_syntax)]
#[cfg(feature = "ctcp")] extern crate time;
#[cfg(feature = "encode")] extern crate encoding;
extern crate serialize;
#[cfg(feature = "ssl")] extern crate openssl;

View file

@ -5,8 +5,9 @@ use std::io::{BufferedReader, BufferedWriter, IoError, IoErrorKind, IoResult};
use std::sync::{Mutex, RWLock};
use conn::{Connection, NetStream};
use data::{Command, Config, Message, Response, User};
use data::Command::{JOIN, NICK, NICKSERV, PONG};
use data::Command::{JOIN, NICK, NICKSERV, NOTICE, PONG};
use data::kinds::{IrcReader, IrcWriter};
#[cfg(feature = "ctcp")] use time::now;
pub mod utils;
@ -170,8 +171,63 @@ impl<T: IrcReader, U: IrcWriter> IrcServer<T, U> {
vec[n].update_access_level(mode[]);
}
}
} else {
self.handle_ctcp(msg);
}
}
/// Handles CTCP requests if the CTCP feature is enabled.
#[experimental]
#[cfg(feature = "ctcp")]
fn handle_ctcp(&self, msg: &Message) {
let source = match msg.prefix {
Some(ref source) => source.find('!').map_or(source[], |i| source[..i]),
None => "",
};
if let ("PRIVMSG", [ref target]) = (msg.command[], msg.args[]) {
let resp = if target.starts_with("#") { target[] } else { source };
match msg.suffix {
Some(ref msg) if msg.starts_with("\u{001}") => {
let tokens: Vec<_> = {
let end = if msg.ends_with("\u{001}") {
msg.len() - 1
} else {
msg.len()
};
msg[1..end].split_str(" ").collect()
};
match tokens[0] {
"FINGER" => self.send_ctcp(resp, format!("FINGER :{} ({})",
self.config.real_name(),
self.config.username())[]),
"VERSION" => self.send_ctcp(resp, "VERSION irc v0.6.0 Rust"),
"SOURCE" => {
self.send_ctcp(resp, "SOURCE https://github.com/aatxe/irc");
self.send_ctcp(resp, "SOURCE");
},
"PING" => self.send_ctcp(resp, format!("PING {}", tokens[1])[]),
"TIME" => self.send_ctcp(resp, format!("TIME :{}", now().rfc822z())[]),
"USERINFO" => self.send_ctcp(resp, format!("USERINFO :{}",
self.config.user_info())[]),
_ => {}
}
},
_ => {}
}
}
}
/// Sends a CTCP-escaped message.
#[experimental]
#[cfg(feature = "ctcp")]
fn send_ctcp(&self, target: &str, msg: &str) {
self.send(NOTICE(target, format!("\u{001}{}\u{001}", msg)[])).unwrap();
}
/// Handles CTCP requests if the CTCP feature is enabled.
#[experimental]
#[cfg(not(feature = "ctcp"))]
fn handle_ctcp(&self, msg: &Message) {}
}
/// An Iterator over an IrcServer's incoming Messages.

View file

@ -164,6 +164,13 @@ impl<'a, T: IrcReader, U: IrcWriter> Wrapper<'a, T, U> {
msg
})))
}
/// Sends an action command to the specified target.
#[experimental]
#[cfg(feature = "ctcp")]
pub fn send_action(&self, target: &str, msg: &str) -> IoResult<()> {
self.send_privmsg(target, format!("\u{001}ACTION {}\u{001}", msg )[])
}
}
#[cfg(test)]