mirror of
https://git.nickiel.net/Nickiel/nicks_nextcloud_integrations.git
synced 2025-06-29 01:21:19 -07:00
added email capability
This commit is contained in:
parent
4b7241f5a4
commit
0a9a6838bf
4 changed files with 595 additions and 9 deletions
|
@ -9,6 +9,8 @@ edition = "2021"
|
|||
chrono = "0.4.26"
|
||||
chrono-tz = "0.8.3"
|
||||
clap = { version = "4.4.0", features = ["derive"] }
|
||||
csv = "1.2.2"
|
||||
lettre = { version = "0.10.4", features = ["sendmail-transport", "builder"] }
|
||||
log = "0.4.20"
|
||||
reqwest = { version = "0.11.18", features = ["blocking", "json"] }
|
||||
serde = { version = "1.0.184", features = ["serde_derive"] }
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
use chrono::{DateTime, Datelike, Utc};
|
||||
use chrono_tz::US::Central;
|
||||
use clap::Parser;
|
||||
use lettre::{
|
||||
message::{Message, header::ContentType, Attachment, MultiPart, SinglePart},
|
||||
SendmailTransport, Transport,
|
||||
};
|
||||
use log::{debug, error};
|
||||
use reqwest::blocking::Client;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -41,6 +45,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
};
|
||||
// --- END Loading and setup ---
|
||||
|
||||
let mut do_email_summary: bool = false;
|
||||
|
||||
// --- Get checked and unchecked ---
|
||||
let primary_note = Client::new()
|
||||
.get(format!(
|
||||
|
@ -59,6 +65,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
let mut checked: Vec<String> = vec![];
|
||||
|
||||
for line in primary_note_lines.iter() {
|
||||
if line.starts_with("- [x] Send Summary") {
|
||||
do_email_summary = true;
|
||||
continue;
|
||||
}
|
||||
if line.starts_with("- [ ] Send Summary") {
|
||||
continue;
|
||||
}
|
||||
// if line is a checkbox
|
||||
if line.starts_with("- [") {
|
||||
if line.starts_with("- [ ] ") {
|
||||
|
@ -140,13 +153,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
body_content.push(format!(
|
||||
"### {item} | {}h {}m | {}/{}/{}",
|
||||
elapsed_time.num_hours(),
|
||||
elapsed_time.num_minutes(),
|
||||
elapsed_time.num_minutes() - elapsed_time.num_hours() * 60, // yes, I'm lazy
|
||||
now.month(),
|
||||
now.day(),
|
||||
now.year()
|
||||
));
|
||||
} else if !line.is_empty() {
|
||||
body_content.push(line.to_string());
|
||||
body_content.push(line.to_string());
|
||||
}
|
||||
checked.retain(|val| val != &item);
|
||||
}
|
||||
|
@ -161,8 +174,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
})
|
||||
.collect();
|
||||
|
||||
debug!("checked: {:#?}", checked);
|
||||
debug!("unchecked: {:#?}", unchecked);
|
||||
debug!("Creating new logs for: {:#?}", checked);
|
||||
|
||||
body_content.splice(2..2, checked);
|
||||
|
||||
|
@ -181,6 +193,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
})?)
|
||||
.send()?;
|
||||
|
||||
if do_email_summary {
|
||||
debug!("Send email selected");
|
||||
reset_email_checkbox(&cfg);
|
||||
send_email_summary(&cfg, body_content);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -205,6 +223,7 @@ struct NoteUpdate {
|
|||
struct Config {
|
||||
user: String,
|
||||
pswd: String,
|
||||
email_addr: String,
|
||||
primary_note_id: String,
|
||||
logging_note_id: String,
|
||||
server_url: String,
|
||||
|
@ -220,3 +239,149 @@ struct CliArgs {
|
|||
#[arg(short, long)]
|
||||
debug: bool,
|
||||
}
|
||||
|
||||
// All unwraps are unrecoverable errors
|
||||
// All calls after depend on the success of the one before, and cannot run
|
||||
// without. So it would be too much work to make an error type to handle an error
|
||||
// that would cause a crash anyways
|
||||
fn reset_email_checkbox(config: &Config) {
|
||||
let primary_note = Client::new()
|
||||
.get(format!(
|
||||
"https://{}/index.php/apps/notes/api/v1/notes/{}",
|
||||
&config.server_url, &config.primary_note_id
|
||||
))
|
||||
.header("Accept", "application/json")
|
||||
.header("Content-Type", "application/json")
|
||||
.basic_auth(&config.user, Some(&config.pswd))
|
||||
.send()
|
||||
.unwrap()
|
||||
.json::<Note>()
|
||||
.unwrap();
|
||||
|
||||
let primary_note_lines: Vec<&str> = primary_note.content.lines().collect();
|
||||
|
||||
let mut body_content: Vec<&str> = vec![];
|
||||
|
||||
for line in primary_note_lines.iter() {
|
||||
if line.starts_with("- [x] Send Summary") {
|
||||
body_content.push("- [ ] Send Summary");
|
||||
} else {
|
||||
body_content.push(line);
|
||||
}
|
||||
}
|
||||
|
||||
Client::new()
|
||||
.put(format!(
|
||||
"https://{}/index.php/apps/notes/api/v1/notes/{}",
|
||||
&config.server_url, &config.primary_note_id
|
||||
))
|
||||
.header("Accept", "application/json")
|
||||
.header("Content-Type", "application/json")
|
||||
.basic_auth(&config.user, Some(&config.pswd))
|
||||
.body(
|
||||
serde_json::to_string(&NoteUpdate {
|
||||
content: body_content.join("\n"),
|
||||
})
|
||||
.unwrap(),
|
||||
)
|
||||
.send()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct SummaryRow {
|
||||
date: String,
|
||||
total_time: i64,
|
||||
task_name: String,
|
||||
}
|
||||
|
||||
// All unwraps are unrecoverable errors
|
||||
// All calls after depend on the success of the one before, and cannot run
|
||||
// without. So it would be too much work to make an error type to handle an error
|
||||
// that would cause a crash anyways
|
||||
fn send_email_summary(config: &Config, body_content: Vec<String>) {
|
||||
let mut wtr = csv::Writer::from_writer(vec![]);
|
||||
|
||||
for line in body_content {
|
||||
if line.starts_with("### ") {
|
||||
let mut split: Vec<&str> = line.split('|').collect();
|
||||
if split.len() != 3 {
|
||||
error!("There was an issue with this line: {line}");
|
||||
continue;
|
||||
}
|
||||
let date: String = match split.pop() {
|
||||
Some(val) => val,
|
||||
None => continue,
|
||||
}
|
||||
.to_string();
|
||||
let time: i64 = {
|
||||
// This should never error as the len should always be 3
|
||||
let data = match split.pop() {
|
||||
Some(val) => val,
|
||||
None => continue,
|
||||
}
|
||||
.trim();
|
||||
|
||||
// There should always be an h and an m in the second item
|
||||
let h_index = match data.chars().position(|c| c == 'h') {
|
||||
Some(val) => val,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let m_index = match data.chars().position(|c| c == 'm') {
|
||||
Some(val) => val,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
// this should always be the "10" in "10h"
|
||||
let hours = match data[0..h_index].parse::<i64>() {
|
||||
Ok(val) => val,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
// this should always be the "20" in "10h 20m"
|
||||
let minutes = match data[(h_index + 2)..m_index].parse::<i64>() {
|
||||
Ok(val) => val,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
hours * 60 + minutes
|
||||
};
|
||||
|
||||
let task_name: String = match split.pop() {
|
||||
Some(val) => val.trim(),
|
||||
None => continue,
|
||||
}
|
||||
.to_string()
|
||||
.replace("### ", "");
|
||||
|
||||
wtr.serialize(SummaryRow { date: date.trim().to_string(), total_time: time, task_name }).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
let data = String::from_utf8(wtr.into_inner().unwrap()).unwrap();
|
||||
|
||||
debug!("{:#?}", data);
|
||||
|
||||
let attachment = Attachment::new("TimeSummary.csv".to_string()).body(data, ContentType::parse("text/csv").unwrap());
|
||||
|
||||
let email = Message::builder()
|
||||
.from("NoReplay <noreply@nickiel.net>".parse().unwrap())
|
||||
.to("Nicholas Young <nicholasyoungsumner@gmail.com>".parse().unwrap())
|
||||
.subject("Testing email")
|
||||
.header(ContentType::TEXT_PLAIN)
|
||||
.multipart(
|
||||
MultiPart::mixed()
|
||||
.singlepart(SinglePart::plain("Hello World".to_string()))
|
||||
.singlepart(attachment)
|
||||
).unwrap();
|
||||
|
||||
|
||||
let mailer = SendmailTransport::new();
|
||||
|
||||
match mailer.send(&email) {
|
||||
Ok(val) => debug!("email sent: {:?}", val),
|
||||
Err(e) => debug!("Couldn't send email {}", e)
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue