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,
|
||||
favourited: false,
|
||||
reblogged: false,
|
||||
reblog: None,
|
||||
muted: false,
|
||||
bookmarked: false,
|
||||
media_attachments: vec![],
|
||||
|
|
|
@ -27,6 +27,7 @@ pub struct TimelineStatus {
|
|||
pub reblogged: bool,
|
||||
pub muted: bool,
|
||||
pub bookmarked: bool,
|
||||
pub reblog: Option<Box<TimelineStatus>>,
|
||||
pub media_attachments: Vec<()>,
|
||||
pub account: TimelineAccount,
|
||||
}
|
||||
|
@ -40,7 +41,7 @@ pub async fn home(
|
|||
let posts = 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
|
||||
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
|
||||
"#
|
||||
|
@ -51,6 +52,64 @@ pub async fn home(
|
|||
|
||||
let mut out = Vec::<TimelineStatus>::new();
|
||||
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);
|
||||
out.push(TimelineStatus {
|
||||
id: record.post_id.clone(),
|
||||
|
@ -68,6 +127,7 @@ pub async fn home(
|
|||
favourites_count: 0,
|
||||
favourited: false,
|
||||
reblogged: false,
|
||||
reblog: boost,
|
||||
muted: false,
|
||||
bookmarked: false,
|
||||
media_attachments: vec![],
|
||||
|
|
|
@ -165,6 +165,7 @@ pub async fn statuses(
|
|||
favourites_count: 0,
|
||||
favourited: false,
|
||||
reblogged: false,
|
||||
reblog: None,
|
||||
muted: false,
|
||||
bookmarked: false,
|
||||
media_attachments: vec![],
|
||||
|
|
|
@ -99,6 +99,7 @@ pub async fn test(http: &State<HttpClient>) -> &'static str {
|
|||
ts: "2025-04-10T10:48:11Z".to_string(),
|
||||
to: vec!["https://ferri.amy.mov/users/amy/followers".to_string()],
|
||||
cc: vec!["https://www.w3.org/ns/activitystreams#Public".to_string()],
|
||||
attributed_to: None
|
||||
},
|
||||
ts: "2025-04-10T10:48:11Z".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::{
|
||||
Db,
|
||||
http::HttpClient,
|
||||
types::{Person, activity},
|
||||
types::{Person, content::Post, activity},
|
||||
};
|
||||
|
||||
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(
|
||||
activity: activity::CreateActivity,
|
||||
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();
|
||||
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 => {
|
||||
warn!(act, body, "unknown activity");
|
||||
}
|
||||
|
|
|
@ -107,6 +107,7 @@ pub async fn post(
|
|||
Json(content::Post {
|
||||
context: "https://www.w3.org/ns/activitystreams".to_string(),
|
||||
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(),
|
||||
content: post.content,
|
||||
ts: post.created_at,
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use rocket::serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::types::content::Post;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
@ -60,3 +59,17 @@ pub struct AcceptActivity {
|
|||
pub object: 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 to: 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 outbox: String,
|
||||
pub preferred_username: String,
|
||||
#[serde(default)]
|
||||
pub name: String,
|
||||
pub public_key: Option<UserKey>,
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ CREATE TABLE IF NOT EXISTS post
|
|||
user_id TEXT NOT NULL,
|
||||
content 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