Merge branch 'read-email-and-post-to-hook' into 'main'
Read email and post to hook See merge request c4181/email2matrix-message-service!1
This commit is contained in:
commit
1a51313f21
4 changed files with 1716 additions and 0 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -9,4 +9,8 @@ target/
|
|||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
|
||||
# Rust rover
|
||||
.idea
|
||||
*.iml
|
||||
|
||||
config.toml
|
||||
1548
Cargo.lock
generated
Normal file
1548
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "email2matrix-message-service"
|
||||
version = "1.0.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
confy = "0.6.1"
|
||||
imap = "2.4.1"
|
||||
serde = { version = "1.0.201", features = ["derive"] }
|
||||
serde_json = "1.0.117"
|
||||
reqwest = { version = "0.12.4", features = ["json", "blocking"] }
|
||||
151
src/main.rs
Normal file
151
src/main.rs
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
use std::{env, fmt};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Display;
|
||||
use std::net::TcpStream;
|
||||
use std::path::Path;
|
||||
|
||||
use reqwest::blocking::Response;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let cfg: AppConfig = confy::load_path(Path::new(&args[1])).expect("Couldn't read config");
|
||||
|
||||
let mut imap_session = get_imap_session(&cfg);
|
||||
|
||||
let message = get_emails(&mut imap_session);
|
||||
|
||||
match message {
|
||||
Some(x) => {
|
||||
let mut messages_sent_successfully = Vec::new();
|
||||
for email in x {
|
||||
let response = post_to_hookshot(&cfg.hookshot_url, &email.1);
|
||||
match response {
|
||||
Ok(x) => {
|
||||
if x.status().is_success() {
|
||||
messages_sent_successfully.push(email.0);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
delete_emails(messages_sent_successfully, &mut imap_session);
|
||||
},
|
||||
None => println!("No emails")
|
||||
}
|
||||
|
||||
imap_session.logout().unwrap();
|
||||
}
|
||||
|
||||
fn get_imap_session(config: &AppConfig) -> imap::Session<TcpStream> {
|
||||
let stream = TcpStream::connect(&config.imap_ip).unwrap();
|
||||
let client = imap::Client::new(stream);
|
||||
let mut imap_session = client.login(&config.imap_user, &config.imap_password).expect("Couldn't login");
|
||||
imap_session.select("INBOX").unwrap();
|
||||
|
||||
return imap_session;
|
||||
}
|
||||
|
||||
fn get_emails(imap_session: &mut imap::Session<TcpStream>) -> Option<HashMap<u32, Email>> {
|
||||
let ordinals = imap_session.search("ALL")?;
|
||||
if ordinals.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let messages = imap_session.fetch("1:".to_string() + &*ordinals.iter().max().unwrap().to_string(), "(BODY[TEXT] ENVELOPE)")?;
|
||||
let mut emails: HashMap<u32, Email> = HashMap::new();
|
||||
if messages.is_empty() {
|
||||
return None;
|
||||
}
|
||||
for message in messages.iter() {
|
||||
let body = message.text().unwrap_or_default();
|
||||
let body = std::str::from_utf8(body).unwrap_or_default().to_string();
|
||||
|
||||
let envelope = message.envelope().unwrap();
|
||||
|
||||
let subject = envelope.subject.unwrap_or("No Subject".as_bytes());
|
||||
let subject = std::str::from_utf8(subject).unwrap().to_string();
|
||||
|
||||
let from = envelope.from.as_slice();
|
||||
let from_host = from.get(0).unwrap().get(0).unwrap().host.unwrap_or_default();
|
||||
let from_mailbox = from.get(0).unwrap().get(0).unwrap().mailbox.unwrap_or_default();
|
||||
let from = std::str::from_utf8(from_mailbox).unwrap().to_string() + "@" + &*std::str::from_utf8(from_host).unwrap().to_string();
|
||||
|
||||
let email = Email {
|
||||
body,
|
||||
subject,
|
||||
from
|
||||
};
|
||||
|
||||
emails.insert(message.message, email);
|
||||
}
|
||||
|
||||
Some(emails)
|
||||
}
|
||||
|
||||
fn delete_emails(ordinals: Vec<u32>, imap_session: &mut imap::Session<TcpStream>) {
|
||||
for ordinal in ordinals {
|
||||
imap_session.store(format!("{}", ordinal), "+FLAGS (\\Deleted)")
|
||||
.expect("Couldn't delete message");
|
||||
}
|
||||
|
||||
imap_session.expunge().unwrap();
|
||||
}
|
||||
|
||||
fn post_to_hookshot(hookshot_url: &str, email: &Email) -> reqwest::Result<Response> {
|
||||
let request: HookshotRequest = match email.subject.contains("PowerPanel") {
|
||||
true => {
|
||||
HookshotRequest {
|
||||
username: email.from.to_string(),
|
||||
text: format!("{}", email.subject)
|
||||
}
|
||||
}
|
||||
false => {
|
||||
HookshotRequest {
|
||||
username: email.from.to_string(),
|
||||
text: format!("{}\n\n{}", email.subject, email.body)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let response = reqwest::blocking::Client::new()
|
||||
.post(hookshot_url)
|
||||
.json(&request)
|
||||
.send();
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct AppConfig {
|
||||
imap_ip: String,
|
||||
imap_user: String,
|
||||
imap_password: String,
|
||||
hookshot_url: String
|
||||
}
|
||||
|
||||
impl Default for AppConfig {
|
||||
fn default() -> Self {
|
||||
panic!("Could not find config file")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Email {
|
||||
body: String,
|
||||
from: String,
|
||||
subject: String
|
||||
}
|
||||
|
||||
impl Display for Email {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{} : {}\n {}", self.from, self.subject, self.body)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct HookshotRequest {
|
||||
username: String,
|
||||
text: String
|
||||
}
|
||||
Loading…
Reference in a new issue