mirror of
https://github.com/nullishamy/ferri.git
synced 2025-04-29 20:29:23 +00:00
feat: basic Announce support
This commit is contained in:
parent
3719fae102
commit
2270324711
10 changed files with 178 additions and 4 deletions
|
@ -111,6 +111,7 @@ async fn create_status(
|
||||||
favourites_count: 0,
|
favourites_count: 0,
|
||||||
favourited: false,
|
favourited: false,
|
||||||
reblogged: false,
|
reblogged: false,
|
||||||
|
reblog: None,
|
||||||
muted: false,
|
muted: false,
|
||||||
bookmarked: false,
|
bookmarked: false,
|
||||||
media_attachments: vec![],
|
media_attachments: vec![],
|
||||||
|
|
|
@ -27,6 +27,7 @@ pub struct TimelineStatus {
|
||||||
pub reblogged: bool,
|
pub reblogged: bool,
|
||||||
pub muted: bool,
|
pub muted: bool,
|
||||||
pub bookmarked: bool,
|
pub bookmarked: bool,
|
||||||
|
pub reblog: Option<Box<TimelineStatus>>,
|
||||||
pub media_attachments: Vec<()>,
|
pub media_attachments: Vec<()>,
|
||||||
pub account: TimelineAccount,
|
pub account: TimelineAccount,
|
||||||
}
|
}
|
||||||
|
@ -40,7 +41,7 @@ pub async fn home(
|
||||||
let posts = sqlx::query!(
|
let posts = sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
SELECT p.id as "post_id", u.id as "user_id", p.content, p.uri as "post_uri",
|
SELECT p.id as "post_id", u.id as "user_id", p.content, p.uri as "post_uri",
|
||||||
u.username, u.display_name, u.actor_id, p.created_at
|
u.username, u.display_name, u.actor_id, p.created_at, p.boosted_post_id
|
||||||
FROM post p
|
FROM post p
|
||||||
INNER JOIN user u on p.user_id = u.id
|
INNER JOIN user u on p.user_id = u.id
|
||||||
"#
|
"#
|
||||||
|
@ -51,6 +52,64 @@ pub async fn home(
|
||||||
|
|
||||||
let mut out = Vec::<TimelineStatus>::new();
|
let mut out = Vec::<TimelineStatus>::new();
|
||||||
for record in posts {
|
for record in posts {
|
||||||
|
let mut boost: Option<Box<TimelineStatus>> = None;
|
||||||
|
if let Some(boosted_id) = record.boosted_post_id {
|
||||||
|
let record = sqlx::query!(
|
||||||
|
r#"
|
||||||
|
SELECT p.id as "post_id", u.id as "user_id", p.content, p.uri as "post_uri",
|
||||||
|
u.username, u.display_name, u.actor_id, p.created_at, p.boosted_post_id
|
||||||
|
FROM post p
|
||||||
|
INNER JOIN user u on p.user_id = u.id
|
||||||
|
WHERE p.id = ?1
|
||||||
|
"#, boosted_id)
|
||||||
|
.fetch_one(&mut **db)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let user_uri = format!("https://ferri.amy.mov/users/{}", record.user_id);
|
||||||
|
boost = Some(Box::new(TimelineStatus {
|
||||||
|
id: record.post_id.clone(),
|
||||||
|
created_at: record.created_at.clone(),
|
||||||
|
in_reply_to_id: None,
|
||||||
|
in_reply_to_account_id: None,
|
||||||
|
content: record.content.clone(),
|
||||||
|
visibility: "public".to_string(),
|
||||||
|
spoiler_text: "".to_string(),
|
||||||
|
sensitive: false,
|
||||||
|
uri: record.post_uri.clone(),
|
||||||
|
url: record.post_uri.clone(),
|
||||||
|
replies_count: 0,
|
||||||
|
reblogs_count: 0,
|
||||||
|
favourites_count: 0,
|
||||||
|
favourited: false,
|
||||||
|
reblogged: false,
|
||||||
|
reblog: boost,
|
||||||
|
muted: false,
|
||||||
|
bookmarked: false,
|
||||||
|
media_attachments: vec![],
|
||||||
|
account: CredentialAcount {
|
||||||
|
id: record.user_id.clone(),
|
||||||
|
username: record.username.clone(),
|
||||||
|
acct: record.username.clone(),
|
||||||
|
display_name: record.display_name.clone(),
|
||||||
|
locked: false,
|
||||||
|
bot: false,
|
||||||
|
created_at: "2025-04-10T22:12:09Z".to_string(),
|
||||||
|
attribution_domains: vec![],
|
||||||
|
note: "".to_string(),
|
||||||
|
url: user_uri,
|
||||||
|
avatar: "https://ferri.amy.mov/assets/pfp.png".to_string(),
|
||||||
|
avatar_static: "https://ferri.amy.mov/assets/pfp.png".to_string(),
|
||||||
|
header: "https://ferri.amy.mov/assets/pfp.png".to_string(),
|
||||||
|
header_static: "https://ferri.amy.mov/assets/pfp.png".to_string(),
|
||||||
|
followers_count: 1,
|
||||||
|
following_count: 1,
|
||||||
|
statuses_count: 1,
|
||||||
|
last_status_at: "2025-04-10T22:14:34Z".to_string(),
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
let user_uri = format!("https://ferri.amy.mov/users/{}", record.username);
|
let user_uri = format!("https://ferri.amy.mov/users/{}", record.username);
|
||||||
out.push(TimelineStatus {
|
out.push(TimelineStatus {
|
||||||
id: record.post_id.clone(),
|
id: record.post_id.clone(),
|
||||||
|
@ -68,6 +127,7 @@ pub async fn home(
|
||||||
favourites_count: 0,
|
favourites_count: 0,
|
||||||
favourited: false,
|
favourited: false,
|
||||||
reblogged: false,
|
reblogged: false,
|
||||||
|
reblog: boost,
|
||||||
muted: false,
|
muted: false,
|
||||||
bookmarked: false,
|
bookmarked: false,
|
||||||
media_attachments: vec![],
|
media_attachments: vec![],
|
||||||
|
|
|
@ -165,6 +165,7 @@ pub async fn statuses(
|
||||||
favourites_count: 0,
|
favourites_count: 0,
|
||||||
favourited: false,
|
favourited: false,
|
||||||
reblogged: false,
|
reblogged: false,
|
||||||
|
reblog: None,
|
||||||
muted: false,
|
muted: false,
|
||||||
bookmarked: false,
|
bookmarked: false,
|
||||||
media_attachments: vec![],
|
media_attachments: vec![],
|
||||||
|
|
|
@ -99,6 +99,7 @@ pub async fn test(http: &State<HttpClient>) -> &'static str {
|
||||||
ts: "2025-04-10T10:48:11Z".to_string(),
|
ts: "2025-04-10T10:48:11Z".to_string(),
|
||||||
to: vec!["https://ferri.amy.mov/users/amy/followers".to_string()],
|
to: vec!["https://ferri.amy.mov/users/amy/followers".to_string()],
|
||||||
cc: vec!["https://www.w3.org/ns/activitystreams#Public".to_string()],
|
cc: vec!["https://www.w3.org/ns/activitystreams#Public".to_string()],
|
||||||
|
attributed_to: None
|
||||||
},
|
},
|
||||||
ts: "2025-04-10T10:48:11Z".to_string(),
|
ts: "2025-04-10T10:48:11Z".to_string(),
|
||||||
to: vec!["https://ferri.amy.mov/users/amy/followers".to_string()],
|
to: vec!["https://ferri.amy.mov/users/amy/followers".to_string()],
|
||||||
|
|
|
@ -11,7 +11,7 @@ use tracing::{event, span, Level, debug, warn, info};
|
||||||
use crate::{
|
use crate::{
|
||||||
Db,
|
Db,
|
||||||
http::HttpClient,
|
http::HttpClient,
|
||||||
types::{Person, activity},
|
types::{Person, content::Post, activity},
|
||||||
};
|
};
|
||||||
|
|
||||||
fn handle_delete_activity(activity: activity::DeleteActivity) {
|
fn handle_delete_activity(activity: activity::DeleteActivity) {
|
||||||
|
@ -142,6 +142,92 @@ async fn handle_like_activity(activity: activity::LikeActivity, mut db: Connecti
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_boost_activity(
|
||||||
|
activity: activity::BoostActivity,
|
||||||
|
http: &HttpClient,
|
||||||
|
mut db: Connection<Db>,
|
||||||
|
) {
|
||||||
|
let key_id = "https://ferri.amy.mov/users/amy#main-key";
|
||||||
|
dbg!(&activity);
|
||||||
|
let post = http
|
||||||
|
.get(&activity.object)
|
||||||
|
.activity()
|
||||||
|
.sign(&key_id)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.json::<Post>()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
dbg!(&post);
|
||||||
|
let attribution = post.attributed_to.unwrap();
|
||||||
|
let post_user = http
|
||||||
|
.get(&attribution)
|
||||||
|
.activity()
|
||||||
|
.sign(&key_id)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.json::<Person>()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let user = http
|
||||||
|
.get(&activity.actor)
|
||||||
|
.activity()
|
||||||
|
.sign(&key_id)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.json::<Person>()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
dbg!(&post_user);
|
||||||
|
|
||||||
|
debug!("creating actor {}", activity.actor);
|
||||||
|
create_actor(&user, &activity.actor, &mut **db).await;
|
||||||
|
|
||||||
|
debug!("creating user {}", activity.actor);
|
||||||
|
create_user(&user, &activity.actor, &mut **db).await;
|
||||||
|
|
||||||
|
debug!("creating actor {}", attribution);
|
||||||
|
create_actor(&post_user, &attribution, &mut **db).await;
|
||||||
|
|
||||||
|
debug!("creating user {}", attribution);
|
||||||
|
create_user(&post_user, &attribution, &mut **db).await;
|
||||||
|
|
||||||
|
let attributed_user = ap::User::from_actor_id(&attribution, &mut **db).await;
|
||||||
|
let actor_user = ap::User::from_actor_id(&activity.actor, &mut **db).await;
|
||||||
|
|
||||||
|
let base_id = ap::new_id();
|
||||||
|
let now = ap::new_ts();
|
||||||
|
|
||||||
|
let reblog_id = ap::new_id();
|
||||||
|
|
||||||
|
let attr_id = attributed_user.id();
|
||||||
|
sqlx::query!("
|
||||||
|
INSERT INTO post (id, uri, user_id, content, created_at)
|
||||||
|
VALUES (?1, ?2, ?3, ?4, ?5)
|
||||||
|
", reblog_id, post.id, attr_id, post.content, post.ts)
|
||||||
|
.execute(&mut **db)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let uri = format!("https://ferri.amy.mov/users/{}/posts/{}", actor_user.id(), post.id);
|
||||||
|
let user_id = actor_user.id();
|
||||||
|
|
||||||
|
sqlx::query!("
|
||||||
|
INSERT INTO post (id, uri, user_id, content, created_at, boosted_post_id)
|
||||||
|
VALUES (?1, ?2, ?3, ?4, ?5, ?6)
|
||||||
|
", base_id, uri, user_id, "", now, reblog_id)
|
||||||
|
.execute(&mut **db)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
async fn handle_create_activity(
|
async fn handle_create_activity(
|
||||||
activity: activity::CreateActivity,
|
activity: activity::CreateActivity,
|
||||||
http: &HttpClient,
|
http: &HttpClient,
|
||||||
|
@ -218,6 +304,11 @@ pub async fn inbox(db: Connection<Db>, http: &State<HttpClient>, user: &str, bod
|
||||||
let activity = serde_json::from_str::<activity::LikeActivity>(&body).unwrap();
|
let activity = serde_json::from_str::<activity::LikeActivity>(&body).unwrap();
|
||||||
handle_like_activity(activity, db).await;
|
handle_like_activity(activity, db).await;
|
||||||
}
|
}
|
||||||
|
"Announce" => {
|
||||||
|
let activity = serde_json::from_str::<activity::BoostActivity>(&body).unwrap();
|
||||||
|
handle_boost_activity(activity, http.inner(), db).await;
|
||||||
|
}
|
||||||
|
|
||||||
act => {
|
act => {
|
||||||
warn!(act, body, "unknown activity");
|
warn!(act, body, "unknown activity");
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,6 +107,7 @@ pub async fn post(
|
||||||
Json(content::Post {
|
Json(content::Post {
|
||||||
context: "https://www.w3.org/ns/activitystreams".to_string(),
|
context: "https://www.w3.org/ns/activitystreams".to_string(),
|
||||||
id: format!("https://ferri.amy.mov/users/{}/posts/{}", uuid, post.id),
|
id: format!("https://ferri.amy.mov/users/{}/posts/{}", uuid, post.id),
|
||||||
|
attributed_to: Some(format!("https://ferri.amy.mov/users/{}/posts/{}", uuid, post.id)),
|
||||||
ty: "Note".to_string(),
|
ty: "Note".to_string(),
|
||||||
content: post.content,
|
content: post.content,
|
||||||
ts: post.created_at,
|
ts: post.created_at,
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use rocket::serde::{Deserialize, Serialize};
|
use rocket::serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::content::Post;
|
use crate::types::content::Post;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
@ -60,3 +59,17 @@ pub struct AcceptActivity {
|
||||||
pub object: String,
|
pub object: String,
|
||||||
pub actor: String,
|
pub actor: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct BoostActivity {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub ty: String,
|
||||||
|
|
||||||
|
pub id: String,
|
||||||
|
pub actor: String,
|
||||||
|
pub published: String,
|
||||||
|
pub to: Vec<String>,
|
||||||
|
pub cc: Vec<String>,
|
||||||
|
pub object: String,
|
||||||
|
}
|
||||||
|
|
|
@ -15,4 +15,7 @@ pub struct Post {
|
||||||
pub content: String,
|
pub content: String,
|
||||||
pub to: Vec<String>,
|
pub to: Vec<String>,
|
||||||
pub cc: Vec<String>,
|
pub cc: Vec<String>,
|
||||||
|
|
||||||
|
#[serde(rename = "attributedTo")]
|
||||||
|
pub attributed_to: Option<String>
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ pub struct Person {
|
||||||
pub inbox: String,
|
pub inbox: String,
|
||||||
pub outbox: String,
|
pub outbox: String,
|
||||||
pub preferred_username: String,
|
pub preferred_username: String,
|
||||||
|
#[serde(default)]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub public_key: Option<UserKey>,
|
pub public_key: Option<UserKey>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ CREATE TABLE IF NOT EXISTS post
|
||||||
user_id TEXT NOT NULL,
|
user_id TEXT NOT NULL,
|
||||||
content TEXT NOT NULL,
|
content TEXT NOT NULL,
|
||||||
created_at TEXT NOT NULL,
|
created_at TEXT NOT NULL,
|
||||||
|
boosted_post_id TEXT,
|
||||||
|
|
||||||
FOREIGN KEY(user_id) REFERENCES user(id)
|
FOREIGN KEY(user_id) REFERENCES user(id),
|
||||||
|
FOREIGN KEY(boosted_post_id) REFERENCES post(id)
|
||||||
);
|
);
|
||||||
|
|
Loading…
Add table
Reference in a new issue