diff --git a/backup.txt b/backup.txt new file mode 100644 index 0000000..6588b5f --- /dev/null +++ b/backup.txt @@ -0,0 +1,72 @@ +https://en.wikipedia.org/wiki/Nova_Scotia_Trunk_9 +https://en.wikipedia.org/wiki/World_line +https://en.wikipedia.org/wiki/Vorobyovy_Gory_(Moscow_Metro) +https://en.wikipedia.org/wiki/Female_impersonators +https://en.wikipedia.org/wiki/Pauline_Donalda +https://en.wikipedia.org/wiki/Skull_and_Bones +https://en.wikipedia.org/wiki/Woodin_cardinal +https://en.wikipedia.org/wiki/Hoy_No_Circula?wprov=sfla1 +https://en.wikipedia.org/wiki/Bogdanov_affair +https://en.wikipedia.org/wiki/Subscriber_trunk_dialling +https://en.wikipedia.org/wiki/Area_code_318 +https://en.wikipedia.org/wiki/Irving_Fisher +https://en.wikipedia.org/wiki/Can%27t_Stop_(board_game) +https://en.wikipedia.org/wiki/Three_Arrows_Capital?wprov=sfla1 +https://en.wikipedia.org/wiki/Karlheinz_Stockhausen?wprov=sfla1 +https://en.wikipedia.org/wiki/Kansas_experiment +https://en.wikipedia.org/wiki/Refusenik +https://en.wikipedia.org/wiki/Havre_de_Grace,_Maryland +https://en.wikipedia.org/wiki/Bootblacking_%28BDSM%29?wprov=sfla1 +https://en.wikipedia.org/wiki/Flag_of_Martinique +https://en.wikipedia.org/wiki/Wii_system_software?wprov=sfti1 +https://en.wikipedia.org/wiki/Vajont_Dam?wprov=sfla1 +https://en.wikipedia.org/wiki/Massachusetts_Avenue_(metropolitan_Boston) +https://en.wikipedia.org/wiki/User_error?wprov=sfla1 +https://en.wikipedia.org/wiki/Pangram +https://en.wikipedia.org/wiki/Analemma +https://en.wikipedia.org/wiki/Market_basket +https://en.wikipedia.org/wiki/Indian-made_foreign_liquor +https://en.wikipedia.org/wiki/Meeting_of_Waters +https://en.wikipedia.org/wiki/Snub_(geometry) +https://en.wikipedia.org/wiki/List_of_Interstate_Highways_in_Alaska +https://en.wikipedia.org/wiki/Jeff_Award +https://en.wikipedia.org/wiki/Crazy_Frog +https://en.wikipedia.org/wiki/Hurricane_Iota +https://en.wikipedia.org/wiki/Wade%E2%80%93Giles?wprov=sfti1 +https://en.wikipedia.org/wiki/Japanese_addressing_system?wprov=sfti1 +https://en.wikipedia.org/wiki/Cuban_peso +https://en.wikipedia.org/wiki/Telephone_numbers_in_the_State_of_Palestine +https://en.wikipedia.org/wiki/Han_unification +https://en.wikipedia.org/wiki/Changhua%E2%80%93Kaohsiung_Viaduct +https://en.wikipedia.org/wiki/Subway_%28George_Bush_Intercontinental_Airport%29?wprov=sfla1 +https://en.wikipedia.org/wiki/Hawaii_Route_200?wprov=sfla1 +https://en.wikipedia.org/wiki/Santa_Muerte?wprov=sfla1 +https://en.wikipedia.org/wiki/National_conventions_for_writing_telephone_numbers?wprov=sfti1 +https://en.wikipedia.org/wiki/FOAF?wprov=sfti1 +https://en.wikipedia.org/wiki/Quebec_Route_389 +https://en.wikipedia.org/wiki/List_of_terms_referring_to_an_average_person?wprov=sfti1 +https://en.wikipedia.org/wiki/Artemis_program?wprov=sfla1 +https://en.wikipedia.org/wiki/The_Chalice_of_the_Gods?wprov=sfla1 +https://en.wikipedia.org/wiki/Chevrolet_Citation +https://en.wikipedia.org/wiki/History_of_the_Comoros_(1978%E2%80%931989) +https://en.wikipedia.org/wiki/Wag_(company) +https://en.wikipedia.org/wiki/New_York_City_steam_system +https://en.wikipedia.org/wiki/Airbus_Mobile +https://en.wikipedia.org/wiki/Derry/Londonderry_name_dispute +https://en.wikipedia.org/wiki/RS-Computer +https://en.wikipedia.org/wiki/Trainbow?wprov=sfti1 +https://en.wikipedia.org/wiki/Siege_of_Suiyang +https://en.wikipedia.org/wiki/Amtrak's_25_Hz_traction_power_system +https://en.wikipedia.org/wiki/Amtrak's_60_Hz_traction_power_system +https://en.wikipedia.org/wiki/PowerCon +https://en.wikipedia.org/wiki/Chateaugay,_New_York +https://en.wikipedia.org/wiki/996_working_hour_system +https://en.wikipedia.org/wiki/Cin%C3%A9mas_Guzzo +https://en.wikipedia.org/wiki/List_of_people_from_Montclair,_New_Jersey +https://en.wikipedia.org/wiki/Latent_heat +https://en.wikipedia.org/wiki/Enthalpy_of_vaporization +https://en.wikipedia.org/wiki/Asahi-class_destroyer +https://en.wikipedia.org/wiki/Algiers_Accords +https://en.wikipedia.org/wiki/U.S._Route_23_in_Tennessee +https://en.wikipedia.org/wiki/Gradian +https://en.wikipedia.org/wiki/Electrostatics diff --git a/queue.txt b/queue.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/file.rs b/src/file.rs new file mode 100644 index 0000000..fe2af7c --- /dev/null +++ b/src/file.rs @@ -0,0 +1,45 @@ +use std::fs::{read_to_string, write, File}; +use std::io; +use std::io::{Read, Write}; +use std::path::Path; + +#[derive(Debug)] +pub enum FileError { + IOError(io::Error), + EmptyLine, +} + +impl From for FileError { + fn from(value: io::Error) -> Self { + Self::IOError(value) + } +} + +pub struct LinksFile<'a>(&'a Path); + +impl<'a> LinksFile<'a> { + pub fn new(p: &'a Path) -> LinksFile<'a> { + LinksFile(p) + } + + pub fn remove_first_line(&self) -> Result { + let file_contents = read_to_string(self.0)?; + let mut file_lines = file_contents.split("\n"); + let first_line = file_lines.next(); + let first_line_clean = match first_line { + Some("") => return Err(FileError::EmptyLine), + Some(a) => a, + None => panic!("Iterator returns None, something wrong happened") + }; + let rest_of_file = file_lines.collect::>().join("\n"); + write(self.0, rest_of_file)?; + Ok(first_line_clean.to_string()) + } + + 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; + write(self.0, new_contents)?; + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 40b968d..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,17 +0,0 @@ -use regex::Regex; - -fn slug_from_link(link: String) -> Result { - let re = Regex::new(r"\.wikipedia\.org\/wiki\/|\?").unwrap(); - let v: Vec<&str> = re.split(&link).collect(); - println!("{v:?}"); - if v.len() >= 2 { Ok(v[1].to_string()) } else { Err("Illegal link {link} provided".to_string()) } -} - -mod tests { - use super::*; - - #[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") - } -} diff --git a/src/main.rs b/src/main.rs index e7a11a9..9b6f27b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,31 @@ +use std::path::Path; + +use file::{FileError, LinksFile}; +use mastodon::Mastodon; + +mod file; +mod wikipedia; +mod mastodon; + fn main() { - println!("Hello, world!"); + dotenv::dotenv().ok(); + let app_code = std::env::var("APP_CODE").expect("APP_CODE 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 queue = LinksFile::new(Path::new("queue.txt")); + let backup = LinksFile::new(Path::new("backup.txt")); + + let mastodon = Mastodon::new(app_instance, app_token); + + // get link to post + let link_to_post = match queue.remove_first_line() { + Ok(a) => a, + Err(FileError::EmptyLine) => match backup.remove_first_line() { + Ok(b) => b, + Err(c) => panic!("{c:?}") + }, + Err(d) => panic!("{d:?}") + }; + println!("{link_to_post}") } diff --git a/src/mastodon.rs b/src/mastodon.rs new file mode 100644 index 0000000..9566a78 --- /dev/null +++ b/src/mastodon.rs @@ -0,0 +1,58 @@ +use reqwest::{blocking::multipart, StatusCode}; + +pub enum Error { + ReqwestError(reqwest::Error), + FailureStatus(reqwest::StatusCode) +} + +impl From for Error { + fn from(value: reqwest::Error) -> Self { + Self::ReqwestError(value) + } +} + +#[derive(Copy, Clone)] +enum Visibility { + Public, + Unlisted, + Private, + Direct +} + +impl Visibility { + pub fn enum_name(&self) -> String { + match self { + Self::Public => "public".to_string(), + Self::Unlisted => "unlisted".to_string(), + Self::Private => "private".to_string(), + Self::Direct => "direct".to_string(), + } + } +} + +pub struct Mastodon { + instance: String, + token: String +} + +impl Mastodon { + pub fn new(instance: String, token: String) -> Self { + Mastodon { instance, token } + } + + pub fn post_text_status(&self, status: String, visibility: Visibility) -> Result<(), Error> { + let form = multipart::Form::new() + .text("status", status) + .text("visibility", visibility.enum_name()); + let client = reqwest::blocking::Client::new(); + let response = client + .post(format!("{}/api/v1/statuses", self.instance)) + .header("Authorization", format!("Bearer {}", self.token)) + .multipart(form) + .send()?; + match response.status() { + StatusCode::OK => Ok(()), + s => Err(Error::FailureStatus(s)) + } + } +} diff --git a/src/wikipedia.rs b/src/wikipedia.rs new file mode 100644 index 0000000..7487a83 --- /dev/null +++ b/src/wikipedia.rs @@ -0,0 +1,87 @@ +use regex::Regex; + +#[derive(Debug)] +pub enum Error{ + IllegalLink(String), + ReqwestError(reqwest::Error), + JsonError(json::Error), + ArticleNotFound, +} + +impl From for Error { + fn from(value: reqwest::Error) -> Self { + Self::ReqwestError(value) + } +} + +impl From for Error { + fn from(value: json::Error) -> Self { + Self::JsonError(value) + } +} + +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()) + } else { + Err(Error::IllegalLink(link)) + } +} + +pub fn title_from_slug(slug: String) -> Result { + let escaped_slug = slug.replace("/", "%2F"); + let request_url = + format!( + "https://api.wikimedia.org/core/v1/wikipedia/en/page/{escaped_slug}/bare" + ); + let response = reqwest::blocking::get(request_url)?; + let json_body = response.text()?; + let body = json::parse(&json_body)?; + match body["title"].as_str() { + Some(x) => Ok(x.to_string()), + None => Err(Error::ArticleNotFound) + } +} + +pub fn post_body(link: String) -> Result { + let title = title_from_slug(slug_from_link(link.clone())?)?; + Ok(format!("Today's wikipedia article is {title}\n\n\ + {link}\n\n\ + #wikipedia").to_string()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[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") + } + + #[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") + } + + #[test] + fn test_title_from_slug_1() { + assert_eq!(title_from_slug("Buck-a-beer".to_string()).unwrap().as_str(), "Buck-a-beer") + } + + #[test] + fn test_title_from_slug_2() { + assert_eq!(title_from_slug("GNU/Linux_naming_controversy".to_string()).unwrap().as_str(), "GNU/Linux naming controversy") + } + + #[test] + fn test_post_body_1() { + let body = post_body("https://en.wikipedia.org/wiki/GNU%2FLinux_naming_controversy".to_string()).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"; + assert_eq!(body, expected) + } +}