Everything more or less works now and needs minor tweaks.
This commit is contained in:
parent
7a12fc4490
commit
82e67a3c79
21
Cargo.lock
generated
21
Cargo.lock
generated
|
|
@ -72,6 +72,7 @@ checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum-core",
|
"axum-core",
|
||||||
|
"axum-macros",
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http",
|
"http",
|
||||||
|
|
@ -119,6 +120,17 @@ dependencies = [
|
||||||
"tracing",
|
"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]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.74"
|
version = "0.3.74"
|
||||||
|
|
@ -213,6 +225,7 @@ dependencies = [
|
||||||
"json",
|
"json",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
"serde",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -1146,18 +1159,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.216"
|
version = "1.0.217"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
|
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.216"
|
version = "1.0.217"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
|
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,11 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
axum = "0.7.9"
|
axum = { version = "0.7.9", features = ["macros"] }
|
||||||
chrono = "0.4.39"
|
chrono = "0.4.39"
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
json = "0.12.4"
|
json = "0.12.4"
|
||||||
regex = "1.11.1"
|
regex = "1.11.1"
|
||||||
reqwest = { version = "0.12.11", features = ['blocking', 'multipart'] }
|
reqwest = { version = "0.12.11", features = ['blocking', 'multipart'] }
|
||||||
|
serde = { version = "1.0.217", features = ["derive"] }
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
|
|
||||||
21
src/file.rs
21
src/file.rs
|
|
@ -22,6 +22,16 @@ impl<'a> LinksFile<'a> {
|
||||||
LinksFile(p)
|
LinksFile(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn read_all_lines(&self) -> Result<Vec<String>, 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::<Vec<String>>()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn remove_first_line(&self) -> Result<String, FileError> {
|
pub fn remove_first_line(&self) -> Result<String, FileError> {
|
||||||
let file_contents = read_to_string(self.0)?;
|
let file_contents = read_to_string(self.0)?;
|
||||||
let mut file_lines = file_contents.split("\n");
|
let mut file_lines = file_contents.split("\n");
|
||||||
|
|
@ -31,14 +41,19 @@ impl<'a> LinksFile<'a> {
|
||||||
Some(a) => a,
|
Some(a) => a,
|
||||||
None => panic!("Iterator returns None, something wrong happened")
|
None => panic!("Iterator returns None, something wrong happened")
|
||||||
};
|
};
|
||||||
let rest_of_file = file_lines.collect::<Vec<&str>>().join("\n");
|
let rest_of_file = file_lines
|
||||||
|
.collect::<Vec<&str>>()
|
||||||
|
.join("\n");
|
||||||
write(self.0, rest_of_file)?;
|
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> {
|
pub fn add_line_to_end(&self, line: String) -> Result<(), FileError> {
|
||||||
let old_contents = read_to_string(self.0)?;
|
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)?;
|
write(self.0, new_contents)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use tokio::{spawn};
|
||||||
|
|
||||||
mod file;
|
mod file;
|
||||||
mod wikipedia;
|
mod wikipedia;
|
||||||
|
|
@ -32,6 +33,8 @@ async fn main() {
|
||||||
let app_token = std::env::var("APP_TOKEN").expect("APP_TOKEN required.");
|
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 app_instance = std::env::var("APP_INSTANCE").expect("APP_INSTANCE required.");
|
||||||
|
|
||||||
|
|
||||||
|
let poster = spawn(async {
|
||||||
let queue = LinksFile::new(Path::new("queue.txt"));
|
let queue = LinksFile::new(Path::new("queue.txt"));
|
||||||
let backup = LinksFile::new(Path::new("backup.txt"));
|
let backup = LinksFile::new(Path::new("backup.txt"));
|
||||||
let mastodon = Mastodon::new(app_instance, app_token);
|
let mastodon = Mastodon::new(app_instance, app_token);
|
||||||
|
|
@ -39,9 +42,13 @@ async fn main() {
|
||||||
let mut interval_timer = tokio::time::interval(
|
let mut interval_timer = tokio::time::interval(
|
||||||
chrono::Duration::minutes(10).to_std().unwrap()
|
chrono::Duration::minutes(10).to_std().unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
interval_timer.tick().await;
|
interval_timer.tick().await;
|
||||||
post_link_from_file(queue, backup, mastodon.clone()).await;
|
post_link_from_file(queue, backup, mastodon.clone()).await;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let server = spawn(server::serve());
|
||||||
|
|
||||||
|
let _ = poster.await;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,12 @@ pub enum Visibility {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Visibility {
|
impl Visibility {
|
||||||
pub fn enum_name(&self) -> String {
|
pub fn enum_name(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::Public => "public".to_string(),
|
Self::Public => "public",
|
||||||
Self::Unlisted => "unlisted".to_string(),
|
Self::Unlisted => "unlisted",
|
||||||
Self::Private => "private".to_string(),
|
Self::Private => "private",
|
||||||
Self::Direct => "direct".to_string(),
|
Self::Direct => "direct",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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<AddPost>,) -> (StatusCode, Json<AddPostResponse>) {
|
||||||
|
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<AddPost>,) -> (StatusCode, Json<AddPostResponse>) {
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
@ -24,7 +24,7 @@ pub fn slug_from_link(link: String) -> Result<String, Error> {
|
||||||
let regex_pattern = Regex::new(r"\.wikipedia\.org\/wiki\/|\?").unwrap();
|
let regex_pattern = Regex::new(r"\.wikipedia\.org\/wiki\/|\?").unwrap();
|
||||||
let link_parts: Vec<&str> = regex_pattern.split(&link).collect();
|
let link_parts: Vec<&str> = regex_pattern.split(&link).collect();
|
||||||
if link_parts.len() >= 2 {
|
if link_parts.len() >= 2 {
|
||||||
Ok(link_parts[1].to_string())
|
Ok(link_parts[1].to_owned())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::IllegalLink(link))
|
Err(Error::IllegalLink(link))
|
||||||
}
|
}
|
||||||
|
|
@ -40,7 +40,7 @@ pub async fn title_from_slug(slug: String) -> Result<String, Error> {
|
||||||
let json_body = response.text().await?;
|
let json_body = response.text().await?;
|
||||||
let body = json::parse(&json_body)?;
|
let body = json::parse(&json_body)?;
|
||||||
match body["title"].as_str() {
|
match body["title"].as_str() {
|
||||||
Some(x) => Ok(x.to_string()),
|
Some(x) => Ok(x.to_owned()),
|
||||||
None => Err(Error::ArticleNotFound)
|
None => Err(Error::ArticleNotFound)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -49,7 +49,7 @@ pub async fn post_body(link: String) -> Result<String, Error> {
|
||||||
let title = title_from_slug(slug_from_link(link.clone())?).await?;
|
let title = title_from_slug(slug_from_link(link.clone())?).await?;
|
||||||
Ok(format!("Today's wikipedia article is {title}\n\n\
|
Ok(format!("Today's wikipedia article is {title}\n\n\
|
||||||
{link}\n\n\
|
{link}\n\n\
|
||||||
#wikipedia").to_string())
|
#wikipedia").to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
@ -58,27 +58,27 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_slug_from_link_1() {
|
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]
|
#[test]
|
||||||
fn test_slug_from_link_2() {
|
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]
|
#[tokio::test]
|
||||||
async fn test_title_from_slug_1() {
|
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]
|
#[tokio::test]
|
||||||
async fn test_title_from_slug_2() {
|
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]
|
#[tokio::test]
|
||||||
async fn test_post_body_1() {
|
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\
|
let expected = "Today's wikipedia article is GNU/Linux naming controversy\n\n\
|
||||||
https://en.wikipedia.org/wiki/GNU%2FLinux_naming_controversy\n\n\
|
https://en.wikipedia.org/wiki/GNU%2FLinux_naming_controversy\n\n\
|
||||||
#wikipedia";
|
#wikipedia";
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue