diff options
| author | Jokler <jokler.contact@gmail.com> | 2018-03-12 16:02:51 +0100 |
|---|---|---|
| committer | Jokler <jokler.contact@gmail.com> | 2018-03-12 16:02:51 +0100 |
| commit | 909cabe9280722e43c5fb283f768051bb85e1890 (patch) | |
| tree | 506ac34b7e22cdb95568cef9e649ee64cb3b0fdb /src/plugins/url.rs | |
| parent | 15e855ddecfdac31ddda26b12fcfd1a142a0ec21 (diff) | |
| parent | 8e40e919aca8b8592be43e2c5bbcc0717bf14a6b (diff) | |
| download | frippy-909cabe9280722e43c5fb283f768051bb85e1890.tar.gz frippy-909cabe9280722e43c5fb283f768051bb85e1890.zip | |
Merge branch 'dev'
Diffstat (limited to 'src/plugins/url.rs')
| -rw-r--r-- | src/plugins/url.rs | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/src/plugins/url.rs b/src/plugins/url.rs new file mode 100644 index 0000000..bff840f --- /dev/null +++ b/src/plugins/url.rs @@ -0,0 +1,130 @@ +extern crate htmlescape; +extern crate regex; + +use irc::client::prelude::*; + +use self::regex::Regex; + +use plugin::*; +use utils; + +use self::error::*; +use error::FrippyError; +use error::ErrorKind as FrippyErrorKind; +use failure::Fail; +use failure::ResultExt; + +lazy_static! { + static ref RE: Regex = Regex::new(r"(^|\s)(https?://\S+)").unwrap(); +} + +#[derive(PluginName, Debug)] +pub struct Url { + max_kib: usize, +} + +impl Url { + /// If a file is larger than `max_kib` KiB the download is stopped + pub fn new(max_kib: usize) -> Url { + Url { max_kib: max_kib } + } + + fn grep_url(&self, msg: &str) -> Option<String> { + let captures = RE.captures(msg)?; + debug!("Url captures: {:?}", captures); + + Some(captures.get(2)?.as_str().to_owned()) + } + + fn get_title<'a>(&self, body: &str) -> Result<String, UrlError> { + let title = body.find("<title") + .map(|tag| { + body[tag..] + .find('>') + .map(|offset| tag + offset + 1) + .map(|start| { + body[start..] + .find("</title>") + .map(|offset| start + offset) + .map(|end| &body[start..end]) + }) + }) + .and_then(|s| s.and_then(|s| s)) + .ok_or(ErrorKind::MissingTitle)?; + + debug!("Title: {:?}", title); + + htmlescape::decode_html(title).map_err(|_| ErrorKind::HtmlDecoding.into()) + } + + fn url(&self, text: &str) -> Result<String, UrlError> { + let url = self.grep_url(text).ok_or(ErrorKind::MissingUrl)?; + let body = utils::download(&url, Some(self.max_kib)).context(ErrorKind::Download)?; + + let title = self.get_title(&body)?; + + Ok(title.replace('\n', "|").replace('\r', "|")) + } +} + +impl Plugin for Url { + fn execute(&self, _: &IrcClient, message: &Message) -> ExecutionStatus { + match message.command { + Command::PRIVMSG(_, ref msg) => if RE.is_match(msg) { + ExecutionStatus::RequiresThread + } else { + ExecutionStatus::Done + }, + _ => ExecutionStatus::Done, + } + } + + fn execute_threaded(&self, client: &IrcClient, message: &Message) -> Result<(), FrippyError> { + Ok(match message.command { + Command::PRIVMSG(_, ref content) => match self.url(content) { + Ok(title) => client + .send_privmsg(message.response_target().unwrap(), &title) + .context(FrippyErrorKind::Connection)?, + Err(e) => Err(e).context(FrippyErrorKind::Url)?, + }, + _ => (), + }) + } + + fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), FrippyError> { + Ok(client + .send_notice( + &command.source, + "This Plugin does not implement any commands.", + ) + .context(FrippyErrorKind::Connection)?) + } + + fn evaluate(&self, _: &IrcClient, command: PluginCommand) -> Result<String, String> { + self.url(&command.tokens[0]) + .map_err(|e| e.cause().unwrap().to_string()) + } +} + +pub mod error { + /// A URL plugin error + #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)] + #[error = "UrlError"] + pub enum ErrorKind { + /// A download error + #[fail(display = "A download error occured")] + Download, + + /// Missing URL error + #[fail(display = "No URL was found")] + MissingUrl, + + /// Missing title error + #[fail(display = "No title was found")] + MissingTitle, + + /// Html decoding error + #[fail(display = "Failed to decode Html characters")] + HtmlDecoding, + } +} |
