Send ntfy notifications

This commit is contained in:
Marc Plano-Lesay 2025-05-02 11:19:53 +10:00
parent 77a72329a8
commit bdcc0def42
Signed by: kernald
GPG key ID: 66A41B08CC62A6CF
10 changed files with 289 additions and 44 deletions

2
src/notifications/mod.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod notification;
pub mod ntfy;

View file

@ -0,0 +1,31 @@
use crate::actions::action::ProcessedMagnets;
use async_trait::async_trait;
use color_eyre::eyre::Result;
use std::collections::HashMap;
/// Trait for notification services
#[async_trait]
pub trait Notification {
/// Return the name of the notification service
fn name(&self) -> &str;
/// Send a notification about the processing results
async fn send_notification(
&self,
action_results: &HashMap<String, ProcessedMagnets>,
total_new_links: usize,
) -> Result<()>;
/// Determine if a notification should be sent based on the results
fn should_notify(
&self,
action_results: &HashMap<String, ProcessedMagnets>,
total_new_links: usize,
) -> bool {
// Don't send notification if everything was successful but 0 links were grabbed and processed
if total_new_links == 0 && action_results.values().all(|pm| pm.failed.is_empty()) {
return false;
}
true
}
}

View file

@ -0,0 +1,23 @@
use serde::{Deserialize, Serialize};
/// Configuration for the ntfy notification service
#[derive(Debug, Serialize, Deserialize)]
pub struct NtfyConfig {
/// Whether to enable the ntfy notification service
#[serde(default)]
pub enable: bool,
/// The host URL of the ntfy server
pub host: String,
/// The username for authentication (optional)
#[serde(default)]
pub username: Option<String>,
/// The password for authentication (optional)
#[serde(default)]
pub password: Option<String>,
/// The topic to publish notifications to
pub topic: String,
}

View file

@ -0,0 +1,5 @@
pub mod config;
pub mod notification;
pub use config::NtfyConfig;
pub use notification::NtfyNotification;

View file

@ -0,0 +1,68 @@
use crate::actions::action::ProcessedMagnets;
use crate::notifications::notification::Notification;
use crate::notifications::ntfy::config::NtfyConfig;
use crate::report;
use async_trait::async_trait;
use color_eyre::eyre::Result;
use log::{debug, info};
use ntfy::prelude::*;
use std::collections::HashMap;
/// Notification service using ntfy
pub struct NtfyNotification {
config: NtfyConfig,
client: Dispatcher<Async>,
}
impl NtfyNotification {
/// Create a new NtfyNotification instance
pub fn new(config: NtfyConfig) -> Result<Self> {
let mut builder = dispatcher::builder(config.host.clone());
// Add authentication if provided
if let (Some(username), Some(password)) = (&config.username, &config.password) {
builder = builder.credentials(Auth::credentials(username, password))
}
let client = builder.build_async()?;
Ok(NtfyNotification { config, client })
}
}
#[async_trait]
impl Notification for NtfyNotification {
fn name(&self) -> &str {
"Ntfy"
}
async fn send_notification(
&self,
action_results: &HashMap<String, ProcessedMagnets>,
total_new_links: usize,
) -> Result<()> {
if !self.should_notify(action_results, total_new_links) {
debug!("Skipping notification as there's nothing to report");
return Ok(());
}
let priority = if action_results.values().any(|pm| !pm.failed.is_empty()) {
Priority::High
} else {
Priority::Default
};
let payload = Payload::new(&self.config.topic)
.message(report::generate_report(
action_results,
total_new_links,
false,
))
.priority(priority);
self.client.send(&payload).await?;
info!("Sent notification to ntfy topic: {}", self.config.topic);
Ok(())
}
}