diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..4f17763 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "ghnotify" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +notify-rust = "4.8.0" +octocrab = "0.28.0" +open = "5.0.0" +reqwest = { version = "0.11.18", features = ["json"]} +serde_json = "1.0.103" +tokio = { version = "1.29.1", features = ["full"] } # TODO slim down features diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..42160b6 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,64 @@ +use octocrab::Octocrab; +use notify_rust::{Notification, Hint, CloseReason}; +use tokio::sync::mpsc; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required"); + + let octocrab = Octocrab::builder().personal_token(token).build()?; + + let notifications = octocrab + .activity() + .notifications() + .list() + .send() + .await?; + + let mut tasks = Vec::new(); + for notification in notifications { + // TODO on_close which doesn't block so I don't need to spawn all these workers!!! + tasks.push(tokio::spawn(async move { + // TODO on_close which can be FnOnce so I can move stuff into it and avoid channels!!! + let (tx, mut rx) = mpsc::unbounded_channel(); + Notification::new() + .summary(&format!("{} (GitHub)", notification.repository.name)) + .body(&format!("[{}] {}", notification.subject.r#type, notification.subject.title)) + .appname("github") + .icon("github") + .action("clicked", "click here") + .hint(Hint::Resident(true)) + .show_async().await.unwrap() + .on_close(|reason| match reason { + CloseReason::Dismissed => tx.send(true).unwrap(), + _ => tx.send(false).unwrap(), + }); + if rx.recv().await.unwrap() { + if let Some(url) = notification.subject.url { + // TODO this is awful! Is there no proper way to get html_url using octocrab????? + let client = reqwest::Client::builder() + .user_agent("holy-shit-fuck-you-github-why-are-you-401-me-if-i-dont-give-an-user-agent") + .build().unwrap(); + let response_raw = client.get(url) + .send() + .await.unwrap() + .text().await.unwrap(); + let response : serde_json::Value = serde_json::from_str(&response_raw).unwrap(); + let html_url = response + .get("html_url").unwrap(); + open::that(html_url.as_str().unwrap()).unwrap(); + } else if let Some(url) = notification.repository.html_url { + open::that(url.to_string()).unwrap(); + } else { + open::that(notification.url.to_string()).unwrap(); + }; + } + })); + } + + for t in tasks { + t.await?; + } + + Ok(()) +}