diff --git a/Cargo.lock b/Cargo.lock index 1b9d19a..f7cfe6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,7 +68,7 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -78,7 +78,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -304,6 +304,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "eyre" version = "0.6.12" @@ -314,6 +324,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + [[package]] name = "fnv" version = "1.0.7" @@ -594,6 +610,12 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "lock_api" version = "0.4.12" @@ -656,7 +678,7 @@ dependencies = [ "hermit-abi", "libc", "wasi", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -808,6 +830,19 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "ryu" version = "1.0.18" @@ -889,6 +924,7 @@ dependencies = [ "jsonrpsee", "serde", "serde_json", + "tempfile", "thiserror", "tokio", "tokio-stream", @@ -917,7 +953,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -937,6 +973,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "thiserror" version = "1.0.63" @@ -982,7 +1031,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1202,6 +1251,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index 53ed57c..171d03b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ futures = "0.3.30" jsonrpsee = { version = "0.24.3", features = ["macros", "async-client"] } serde = { version = "1.0.209", features = ["derive"] } serde_json = "1.0.127" +tempfile = "3.12.0" thiserror = "1.0.63" tokio = { version = "1.40.0", features = ["full"] } tokio-stream = "0.1.15" diff --git a/src/main.rs b/src/main.rs index 6559201..a83b704 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,18 +1,21 @@ -use std::fs::{create_dir_all, write, File, OpenOptions}; +use std::fs::{create_dir_all, remove_file, rename, write, File, OpenOptions}; use std::io::{BufRead, BufReader, Write}; use std::path::{Path, PathBuf}; use std::process::Stdio; +use std::time::Duration; use askama::Template; use base64::prelude::BASE64_STANDARD; use base64::Engine; -use chrono::{DateTime, Local, Timelike}; +use chrono::{DateTime, Local}; use clap::{command, Parser}; -use color_eyre::eyre::Result; +use color_eyre::eyre::{eyre, Result}; use futures::future::join_all; use jsonrpsee::async_client::{Client, ClientBuilder}; use serde_json::Value; +use tempfile::tempdir; use tokio::process::Command; +use tokio::task::yield_now; use tokio_util::codec::{FramedRead, LinesCodec}; use crate::jsonrpc::RpcClient; @@ -37,9 +40,9 @@ struct Config { #[arg(short, long)] output_folder: String, - ///The time of day when generate the post + ///The used as the journal #[arg(short, long)] - time: String, + url: String, } #[derive(Debug, Default, Template)] @@ -57,14 +60,12 @@ struct Message { time: String, } -async fn generate_post(config: &Config, client: &mut Client) -> Result<()> { +async fn generate_post(config: &Config, client: &mut Client) -> Result { let day: DateTime = Local::now(); - let _hour = day.hour(); - let folder: PathBuf = [config.output_folder.clone(), day.to_rfc3339()] - .iter() - .collect(); - if !folder.exists() { - create_dir_all(&folder)?; + let file = PathBuf::from(format!("{}/texts.json", config.data_folder)); + let tempdir = tempdir()?; + if !file.exists() { + return Err(eyre!("No content to post")); } let mut template = PostTemplate { date: day.to_rfc3339(), @@ -72,29 +73,44 @@ async fn generate_post(config: &Config, client: &mut Client) -> Result<()> { ..Default::default() }; let mut msgs = Vec::new(); - let file = File::open(format!("{}/texts.json", config.data_folder))?; - let reader = BufReader::new(file); - for line in reader.lines() { - let v = serde_json::from_str(&line?)?; - let mut attachments = Vec::new(); - if let Some(i) = get_msg_attachments(client, &v).await { - for (id, content) in i { - let path = folder.join(id); - save_picture(&path, &content)?; - attachments.push(id.to_string()); + { + let file = File::open(file.clone())?; + let reader = BufReader::new(file.try_clone()?); + for line in reader.lines() { + let v = serde_json::from_str(&line?)?; + let mut attachments = Vec::new(); + if let Some(i) = get_msg_attachments(client, &v).await { + for (id, content) in i { + let path = tempdir.path().join(id); + save_picture(&path, &content)?; + attachments.push(id.to_string()); + } } + + msgs.push(Message { + content: get_msg_text(&v).unwrap_or("").to_string(), + attachments, + time: get_msg_time(&v).unwrap(), + }); } + if msgs.is_empty() { + return Err(eyre!("No content to post")); + }; + template.messages = msgs; - msgs.push(Message { - content: get_msg_text(&v).unwrap_or("").to_string(), - attachments, - time: get_msg_time(&v).unwrap(), - }); + println!("{}", template.render()?); + write(tempdir.path().join("index.md"), template.render()?)?; } - template.messages = msgs; - - println!("{}", template.render()?); - Ok(()) + let folder: PathBuf = [config.output_folder.clone(), day.to_rfc3339()] + .iter() + .collect(); + if folder.exists() { + return Err(eyre!("Blog folder already exists")); + } + rename(tempdir, folder)?; + // Delete messages after we're finished with writing + remove_file(file)?; + Ok(day.to_rfc3339()) } #[tokio::main] @@ -120,17 +136,18 @@ async fn main() -> Result<()> { let mut stream = client.subscribe_receive(None).await?; loop { let v = stream.next().await.unwrap()?; - let sender = get_msg_sender(&v); + // which message doesn't have a sender? + let sender = get_msg_sender(&v).expect("which message doesn't have a sender?"); let text = get_msg_text(&v); let at = get_msg_attachments(&mut client, &v).await; if text.is_none() && at.is_none() { continue; - } else if sender != Some(&config.allowed_sender) { + } else if sender != &config.allowed_sender { println!("{:?}", sender); - if let Some(x) = sender { - client.send( + client + .send( None, - vec![x.to_string()], + vec![sender.to_string()], Vec::new(), false, false, @@ -153,33 +170,116 @@ async fn main() -> Result<()> { None, None, None, - ).await?; - } + ) + .await?; continue; }; println!("{v}"); println!("{:?}", text); - if text == Some("post") { - generate_post(&config, &mut client).await?; - } else { - let mut file = OpenOptions::new() - .append(true) - .create(true) - .open(format!("{}/texts.json", config.data_folder))?; - writeln!(file, "{}", v.to_string())?; + if let Some(text) = text { + // ©ontrol character + if let Some(text) = text.strip_prefix("©").and_then(|x| Some(x.trim())) { + match text { + "post" => { + let res = generate_post(&config, &mut client).await; + if let Ok(res) = res { + client + .send( + None, + vec![sender.to_string()], + Vec::new(), + false, + false, + format!("Generated blog post at: {}/{}", config.url, res) + .to_string(), + Vec::new(), + Vec::new(), + Vec::new(), + None, + None, + None, + Vec::new(), + Vec::new(), + Vec::new(), + None, + None, + None, + None, + None, + None, + None, + None, + ) + .await?; + } else { + client + .send( + None, + vec![sender.to_string()], + Vec::new(), + false, + false, + format!("Error generating blog post:\n{:?}", res).to_string(), + Vec::new(), + Vec::new(), + Vec::new(), + None, + None, + None, + Vec::new(), + Vec::new(), + Vec::new(), + None, + None, + None, + None, + None, + None, + None, + None, + ) + .await?; + } + } + _ => { + client + .send( + None, + vec![sender.to_string()], + Vec::new(), + false, + false, + format!("Unknown command: {}", text).to_string(), + Vec::new(), + Vec::new(), + Vec::new(), + None, + None, + None, + Vec::new(), + Vec::new(), + Vec::new(), + None, + None, + None, + None, + None, + None, + None, + None, + ) + .await?; + } + } + continue; + } } - - //let at = get_msg_attachments(&mut client, &v).await; - //if let Some(i) = at { - // for (id, content) in i { - // let path = format!("{}/{}", &config.data_folder, id); - // save_picture(&path, &content)?; - // } - //} + let mut file = OpenOptions::new() + .append(true) + .create(true) + .open(format!("{}/texts.json", config.data_folder))?; + writeln!(file, "{}", v.to_string())?; } - //stream.unsubscribe().await?; - - //Ok(()) } /// make sure the directory exists before trying to save into it