From 82e67a3c7910815f0fd350d2f6be521474e3790b Mon Sep 17 00:00:00 2001 From: vorboyvo Date: Sat, 28 Dec 2024 21:01:11 -0500 Subject: [PATCH] Everything more or less works now and needs minor tweaks. --- Cargo.lock | 21 +++++++++++++++---- Cargo.toml | 3 ++- src/file.rs | 21 ++++++++++++++++--- src/main.rs | 27 +++++++++++++++--------- src/mastodon.rs | 10 ++++----- src/server.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ src/wikipedia.rs | 16 +++++++------- 7 files changed, 121 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dcf159a..238defb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,6 +72,7 @@ checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" dependencies = [ "async-trait", "axum-core", + "axum-macros", "bytes", "futures-util", "http", @@ -119,6 +120,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "backtrace" version = "0.3.74" @@ -213,6 +225,7 @@ dependencies = [ "json", "regex", "reqwest", + "serde", "tokio", ] @@ -1146,18 +1159,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.216" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.216" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 49fafbe..ef77ea6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,10 +4,11 @@ version = "0.1.0" edition = "2021" [dependencies] -axum = "0.7.9" +axum = { version = "0.7.9", features = ["macros"] } chrono = "0.4.39" dotenv = "0.15.0" json = "0.12.4" regex = "1.11.1" reqwest = { version = "0.12.11", features = ['blocking', 'multipart'] } +serde = { version = "1.0.217", features = ["derive"] } tokio = { version = "1", features = ["full"] } diff --git a/src/file.rs b/src/file.rs index 7cac267..78f5aab 100644 --- a/src/file.rs +++ b/src/file.rs @@ -22,6 +22,16 @@ impl<'a> LinksFile<'a> { LinksFile(p) } + pub fn read_all_lines(&self) -> Result, FileError> { + let file_contents = read_to_string(self.0)?; + let file_lines = file_contents.split("\n"); + Ok( + file_lines + .map(|string| string.to_owned()) + .collect::>() + ) + } + pub fn remove_first_line(&self) -> Result { let file_contents = read_to_string(self.0)?; let mut file_lines = file_contents.split("\n"); @@ -31,14 +41,19 @@ impl<'a> LinksFile<'a> { Some(a) => a, None => panic!("Iterator returns None, something wrong happened") }; - let rest_of_file = file_lines.collect::>().join("\n"); + let rest_of_file = file_lines + .collect::>() + .join("\n"); write(self.0, rest_of_file)?; - Ok(first_line_clean.to_string()) + Ok(first_line_clean.to_owned()) } pub fn add_line_to_end(&self, line: String) -> Result<(), FileError> { let old_contents = read_to_string(self.0)?; - let new_contents = old_contents.trim().to_owned() + "\n" + &line; + let new_contents = old_contents + .trim() + .to_owned() + + "\n" + &line; write(self.0, new_contents)?; Ok(()) } diff --git a/src/main.rs b/src/main.rs index 417b9f4..a0150c1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use std::path::Path; +use tokio::{spawn}; mod file; mod wikipedia; @@ -32,16 +33,22 @@ async fn main() { let app_token = std::env::var("APP_TOKEN").expect("APP_TOKEN required."); let app_instance = std::env::var("APP_INSTANCE").expect("APP_INSTANCE required."); - let queue = LinksFile::new(Path::new("queue.txt")); - let backup = LinksFile::new(Path::new("backup.txt")); - let mastodon = Mastodon::new(app_instance, app_token); - let mut interval_timer = tokio::time::interval( - chrono::Duration::minutes(10).to_std().unwrap() - ); + let poster = spawn(async { + let queue = LinksFile::new(Path::new("queue.txt")); + let backup = LinksFile::new(Path::new("backup.txt")); + let mastodon = Mastodon::new(app_instance, app_token); - loop { - interval_timer.tick().await; - post_link_from_file(queue, backup, mastodon.clone()).await; - } + let mut interval_timer = tokio::time::interval( + chrono::Duration::minutes(10).to_std().unwrap() + ); + loop { + interval_timer.tick().await; + post_link_from_file(queue, backup, mastodon.clone()).await; + } + }); + + let server = spawn(server::serve()); + + let _ = poster.await; } diff --git a/src/mastodon.rs b/src/mastodon.rs index 6dc4042..54c70fa 100644 --- a/src/mastodon.rs +++ b/src/mastodon.rs @@ -21,12 +21,12 @@ pub enum Visibility { } impl Visibility { - pub fn enum_name(&self) -> String { + pub fn enum_name(&self) -> &'static str { match self { - Self::Public => "public".to_string(), - Self::Unlisted => "unlisted".to_string(), - Self::Private => "private".to_string(), - Self::Direct => "direct".to_string(), + Self::Public => "public", + Self::Unlisted => "unlisted", + Self::Private => "private", + Self::Direct => "direct", } } } diff --git a/src/server.rs b/src/server.rs index e69de29..3f0acda 100644 --- a/src/server.rs +++ b/src/server.rs @@ -0,0 +1,54 @@ +use std::path::Path; + +use axum::{ + routing::{get, post}, + extract::State, + http::StatusCode, + Json, Router +}; +use serde::{Serialize, Deserialize}; + +use crate::file::LinksFile; + +pub async fn serve<'a>() { + let app = Router::new() + .route("/addqueue", post(add_queue)) + .route("/addbackup", post(add_backup)); + + let listener = tokio::net::TcpListener::bind("0.0.0.0:3041").await.unwrap(); + axum::serve(listener, app).await.unwrap(); +} + +async fn add_queue(Json(payload): Json,) -> (StatusCode, Json) { + if payload.authorization != "t2Ye@6qgKkm$SQg5" { + return (StatusCode::FORBIDDEN, Json(AddPostResponse { url: "".to_owned(), position: 999 } )) + }; + let queue_file = LinksFile::new(Path::new("queue.txt")); + match queue_file.add_line_to_end(payload.url.clone()) { + Ok(()) => (StatusCode::CREATED, Json( AddPostResponse { url: payload.url, position: 0 })), + Err(_) => (StatusCode::INTERNAL_SERVER_ERROR, Json( AddPostResponse { url: "".to_owned(), position: 999 } )), + } +} + +async fn add_backup(Json(payload): Json,) -> (StatusCode, Json) { + if payload.authorization != "t2Ye@6qgKkm$SQg5" { + return (StatusCode::FORBIDDEN, Json(AddPostResponse { url: "".to_owned(), position: 999 } )) + }; + let backup_file = LinksFile::new(Path::new("backup.txt")); + match backup_file.add_line_to_end(payload.url.clone()) { + Ok(()) => (StatusCode::CREATED, Json( AddPostResponse { url: payload.url, position: 0 })), + Err(_) => (StatusCode::INTERNAL_SERVER_ERROR, Json( AddPostResponse { url: "".to_owned(), position: 999 } )), + } +} + +#[derive(Deserialize)] +struct AddPost { + url: String, + authorization: String, +} + +#[derive(Serialize)] +struct AddPostResponse { + url: String, + position: u32, +} diff --git a/src/wikipedia.rs b/src/wikipedia.rs index 14dd1aa..8317173 100644 --- a/src/wikipedia.rs +++ b/src/wikipedia.rs @@ -24,7 +24,7 @@ pub fn slug_from_link(link: String) -> Result { let regex_pattern = Regex::new(r"\.wikipedia\.org\/wiki\/|\?").unwrap(); let link_parts: Vec<&str> = regex_pattern.split(&link).collect(); if link_parts.len() >= 2 { - Ok(link_parts[1].to_string()) + Ok(link_parts[1].to_owned()) } else { Err(Error::IllegalLink(link)) } @@ -40,7 +40,7 @@ pub async fn title_from_slug(slug: String) -> Result { let json_body = response.text().await?; let body = json::parse(&json_body)?; match body["title"].as_str() { - Some(x) => Ok(x.to_string()), + Some(x) => Ok(x.to_owned()), None => Err(Error::ArticleNotFound) } } @@ -49,7 +49,7 @@ pub async fn post_body(link: String) -> Result { let title = title_from_slug(slug_from_link(link.clone())?).await?; Ok(format!("Today's wikipedia article is {title}\n\n\ {link}\n\n\ - #wikipedia").to_string()) + #wikipedia").to_owned()) } #[cfg(test)] @@ -58,27 +58,27 @@ mod tests { #[test] fn test_slug_from_link_1() { - assert_eq!(slug_from_link("https://en.wikipedia.org/wiki/Buck-a-beer?wprov=sfla1".to_string()).unwrap().as_str(), "Buck-a-beer") + assert_eq!(slug_from_link("https://en.wikipedia.org/wiki/Buck-a-beer?wprov=sfla1".to_owned()).unwrap().as_str(), "Buck-a-beer") } #[test] fn test_slug_from_link_2() { - assert_eq!(slug_from_link("https://en.wikipedia.org/wiki/GNU/Linux_naming_controversy".to_string()).unwrap().as_str(), "GNU/Linux_naming_controversy") + assert_eq!(slug_from_link("https://en.wikipedia.org/wiki/GNU/Linux_naming_controversy".to_owned()).unwrap().as_str(), "GNU/Linux_naming_controversy") } #[tokio::test] async fn test_title_from_slug_1() { - assert_eq!(title_from_slug("Buck-a-beer".to_string()).await.unwrap().as_str(), "Buck-a-beer") + assert_eq!(title_from_slug("Buck-a-beer".to_owned()).await.unwrap().as_str(), "Buck-a-beer") } #[tokio::test] async fn test_title_from_slug_2() { - assert_eq!(title_from_slug("GNU/Linux_naming_controversy".to_string()).await.unwrap().as_str(), "GNU/Linux naming controversy") + assert_eq!(title_from_slug("GNU/Linux_naming_controversy".to_owned()).await.unwrap().as_str(), "GNU/Linux naming controversy") } #[tokio::test] async fn test_post_body_1() { - let body = post_body("https://en.wikipedia.org/wiki/GNU%2FLinux_naming_controversy".to_string()).await.unwrap(); + let body = post_body("https://en.wikipedia.org/wiki/GNU%2FLinux_naming_controversy".to_owned()).await.unwrap(); let expected = "Today's wikipedia article is GNU/Linux naming controversy\n\n\ https://en.wikipedia.org/wiki/GNU%2FLinux_naming_controversy\n\n\ #wikipedia";