Added core functionality.

This commit is contained in:
vorboyvo 2024-12-28 17:02:22 -05:00
parent ed40f2e299
commit 7a12fc4490
5 changed files with 53 additions and 35 deletions

View file

@ -1,6 +1,5 @@
use std::fs::{read_to_string, write, File}; use std::fs::{read_to_string, write};
use std::io; use std::io;
use std::io::{Read, Write};
use std::path::Path; use std::path::Path;
#[derive(Debug)] #[derive(Debug)]
@ -15,6 +14,7 @@ impl From<io::Error> for FileError {
} }
} }
#[derive(Copy, Clone)]
pub struct LinksFile<'a>(&'a Path); pub struct LinksFile<'a>(&'a Path);
impl<'a> LinksFile<'a> { impl<'a> LinksFile<'a> {

View file

@ -1,23 +1,15 @@
use std::path::Path; use std::path::Path;
use file::{FileError, LinksFile};
use mastodon::Mastodon;
mod file; mod file;
mod wikipedia; mod wikipedia;
mod mastodon; mod mastodon;
mod server;
fn main() { use file::{FileError, LinksFile};
dotenv::dotenv().ok(); use mastodon::{Mastodon, Visibility};
let app_code = std::env::var("APP_CODE").expect("APP_CODE required."); use wikipedia::post_body;
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);
async fn post_link_from_file(queue: LinksFile<'_>, backup: LinksFile<'_>, mastodon: Mastodon) {
// get link to post // get link to post
let link_to_post = match queue.remove_first_line() { let link_to_post = match queue.remove_first_line() {
Ok(a) => a, Ok(a) => a,
@ -27,5 +19,29 @@ fn main() {
}, },
Err(d) => panic!("{d:?}") Err(d) => panic!("{d:?}")
}; };
println!("{link_to_post}") // convert said link into a post status
let post_status = post_body(link_to_post).await.unwrap();
// post it on mastodon
mastodon.post_text_status(post_status, Visibility::Direct).await.unwrap();
}
#[tokio::main]
async fn main() {
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);
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;
}
} }

View file

@ -1,5 +1,6 @@
use reqwest::{blocking::multipart, StatusCode}; use reqwest::{multipart, StatusCode};
#[derive(Debug)]
pub enum Error { pub enum Error {
ReqwestError(reqwest::Error), ReqwestError(reqwest::Error),
FailureStatus(reqwest::StatusCode) FailureStatus(reqwest::StatusCode)
@ -12,7 +13,7 @@ impl From<reqwest::Error> for Error {
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
enum Visibility { pub enum Visibility {
Public, Public,
Unlisted, Unlisted,
Private, Private,
@ -30,6 +31,7 @@ impl Visibility {
} }
} }
#[derive(Clone)]
pub struct Mastodon { pub struct Mastodon {
instance: String, instance: String,
token: String token: String
@ -40,16 +42,16 @@ impl Mastodon {
Mastodon { instance, token } Mastodon { instance, token }
} }
pub fn post_text_status(&self, status: String, visibility: Visibility) -> Result<(), Error> { pub async fn post_text_status(&self, status: String, visibility: Visibility) -> Result<(), Error> {
let form = multipart::Form::new() let form = multipart::Form::new()
.text("status", status) .text("status", status)
.text("visibility", visibility.enum_name()); .text("visibility", visibility.enum_name());
let client = reqwest::blocking::Client::new(); let client = reqwest::Client::new();
let response = client let response = client
.post(format!("{}/api/v1/statuses", self.instance)) .post(format!("{}/api/v1/statuses", self.instance))
.header("Authorization", format!("Bearer {}", self.token)) .header("Authorization", format!("Bearer {}", self.token))
.multipart(form) .multipart(form)
.send()?; .send().await?;
match response.status() { match response.status() {
StatusCode::OK => Ok(()), StatusCode::OK => Ok(()),
s => Err(Error::FailureStatus(s)) s => Err(Error::FailureStatus(s))

0
src/server.rs Normal file
View file

View file

@ -30,14 +30,14 @@ pub fn slug_from_link(link: String) -> Result<String, Error> {
} }
} }
pub fn title_from_slug(slug: String) -> Result<String, Error> { pub async fn title_from_slug(slug: String) -> Result<String, Error> {
let escaped_slug = slug.replace("/", "%2F"); let escaped_slug = slug.replace("/", "%2F");
let request_url = let request_url =
format!( format!(
"https://api.wikimedia.org/core/v1/wikipedia/en/page/{escaped_slug}/bare" "https://api.wikimedia.org/core/v1/wikipedia/en/page/{escaped_slug}/bare"
); );
let response = reqwest::blocking::get(request_url)?; let response = reqwest::get(request_url).await?;
let json_body = response.text()?; 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_string()),
@ -45,8 +45,8 @@ pub fn title_from_slug(slug: String) -> Result<String, Error> {
} }
} }
pub fn post_body(link: String) -> Result<String, Error> { pub async fn post_body(link: String) -> Result<String, Error> {
let title = title_from_slug(slug_from_link(link.clone())?)?; 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_string())
@ -66,19 +66,19 @@ mod tests {
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_string()).unwrap().as_str(), "GNU/Linux_naming_controversy")
} }
#[test] #[tokio::test]
fn test_title_from_slug_1() { async fn test_title_from_slug_1() {
assert_eq!(title_from_slug("Buck-a-beer".to_string()).unwrap().as_str(), "Buck-a-beer") assert_eq!(title_from_slug("Buck-a-beer".to_string()).await.unwrap().as_str(), "Buck-a-beer")
} }
#[test] #[tokio::test]
fn test_title_from_slug_2() { async fn test_title_from_slug_2() {
assert_eq!(title_from_slug("GNU/Linux_naming_controversy".to_string()).unwrap().as_str(), "GNU/Linux naming controversy") assert_eq!(title_from_slug("GNU/Linux_naming_controversy".to_string()).await.unwrap().as_str(), "GNU/Linux naming controversy")
} }
#[test] #[tokio::test]
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()).unwrap(); let body = post_body("https://en.wikipedia.org/wiki/GNU%2FLinux_naming_controversy".to_string()).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";