plenum-bot/src/email.rs

112 lines
3.7 KiB
Rust
Raw Normal View History

use crate::config_spec::{CfgField, CfgGroup};
use lettre::message::{header, SinglePart};
use lettre::transport::smtp::authentication::Credentials;
use lettre::{Message, SmtpTransport, Transport};
use std::error::Error;
use uuid::Uuid;
pub const CONFIG: CfgGroup<'static> = CfgGroup {
name: "email",
description: "Sending emails.",
fields: &[
CfgField::Default {
key: "server",
default: "mail.berlin.ccc.de",
description: "SMTP server used for sending emails.",
},
CfgField::Default {
key: "user",
default: "plenum-bot@berlin.ccc.de",
description: "User name used for authenticating with the mail server.",
},
CfgField::Password {
key: "password",
description: "Password for authenticating with the mail server.",
},
CfgField::Default {
key: "sender",
default: "Plenumsbot <plenum-bot@berlin.ccc.de>",
description: "Email address to use for \"From:\".",
},
CfgField::Default {
key: "to",
default: "CCCB Intern <intern@berlin.ccc.de>",
description: "Recipient of the emails sent.",
},
CfgField::Optional {
key: "last-message-id",
description:
"Message-Id of last initial announcement to send In-Reply-To (if applicable).",
},
],
};
#[derive(Clone)]
pub struct Email {
server: String,
credentials: Credentials,
message_id_suffix: String,
is_dry_run: bool,
}
pub struct SimpleEmail {
base: Email,
from: String,
to: String,
in_reply_to: Option<String>,
}
impl SimpleEmail {
pub fn new(base: Email, from: &str, to: &str, in_reply_to: Option<String>) -> Self {
Self { base: base.clone(), from: from.to_string(), to: to.to_string(), in_reply_to }
}
pub fn send_email(&self, subject: String, body: String) -> Result<String, Box<dyn Error>> {
self.base.send_email(
self.from.clone(),
self.to.clone(),
subject,
body,
self.in_reply_to.clone(),
)
}
}
impl Email {
pub fn new(server: &str, user: &str, pass: &str, is_dry_run: bool) -> Self {
let message_id_suffix = user.split('@').next_back().unwrap_or(&server).to_string();
let credentials = Credentials::new(user.to_string(), pass.to_string());
Self { server: server.to_string(), credentials, message_id_suffix, is_dry_run }
}
pub fn send_email(
&self, from: String, to: String, subject: String, body: String, in_reply_to: Option<String>,
) -> Result<String, Box<dyn Error>> {
let message_id = Uuid::new_v4().to_string() + &self.message_id_suffix;
let email = Message::builder()
.from(from.parse().unwrap())
.to(to.parse().unwrap())
.message_id(Some(message_id.clone()));
let email =
if in_reply_to.is_some() { email.in_reply_to(in_reply_to.unwrap()) } else { email }
.subject(subject)
.singlepart(
SinglePart::builder().header(header::ContentType::TEXT_PLAIN).body(body),
)
.unwrap();
if !self.is_dry_run {
let mailer = SmtpTransport::starttls_relay(&self.server)?
.credentials(self.credentials.clone())
.build();
mailer.send(&email)?;
Ok(message_id)
} else {
println!(
"[DRY RUN - NOT sending email]\n(raw message:)\n{}",
std::str::from_utf8(&email.formatted()).unwrap_or("((UTF-8 error))")
);
Ok("dummy-message-id@localhost".to_string())
}
}
}