summaryrefslogtreecommitdiffstatshomepage
path: root/src/actions/ntfy.rs
blob: 7d83b873ed40afdda6fecef316e9a9339711b51e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use super::Action;
use crate::message::MessageConfig;
use crate::message::MessageFormat;
use crate::message::MessageParams;
use anyhow::Result;
use async_trait::async_trait;
use ntfy::Auth;
use ntfy::Dispatcher;
use ntfy::Payload;
use ntfy::Priority;
use ntfy::Url;
use serde::Deserialize;

#[derive(Deserialize, Debug)]
#[serde(remote = "Priority")]
#[serde(rename_all = "snake_case")]
pub enum NtfyPriority {
    Max = 5,
    High = 4,
    Default = 3,
    Low = 2,
    Min = 1,
}

#[derive(Deserialize, Debug)]
pub struct NtfyCredentials {
    username: String,
    password: String,
}

#[derive(Deserialize, Debug)]
pub struct NtfyConfig {
    url: Option<String>,
    proxy: Option<String>,
    topic: Option<String>,
    pub credentials: Option<NtfyCredentials>,
    #[serde(with = "NtfyPriority")]
    #[serde(default)]
    pub priority: Priority,
    pub tags: Option<Vec<String>>,
    pub attach: Option<Url>,
    pub filename: Option<String>,
    pub delay: Option<String>,
    pub email: Option<String>,
}

impl NtfyConfig {
    pub fn url(&self) -> &str {
        self.url.as_deref().unwrap_or("https://ntfy.sh")
    }

    pub fn topic(&self) -> &str {
        self.topic.as_deref().unwrap_or(env!("CARGO_PKG_NAME"))
    }
}

pub struct NtfyAction<'a> {
    message_config: &'a MessageConfig,
    dispatcher: Dispatcher,
    payload_template: Payload,
}

impl<'a> NtfyAction<'a> {
    pub fn new(message_config: &'a MessageConfig, ntfy_config: &'a NtfyConfig) -> Result<Self> {
        let mut dispatcher_builder = Dispatcher::builder(ntfy_config.url());
        if let Some(cred) = &ntfy_config.credentials {
            dispatcher_builder =
                dispatcher_builder.credentials(Auth::new(&cred.username, &cred.password));
        }
        if let Some(proxy) = &ntfy_config.proxy {
            dispatcher_builder = dispatcher_builder.proxy(proxy);
        }

        let mut payload = Payload::new(ntfy_config.topic())
            .markdown(match message_config.format() {
                MessageFormat::Plain => false,
                MessageFormat::Markdown => true,
                MessageFormat::Html => true,
            })
            .priority(ntfy_config.priority.clone())
            .tags(
                ntfy_config
                    .tags
                    .as_deref()
                    .unwrap_or(&["rotating_light".to_string()]),
            );
        if let Some(attach) = &ntfy_config.attach {
            payload = payload.attach(attach.clone());
        }
        if let Some(filename) = &ntfy_config.filename {
            payload = payload.filename(filename.clone());
        }
        if let Some(delay) = &ntfy_config.delay {
            payload = payload.delay(delay.parse()?);
        }
        if let Some(email) = &ntfy_config.email {
            payload = payload.email(email.clone());
        }
        Ok(Self {
            message_config,
            dispatcher: dispatcher_builder.build()?,
            payload_template: payload,
        })
    }
}

#[async_trait]
impl Action<'_> for NtfyAction<'_> {
    async fn run(&self, params: Option<&MessageParams<'_, '_>>) -> Result<()> {
        let payload = self
            .payload_template
            .clone()
            .title(self.message_config.subject(params)?)
            .message(self.message_config.body(params)?)
            .click(self.message_config.get_tx_url(params)?.parse()?);
        self.dispatcher.send(&payload).await?;
        Ok(())
    }
}