fix notify
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
use super::auth;
|
use super::auth;
|
||||||
use crate::lexicons::app_bsky_notification;
|
use crate::lexicons::app_bsky_notification;
|
||||||
@@ -65,3 +66,90 @@ pub async fn update_seen() -> Result<()> {
|
|||||||
println!("{}", serde_json::to_string_pretty(&result)?);
|
println!("{}", serde_json::to_string_pretty(&result)?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Poll notifications and output new mentions as NDJSON.
|
||||||
|
/// Runs until interrupted (Ctrl+C).
|
||||||
|
pub async fn listen(interval_secs: u64, reasons: &[String]) -> Result<()> {
|
||||||
|
let mut last_seen: Option<String> = None;
|
||||||
|
let mut stdout = std::io::stdout();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let session = auth::refresh_session().await?;
|
||||||
|
let pds = session.pds.as_deref().unwrap_or("bsky.social");
|
||||||
|
let client = XrpcClient::new(pds);
|
||||||
|
|
||||||
|
let body: Value = client
|
||||||
|
.query_auth(
|
||||||
|
&app_bsky_notification::LIST_NOTIFICATIONS,
|
||||||
|
&[("limit", "50")],
|
||||||
|
&session.access_jwt,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if let Some(notifications) = body["notifications"].as_array() {
|
||||||
|
let mut new_items: Vec<&Value> = Vec::new();
|
||||||
|
|
||||||
|
for notif in notifications {
|
||||||
|
// Stop at already-seen notifications
|
||||||
|
if let Some(ref seen) = last_seen {
|
||||||
|
if notif["indexedAt"].as_str() == Some(seen.as_str()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip read notifications on first run
|
||||||
|
if last_seen.is_none() && notif["isRead"].as_bool() == Some(true) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter by reason
|
||||||
|
let reason = notif["reason"].as_str().unwrap_or("");
|
||||||
|
if !reasons.is_empty() && !reasons.iter().any(|r| r == reason) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_items.push(notif);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output in chronological order (oldest first)
|
||||||
|
for notif in new_items.iter().rev() {
|
||||||
|
let out = serde_json::json!({
|
||||||
|
"reason": notif["reason"],
|
||||||
|
"uri": notif["uri"],
|
||||||
|
"author": {
|
||||||
|
"did": notif["author"]["did"],
|
||||||
|
"handle": notif["author"]["handle"],
|
||||||
|
},
|
||||||
|
"text": notif["record"]["text"],
|
||||||
|
"indexedAt": notif["indexedAt"],
|
||||||
|
});
|
||||||
|
writeln!(stdout, "{}", serde_json::to_string(&out)?)?;
|
||||||
|
stdout.flush()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update last_seen to newest notification
|
||||||
|
if let Some(newest) = notifications.first() {
|
||||||
|
if let Some(ts) = newest["indexedAt"].as_str() {
|
||||||
|
last_seen = Some(ts.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark as seen
|
||||||
|
if !new_items.is_empty() {
|
||||||
|
let now = chrono::Utc::now()
|
||||||
|
.format("%Y-%m-%dT%H:%M:%S%.3fZ")
|
||||||
|
.to_string();
|
||||||
|
let seen_body = serde_json::json!({ "seenAt": now });
|
||||||
|
let _ = client
|
||||||
|
.call_no_response(
|
||||||
|
&app_bsky_notification::UPDATE_SEEN,
|
||||||
|
&seen_body,
|
||||||
|
&session.access_jwt,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tokio::time::sleep(std::time::Duration::from_secs(interval_secs)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
12
src/main.rs
12
src/main.rs
@@ -188,6 +188,15 @@ enum NotifyCommands {
|
|||||||
Count,
|
Count,
|
||||||
/// Mark all notifications as seen
|
/// Mark all notifications as seen
|
||||||
Seen,
|
Seen,
|
||||||
|
/// Poll for new notifications (runs continuously, NDJSON output)
|
||||||
|
Listen {
|
||||||
|
/// Poll interval in seconds
|
||||||
|
#[arg(short, long, default_value = "30")]
|
||||||
|
interval: u64,
|
||||||
|
/// Filter by reason (mention, reply, like, repost, follow, quote)
|
||||||
|
#[arg(short, long)]
|
||||||
|
reason: Vec<String>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
@@ -262,6 +271,9 @@ async fn main() -> Result<()> {
|
|||||||
NotifyCommands::Seen => {
|
NotifyCommands::Seen => {
|
||||||
commands::notify::update_seen().await?;
|
commands::notify::update_seen().await?;
|
||||||
}
|
}
|
||||||
|
NotifyCommands::Listen { interval, reason } => {
|
||||||
|
commands::notify::listen(interval, &reason).await?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Commands::Pds { command } => {
|
Commands::Pds { command } => {
|
||||||
|
|||||||
Reference in New Issue
Block a user