feat: update email logic to subject not user +
This commit is contained in:
parent
ede986080a
commit
6a652ed4f3
5 changed files with 136 additions and 27 deletions
|
|
@ -7,6 +7,7 @@ pub fn send_notification(
|
|||
question: &str,
|
||||
notify_email: &str,
|
||||
mail_domain: &str,
|
||||
reply_domain: &str,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let truncated = if question.len() > 50 {
|
||||
format!("{}...", &question[..50])
|
||||
|
|
@ -15,14 +16,14 @@ pub fn send_notification(
|
|||
};
|
||||
|
||||
let from: Mailbox = format!("Q&A <qa@{mail_domain}>").parse()?;
|
||||
let reply_to: Mailbox = format!("qa+{id}@{mail_domain}").parse()?;
|
||||
let reply_to: Mailbox = format!("qa@{reply_domain}").parse()?;
|
||||
let to: Mailbox = notify_email.parse()?;
|
||||
|
||||
let email = Message::builder()
|
||||
.from(from)
|
||||
.reply_to(reply_to)
|
||||
.to(to)
|
||||
.subject(format!("Q&A #{id}: {truncated}"))
|
||||
.subject(format!("{id} - {truncated}"))
|
||||
.body(question.to_string())?;
|
||||
|
||||
let mailer = SmtpTransport::builder_dangerous("localhost")
|
||||
|
|
@ -36,6 +37,44 @@ pub fn send_notification(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn extract_id_from_subject(subject: &str) -> Result<i64, Box<dyn std::error::Error>> {
|
||||
let subject = subject.trim();
|
||||
let start = subject
|
||||
.char_indices()
|
||||
.find_map(|(idx, ch)| ch.is_ascii_digit().then_some(idx))
|
||||
.ok_or("Subject missing question id")?;
|
||||
let digits: String = subject[start..]
|
||||
.chars()
|
||||
.take_while(|c| c.is_ascii_digit())
|
||||
.collect();
|
||||
|
||||
if digits.is_empty() {
|
||||
return Err("Subject missing numeric question id".into());
|
||||
}
|
||||
|
||||
let remainder = subject[start + digits.len()..].trim_start();
|
||||
if !(remainder.starts_with('-') || remainder.starts_with(':')) {
|
||||
return Err("Subject missing separator after question id".into());
|
||||
}
|
||||
|
||||
Ok(digits.parse()?)
|
||||
}
|
||||
|
||||
pub fn extract_plain_text_body(contents: &str) -> String {
|
||||
let normalized = contents.replace("\r\n", "\n");
|
||||
let body = if let Some((headers, body)) = normalized.split_once("\n\n") {
|
||||
if headers.lines().any(|line| line.contains(':')) {
|
||||
body
|
||||
} else {
|
||||
normalized.as_str()
|
||||
}
|
||||
} else {
|
||||
normalized.as_str()
|
||||
};
|
||||
|
||||
strip_quoted_text(body)
|
||||
}
|
||||
|
||||
pub fn strip_quoted_text(body: &str) -> String {
|
||||
let mut result = Vec::new();
|
||||
for line in body.lines() {
|
||||
|
|
@ -50,16 +89,18 @@ pub fn strip_quoted_text(body: &str) -> String {
|
|||
result.join("\n").trim().to_string()
|
||||
}
|
||||
|
||||
pub fn extract_id_from_address(to: &str) -> Result<i64, Box<dyn std::error::Error>> {
|
||||
let addr = to.trim();
|
||||
let addr = if let Some(start) = addr.find('<') {
|
||||
&addr[start + 1..addr.find('>').unwrap_or(addr.len())]
|
||||
} else {
|
||||
addr
|
||||
};
|
||||
let local = addr.split('@').next().unwrap_or("");
|
||||
let id_str = local
|
||||
.strip_prefix("qa+")
|
||||
.ok_or("No qa+ prefix in address")?;
|
||||
Ok(id_str.parse()?)
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{extract_id_from_subject, extract_plain_text_body};
|
||||
|
||||
#[test]
|
||||
fn extracts_id_from_subject() {
|
||||
assert_eq!(extract_id_from_subject("Re: 42 - Hello").unwrap(), 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extracts_plain_text_from_raw_email() {
|
||||
let raw = "Subject: Q&A #42: Hello\r\nFrom: Jet <jet@example.com>\r\n\r\nThis is the answer.\r\n\r\nOn earlier mail wrote:\r\n> quoted";
|
||||
assert_eq!(extract_plain_text_body(raw), "This is the answer.");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue