From a6a95981260893762cc1931b4c638dbba9af7654 Mon Sep 17 00:00:00 2001 From: teldra Date: Mon, 26 Aug 2019 01:52:58 +0200 Subject: [PATCH] del notification, del systray, add i3blocks notify --- Cargo.toml | 3 - src/main.rs | 170 ++++++++++++++++------------------------------------ 2 files changed, 52 insertions(+), 121 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0380adf..1796653 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,11 +24,8 @@ debug=true [dependencies] imap = "1.0" native-tls = "0.2" -systray = "0.3.0" mailparse = "0.8" toml = "0.5" xdg = "2.1.0" -notify-rust = "3.4.0" rayon = "1.0.0" -askama_escape = "0.2" chrono = "0.4" diff --git a/src/main.rs b/src/main.rs index 1bdf897..063917d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,8 @@ -extern crate askama_escape; extern crate chrono; extern crate imap; extern crate mailparse; extern crate native_tls; -extern crate notify_rust; extern crate rayon; -extern crate systray; extern crate toml; extern crate xdg; @@ -21,6 +18,7 @@ use std::process::Command; use std::sync::mpsc; use std::thread; use std::time::Duration; +use std::io::Write; #[derive(Clone)] struct Account { @@ -34,9 +32,10 @@ impl Account { pub fn connect(&self) -> Result>, imap::error::Error> { let tls = TlsConnector::builder().build()?; imap::connect((&*self.server.0, self.server.1), &self.server.0, &tls).and_then(|c| { - let mut c = try!(c - .login(self.username.trim(), self.password.trim()) - .map_err(|(e, _)| e)); + let mut c = try!( + c.login(self.username.trim(), self.password.trim()) + .map_err(|(e, _)| e) + ); let cap = try!(c.capabilities()); if !cap.iter().any(|&c| c == "IDLE") { return Err(imap::error::Error::Bad(cap.iter().cloned().collect())); @@ -62,7 +61,6 @@ impl Connection { // the connection has failed for some reason // try to log out (we probably can't) eprintln!("connection to {} failed: {:?}", self.account.name, e); - self.socket.logout().is_err(); break; } } @@ -96,7 +94,6 @@ impl Connection { ) -> Result<(), imap::error::Error> { // Keep track of all the e-mails we have already notified about let mut last_notified = 0; - let mut notification = None::; loop { // check current state of inbox @@ -111,8 +108,7 @@ impl Connection { let mut subjects = BTreeMap::new(); if !uids.is_empty() { let uids: Vec<_> = uids.into_iter().map(|v: u32| format!("{}", v)).collect(); - for msg in self - .socket + for msg in self.socket .uid_fetch(&uids.join(","), "RFC822.HEADER")? .iter() { @@ -158,46 +154,6 @@ impl Connection { } } - if !subjects.is_empty() { - use notify_rust::{Notification, NotificationHint}; - let title = format!( - "@{} has new mail ({} unseen)", - self.account.name, num_unseen - ); - - // we want the n newest e-mail in reverse chronological order - let mut body = String::new(); - for subject in subjects.values().rev() { - body.push_str("> "); - body.push_str(subject); - body.push_str("\n"); - } - let body = body.trim_end(); - - println!("! {}", title); - println!("{}", body); - if let Some(mut n) = notification.take() { - n.summary(&title).body(&format!( - "{}", - askama_escape::escape(body, askama_escape::Html) - )); - n.update(); - } else { - notification = Some( - Notification::new() - .summary(&title) - .body(&format!( - "{}", - askama_escape::escape(body, askama_escape::Html) - )) - .icon("notification-message-email") - .hint(NotificationHint::Category("email.arrived".to_owned())) - .id(42) // for some reason, just updating isn't enough for dunst - .show() - .expect("failed to launch notify-send"), - ); - } - } tx.send((account, num_unseen)).unwrap(); @@ -216,7 +172,7 @@ fn main() { return; } }; - let config = match xdg.find_config_file("buzz.toml") { + let config = match xdg.find_config_file("buzz/buzz.toml") { Some(config) => config, None => { println!("Could not find configuration file buzz.toml"); @@ -247,39 +203,40 @@ fn main() { // Figure out what accounts we have to deal with let accounts: Vec<_> = match config.as_table() { - Some(t) => t - .iter() - .filter_map(|(name, v)| match v.as_table() { - None => { - println!("Configuration for account {} is broken: not a table", name); - None - } - Some(t) => { - let pwcmd = match t.get("pwcmd").and_then(|p| p.as_str()) { - None => return None, - Some(pwcmd) => pwcmd, - }; + Some(t) => { + t.iter() + .filter_map(|(name, v)| match v.as_table() { + None => { + println!("Configuration for account {} is broken: not a table", name); + None + } + Some(t) => { + let pwcmd = match t.get("pwcmd").and_then(|p| p.as_str()) { + None => return None, + Some(pwcmd) => pwcmd, + }; - let password = match Command::new("sh").arg("-c").arg(pwcmd).output() { - Ok(output) => String::from_utf8_lossy(&output.stdout).into_owned(), - Err(e) => { - println!("Failed to launch password command for {}: {}", name, e); - return None; - } - }; + let password = match Command::new("sh").arg("-c").arg(pwcmd).output() { + Ok(output) => String::from_utf8_lossy(&output.stdout).into_owned(), + Err(e) => { + println!("Failed to launch password command for {}: {}", name, e); + return None; + } + }; - Some(Account { - name: name.as_str().to_owned(), - server: ( - t["server"].as_str().unwrap().to_owned(), - t["port"].as_integer().unwrap() as u16, - ), - username: t["username"].as_str().unwrap().to_owned(), - password: password, - }) - } - }) - .collect(), + Some(Account { + name: name.as_str().to_owned(), + server: ( + t["server"].as_str().unwrap().to_owned(), + t["port"].as_integer().unwrap() as u16, + ), + username: t["username"].as_str().unwrap().to_owned(), + password: password, + }) + } + }) + .collect() + } None => { println!("Could not parse configuration file buzz.toml: not a table"); return; @@ -291,25 +248,6 @@ fn main() { return; } - // Create a new application - let mut app = match systray::Application::new() { - Ok(app) => app, - Err(e) => { - println!("Could not create gtk application: {}", e); - return; - } - }; - if let Err(e) = - app.set_icon_from_file(&"/usr/share/icons/Faenza/stock/24/stock_disconnect.png".to_string()) - { - println!("Could not set application icon: {}", e); - } - if let Err(e) = app.add_menu_item(&"Quit".to_string(), |window| { - window.quit(); - }) { - println!("Could not add application Quit menu option: {}", e); - } - // TODO: w.set_tooltip(&"Whatever".to_string()); // TODO: app.wait_for_message(); @@ -323,7 +261,9 @@ fn main() { Err(imap::error::Error::Io(e)) => { println!( "Failed to connect account {}: {}; retrying in {}s", - account.name, e, wait + account.name, + e, + wait ); thread::sleep(Duration::from_secs(wait)); } @@ -346,30 +286,24 @@ fn main() { } // We have now connected - app.set_icon_from_file(&"/usr/share/icons/Faenza/stock/24/stock_connect.png".to_string()) - .ok(); let (tx, rx) = mpsc::channel(); let mut unseen: Vec<_> = accounts.iter().map(|_| 0).collect(); for (i, conn) in accounts.into_iter().enumerate() { let tx = tx.clone(); - thread::spawn(move || { - conn.handle(i, tx); - }); + thread::spawn(move || { conn.handle(i, tx); }); } for (i, num_unseen) in rx { unseen[i] = num_unseen; - if unseen.iter().sum::() == 0 { - app.set_icon_from_file( - &"/usr/share/icons/oxygen/base/32x32/status/mail-unread.png".to_string(), - ) - .unwrap(); - } else { - app.set_icon_from_file( - &"/usr/share/icons/oxygen/base/32x32/status/mail-unread-new.png".to_string(), - ) - .unwrap(); - } + let mut file = std::fs::File::create("/tmp/mails").expect("create failed"); + file.write_all(num_unseen.to_string().as_bytes()).expect( + "write failed", + ); + Command::new("pkill") + .arg("-RTMIN+2") + .arg("i3blocks") + .spawn() + .expect("pkill command failed to start"); } }