hi_push/email/
mod.rs

1use std::time::Duration;
2
3use async_trait::async_trait;
4use lettre::{
5    transport::smtp::authentication::{Credentials, Mechanism},
6    SmtpTransport, Transport,
7};
8
9pub struct Message<'a> {
10    pub title: &'a str,
11    pub body: &'a str,
12    pub to: &'a [&'a str],
13}
14
15#[derive(Debug, PartialEq)]
16pub struct PushResult<'a> {
17    pub email: &'a str,
18    pub success: bool,
19    pub reason: Option<String>,
20}
21
22#[derive(Debug, PartialEq)]
23pub struct Response<'a> {
24    pub results: Vec<PushResult<'a>>,
25}
26
27pub struct Client {
28    client_id: String,
29    cli: SmtpTransport,
30}
31
32impl Client {
33    pub async fn new(client_id: &str, client_secret: &str, push_url: &str) -> Client {
34        let creds = Credentials::new(client_id.to_string(), client_secret.to_string());
35        let mailer = SmtpTransport::starttls_relay(push_url)
36            .unwrap()
37            .authentication(vec![Mechanism::Login])
38            .port(587)
39            .credentials(creds)
40            .timeout(Duration::from_secs(10).into())
41            .pool_config(Default::default())
42            .build();
43
44        Self {
45            client_id: client_id.to_string(),
46            cli: mailer,
47        }
48    }
49}
50
51#[async_trait]
52impl<'b> super::Pusher<'b, Message<'b>, Response<'b>> for Client {
53    async fn push(&self, msg: &'b Message) -> Result<Response<'b>, super::Error> {
54        let mut results = Vec::new();
55
56        for to in msg.to {
57            let to_mail = match format!("<{to}>").parse::<lettre::message::Mailbox>() {
58                Ok(o) => o,
59                Err(e) => {
60                    results.push(PushResult {
61                        email: to,
62                        success: false,
63                        reason: Some(e.to_string()),
64                    });
65                    continue;
66                }
67            };
68
69            let msg = match lettre::Message::builder()
70                .from(format!("<{}>", self.client_id).parse().unwrap())
71                .to(to_mail)
72                .body(msg.body.to_string())
73            {
74                Ok(o) => o,
75                Err(e) => {
76                    results.push(PushResult {
77                        email: to,
78                        success: false,
79                        reason: Some(e.to_string()),
80                    });
81                    continue;
82                }
83            };
84
85            match self.cli.send(&msg) {
86                // @todo handle _resp
87                Ok(resp) => {
88                    if resp.is_positive() {
89                        results.push(PushResult {
90                            email: to,
91                            success: true,
92                            reason: None,
93                        });
94                    } else {
95                        results.push(PushResult {
96                            email: to,
97                            success: false,
98                            reason: Some(resp.code().to_string()),
99                        });
100                    }
101                }
102                Err(e) => results.push(PushResult {
103                    email: to,
104                    success: false,
105                    reason: Some(e.to_string()),
106                }),
107            };
108        }
109
110        Ok(Response { results })
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use crate::Pusher;
117
118    #[tokio::test]
119    async fn test_push() {
120        let client_id = std::env::var("EMAIL_CLIENT_ID").unwrap();
121        let client_secret = std::env::var("EMAIL_CLIENT_SECRET").unwrap();
122
123        let cli = super::Client::new(&client_id, &client_secret, "smtp.office365.com").await;
124        let to = vec!["public@hikit.io", "1234"];
125        let msg = super::Message {
126            title: "Hello Email",
127            body: "This is test.",
128            to: to.as_slice(),
129        };
130        let res = cli.retry_push(&msg).await;
131        println!("{:?}", res);
132    }
133}