begin work on posse support
This commit is contained in:
parent
62489eaff4
commit
c310891743
|
@ -533,7 +533,6 @@ version = "0.12.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc"
|
checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
|
||||||
"version_check 0.9.2",
|
"version_check 0.9.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -866,17 +865,6 @@ dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jsonfeed"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "git+https://github.com/Xe/site#22575fca380f561006f44079ebe59019babe24d7"
|
|
||||||
dependencies = [
|
|
||||||
"error-chain",
|
|
||||||
"serde",
|
|
||||||
"serde_derive",
|
|
||||||
"serde_json",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kankyo"
|
name = "kankyo"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
@ -1013,7 +1001,6 @@ dependencies = [
|
||||||
"diesel",
|
"diesel",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"hex",
|
"hex",
|
||||||
"jsonfeed",
|
|
||||||
"kankyo",
|
"kankyo",
|
||||||
"log 0.4.11",
|
"log 0.4.11",
|
||||||
"mime 0.3.16",
|
"mime 0.3.16",
|
||||||
|
|
|
@ -32,8 +32,6 @@ ureq = { version = "1", features = ["json", "charset"] }
|
||||||
uuid = { version = "0.7", features = ["serde", "v4"] }
|
uuid = { version = "0.7", features = ["serde", "v4"] }
|
||||||
url = "2"
|
url = "2"
|
||||||
|
|
||||||
jsonfeed = { git = "https://github.com/Xe/site" }
|
|
||||||
|
|
||||||
[dependencies.diesel]
|
[dependencies.diesel]
|
||||||
version = "1"
|
version = "1"
|
||||||
features = ["sqlite", "r2d2", "uuidv07", "chrono"]
|
features = ["sqlite", "r2d2", "uuidv07", "chrono"]
|
||||||
|
|
|
@ -31,10 +31,3 @@ api_secret = "..."
|
||||||
instance = "..."
|
instance = "..."
|
||||||
token = "..."
|
token = "..."
|
||||||
account = "..."
|
account = "..."
|
||||||
|
|
||||||
# Get these values by creating an oAuth app
|
|
||||||
[global.reddit]
|
|
||||||
app_id = "..."
|
|
||||||
app_secret = "..."
|
|
||||||
username = "..."
|
|
||||||
password = "..."
|
|
|
@ -1,4 +1,4 @@
|
||||||
CREATE TABLE IF NOT EXISTS blogposts
|
CREATE TABLE IF NOT EXISTS blogposts
|
||||||
( url UNIQUE NOT NULL PRIMARY KEY
|
( url TEXT UNIQUE NOT NULL PRIMARY KEY
|
||||||
, title TEXT NOT NULL
|
, title TEXT NOT NULL
|
||||||
)
|
)
|
||||||
|
|
|
@ -12,6 +12,7 @@ use rocket::{
|
||||||
use rocket_contrib::json::Json;
|
use rocket_contrib::json::Json;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
|
pub mod posse;
|
||||||
pub mod switch;
|
pub mod switch;
|
||||||
pub mod webmention;
|
pub mod webmention;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
use super::Result;
|
||||||
|
use crate::{
|
||||||
|
models, paseto, schema,
|
||||||
|
web::{DiscordWebhook, Error as WebError, Mastodon, Result as WebResult, Twitter},
|
||||||
|
MainDatabase,
|
||||||
|
};
|
||||||
|
use diesel::prelude::*;
|
||||||
|
use rocket::State;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Jsonfeed {
|
||||||
|
pub version: String,
|
||||||
|
pub home_page_url: String,
|
||||||
|
pub items: Vec<Item>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone, Debug)]
|
||||||
|
pub struct Item {
|
||||||
|
pub url: String,
|
||||||
|
pub title: String,
|
||||||
|
pub tags: Option<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Item {
|
||||||
|
fn render(self) -> String {
|
||||||
|
let mut result = String::new();
|
||||||
|
|
||||||
|
write!(result, "{}\n\n{}", self.title, self.url).unwrap();
|
||||||
|
|
||||||
|
if let Some(tags) = self.tags {
|
||||||
|
write!(result, "\n\n").unwrap();
|
||||||
|
|
||||||
|
for tag in tags.iter() {
|
||||||
|
write!(result, "#{} ", tag).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<models::Blogpost> for Item {
|
||||||
|
fn into(self) -> models::Blogpost {
|
||||||
|
models::Blogpost {
|
||||||
|
url: self.url,
|
||||||
|
title: self.title,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_jsonfeed(url: String) -> WebResult<Jsonfeed> {
|
||||||
|
let resp = ureq::get(&url)
|
||||||
|
.set("User-Agent", crate::APPLICATION_NAME)
|
||||||
|
.call();
|
||||||
|
|
||||||
|
if resp.ok() {
|
||||||
|
Ok(resp.into_json_deserialize()?)
|
||||||
|
} else {
|
||||||
|
Err(match resp.synthetic_error() {
|
||||||
|
Some(why) => WebError::UReq(why.to_string()),
|
||||||
|
None => WebError::HttpStatus(resp.status()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(dw, tw, ma), err)]
|
||||||
|
fn posse(item: Item, dw: &DiscordWebhook, tw: &Twitter, ma: &Mastodon) -> WebResult {
|
||||||
|
let message = item.render();
|
||||||
|
|
||||||
|
dw.send(message.clone())?;
|
||||||
|
tw.tweet(message.clone())?;
|
||||||
|
ma.toot(message.clone())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static BLOG_FEED_URL: &'static str = "https://christine.website/blog.json";
|
||||||
|
|
||||||
|
#[post("/blog/refresh")]
|
||||||
|
#[instrument(skip(conn, dw, tw, ma), err)]
|
||||||
|
pub fn refresh_blog(
|
||||||
|
tok: paseto::Token,
|
||||||
|
conn: MainDatabase,
|
||||||
|
dw: State<DiscordWebhook>,
|
||||||
|
tw: State<Twitter>,
|
||||||
|
ma: State<Mastodon>,
|
||||||
|
) -> Result {
|
||||||
|
use schema::blogposts::dsl::blogposts;
|
||||||
|
let feed = read_jsonfeed(BLOG_FEED_URL.to_string())?;
|
||||||
|
|
||||||
|
for item in feed.items.into_iter() {
|
||||||
|
match blogposts
|
||||||
|
.find(item.url.clone())
|
||||||
|
.get_result::<models::Blogpost>(&*conn)
|
||||||
|
{
|
||||||
|
Ok(_) => continue,
|
||||||
|
Err(_) => {
|
||||||
|
diesel::insert_into(schema::blogposts::table)
|
||||||
|
.values(&{
|
||||||
|
let post: models::Blogpost = item.clone().into();
|
||||||
|
post
|
||||||
|
})
|
||||||
|
.execute(&*conn)?;
|
||||||
|
posse(item, &dw, &tw, &ma)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{read_jsonfeed, BLOG_FEED_URL};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_jsonfeed() {
|
||||||
|
read_jsonfeed(BLOG_FEED_URL.to_string()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
use super::{Error, Result};
|
use super::{Error, Result};
|
||||||
use crate::{models, schema, web::discord_webhook::Client as DiscordWebhook, MainDatabase};
|
use crate::{models, paseto, schema, web::discord_webhook::Client as DiscordWebhook, MainDatabase};
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use rocket::{
|
use rocket::{
|
||||||
request::Form,
|
request::Form,
|
||||||
|
@ -118,3 +118,27 @@ pub fn get(conn: MainDatabase, mention_id: String) -> Result<Json<models::WebMen
|
||||||
.map_err(Error::Database)?,
|
.map_err(Error::Database)?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("/webmention?<count>&<page>")]
|
||||||
|
#[instrument(skip(conn), err)]
|
||||||
|
pub fn list(
|
||||||
|
conn: MainDatabase,
|
||||||
|
count: Option<i64>,
|
||||||
|
page: Option<i64>,
|
||||||
|
tok: paseto::Token,
|
||||||
|
) -> Result<Json<Vec<models::WebMention>>> {
|
||||||
|
use schema::webmentions;
|
||||||
|
|
||||||
|
let count = count.unwrap_or(30);
|
||||||
|
let page = page.unwrap_or(0);
|
||||||
|
|
||||||
|
let count = if count < 100 { count } else { 100 };
|
||||||
|
|
||||||
|
Ok(Json(
|
||||||
|
webmentions::table
|
||||||
|
.limit(count)
|
||||||
|
.offset(count * (page - 1))
|
||||||
|
.load::<models::WebMention>(&*conn)
|
||||||
|
.map_err(Error::Database)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate tracing;
|
||||||
|
|
||||||
|
use color_eyre::eyre::Result;
|
||||||
|
use diesel::{prelude::*, SqliteConnection};
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
use mi::{api::posse::*, *};
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let _ = kankyo::init();
|
||||||
|
color_eyre::install()?;
|
||||||
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
|
info!("{} blogpost importer starting up", mi::APPLICATION_NAME);
|
||||||
|
|
||||||
|
let conn = establish_connection();
|
||||||
|
|
||||||
|
let feed = read_jsonfeed(BLOG_FEED_URL.to_string())?;
|
||||||
|
let posts: Vec<models::Blogpost> = feed
|
||||||
|
.items
|
||||||
|
.into_iter()
|
||||||
|
.map(|item| {
|
||||||
|
let post: models::Blogpost = item.into();
|
||||||
|
post
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
diesel::insert_into(schema::blogposts::table)
|
||||||
|
.values(&posts)
|
||||||
|
.execute(&conn)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn establish_connection() -> SqliteConnection {
|
||||||
|
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||||
|
SqliteConnection::establish(&database_url)
|
||||||
|
.expect(&format!("Error connecting to {}", database_url))
|
||||||
|
}
|
|
@ -53,12 +53,14 @@ fn main() -> Result<()> {
|
||||||
.mount(
|
.mount(
|
||||||
"/api",
|
"/api",
|
||||||
routes![
|
routes![
|
||||||
|
api::posse::refresh_blog,
|
||||||
api::switch::current_front,
|
api::switch::current_front,
|
||||||
api::switch::get,
|
api::switch::get,
|
||||||
api::switch::list,
|
api::switch::list,
|
||||||
api::switch::switch,
|
api::switch::switch,
|
||||||
api::webmention::accept,
|
api::webmention::accept,
|
||||||
api::webmention::get,
|
api::webmention::get,
|
||||||
|
api::webmention::list,
|
||||||
api::get_members,
|
api::get_members,
|
||||||
api::token_info,
|
api::token_info,
|
||||||
api::tweet,
|
api::tweet,
|
||||||
|
|
|
@ -58,3 +58,10 @@ pub struct WebMention {
|
||||||
pub source_url: String,
|
pub source_url: String,
|
||||||
pub target_url: String,
|
pub target_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Queryable, Associations, Insertable)]
|
||||||
|
#[table_name = "blogposts"]
|
||||||
|
pub struct Blogpost {
|
||||||
|
pub url: String,
|
||||||
|
pub title: String,
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
table! {
|
table! {
|
||||||
blogposts (url) {
|
blogposts (url) {
|
||||||
url -> Binary,
|
url -> Text,
|
||||||
title -> Text,
|
title -> Text,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue