Skip to content

Commit

Permalink
feat: receive Announce activity (#25)
Browse files Browse the repository at this point in the history
* feat: received announce migration

* feat: received announce schema

* feat(apub/like): support announce

* refactor(apub/activities): remove useless struct

* feat(api_mastodon): implement reblogged_by

* chore(api_mastodon): update openapi

* refactor(apub): move like_or_announce type to mod

* chore(apub): add activities test

* fix: format code

* docs: update federation
  • Loading branch information
kwaa authored May 3, 2024
1 parent 41b3fec commit ac1b6ad
Show file tree
Hide file tree
Showing 29 changed files with 501 additions and 131 deletions.
2 changes: 1 addition & 1 deletion FEDERATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ The following activities and object types are supported:
- `Follow(Actor)`, `Undo(Follow)`
- `Create(Note)`
- `Like(Note)`, `Undo(Like)`
- `Announce(Note)`, `Undo(Announce)`

<!-- - `Create(Note)`, `Update(Note)`, `Delete(Note)` -->
<!-- - `Announce(Note)`, `Undo(Announce)` -->

Activities are implemented in way that is compatible with Mastodon and other
popular ActivityPub servers.
Expand Down
4 changes: 2 additions & 2 deletions crates/api_apub/src/users/user_inbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ use activitypub_federation::{
protocol::context::WithContext,
};
use axum::{debug_handler, response::IntoResponse};
use hatsu_apub::{activities::ServiceInboxActivities, actors::ApubUser};
use hatsu_apub::{activities::UserInboxActivities, actors::ApubUser};
use hatsu_utils::AppData;

#[debug_handler]
pub async fn handler(data: Data<AppData>, activity_data: ActivityData) -> impl IntoResponse {
receive_activity::<WithContext<ServiceInboxActivities>, ApubUser, AppData>(activity_data, &data)
receive_activity::<WithContext<UserInboxActivities>, ApubUser, AppData>(activity_data, &data)
.await
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ use sea_orm::{EntityTrait, ModelTrait};

use crate::entities::Account;

// (status = NOT_FOUND, description = "Status does not exist or is private", body = AppError)
// { "error": "Record not found" }

/// See who favourited a status
///
/// <https://docs.joinmastodon.org/methods/statuses/#favourited_by>
Expand All @@ -18,6 +15,7 @@ use crate::entities::Account;
path = "/api/v1/statuses/{id}/favourited_by",
responses(
(status = OK, description = "A list of accounts who favourited the status", body = Vec<Account>),
(status = NOT_FOUND, description = "Status does not exist or is private", body = AppError),
),
params(
("id" = String, Path, description = "The ID of the Status in the database.")
Expand Down
46 changes: 40 additions & 6 deletions crates/api_mastodon/src/routes/statuses/status_reblogged_by.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use activitypub_federation::config::Data;
use axum::{debug_handler, Json};
use axum::{debug_handler, extract::Path, Json};
use hatsu_db_schema::prelude::{Post, ReceivedAnnounce};
use hatsu_utils::{AppData, AppError};
use sea_orm::{EntityTrait, ModelTrait};

use crate::entities::Account;

// (status = NOT_FOUND, description = "Status does not exist or is private", body = AppError)
// { "error": "Record not found" }

/// See who boosted a status
///
/// <https://docs.joinmastodon.org/methods/statuses/#reblogged_by>
Expand All @@ -16,12 +15,47 @@ use crate::entities::Account;
path = "/api/v1/statuses/{id}/reblogged_by",
responses(
(status = OK, description = "A list of accounts that boosted the status", body = Vec<Account>),
(status = NOT_FOUND, description = "Status does not exist or is private", body = AppError),
),
params(
("id" = String, Path, description = "The ID of the Status in the database.")
)
)]
#[debug_handler]
pub async fn status_reblogged_by(data: Data<AppData>) -> Result<Json<Vec<Account>>, AppError> {
Ok(Json(vec![Account::primary_account(&data).await?]))
pub async fn status_reblogged_by(
Path(base64_url): Path<String>,
data: Data<AppData>,
) -> Result<Json<Vec<Account>>, AppError> {
let base64 = base64_simd::URL_SAFE;

match base64.decode_to_vec(&base64_url) {
Ok(utf8_url) => match String::from_utf8(utf8_url) {
Ok(url) if url.starts_with("https://") => {
let post_url = hatsu_utils::url::generate_post_url(data.domain(), url)?;

match Post::find_by_id(&post_url.to_string())
.one(&data.conn)
.await?
{
Some(post) => {
let handles = post
.find_related(ReceivedAnnounce)
.all(&data.conn)
.await
.unwrap()
.into_iter()
.map(|received_like| async {
Account::from_id(received_like.actor, &data).await.unwrap()
})
.collect::<Vec<_>>();

Ok(Json(futures::future::join_all(handles).await))
},
_ => Err(AppError::not_found("Record", &base64_url)),
}
},
_ => Err(AppError::not_found("Record", &base64_url)),
},
_ => Err(AppError::not_found("Record", &base64_url)),
}
}
62 changes: 62 additions & 0 deletions crates/apub/assets/mastodon/activities/create_note.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"ostatus": "http://ostatus.org#",
"atomUri": "ostatus:atomUri"
}
],
"id": "https://mastodon.madrid/users/felix/statuses/107224289116410645/activity",
"type": "Create",
"actor": "https://mastodon.madrid/users/felix",
"published": "2021-11-05T11:46:50Z",
"to": [
"https://mastodon.madrid/users/felix/followers"
],
"cc": [
"https://www.w3.org/ns/activitystreams#Public",
"https://mamot.fr/users/retiolus"
],
"object": {
"id": "https://mastodon.madrid/users/felix/statuses/107224289116410645",
"type": "Note",
"summary": null,
"inReplyTo": "https://mamot.fr/users/retiolus/statuses/107224244380204526",
"published": "2021-11-05T11:46:50Z",
"url": "https://mastodon.madrid/@felix/107224289116410645",
"attributedTo": "https://mastodon.madrid/users/felix",
"to": [
"https://mastodon.madrid/users/felix/followers"
],
"cc": [
"https://www.w3.org/ns/activitystreams#Public",
"https://mamot.fr/users/retiolus"
],
"sensitive": false,
"atomUri": "https://mastodon.madrid/users/felix/statuses/107224289116410645",
"inReplyToAtomUri": "https://mamot.fr/users/retiolus/statuses/107224244380204526",
"conversation": "tag:mamot.fr,2021-11-05:objectId=64635960:objectType=Conversation",
"content": "<p><span class=\"h-card\"><a href=\"https://mamot.fr/@retiolus\" class=\"u-url mention\">@<span>retiolus</span></a></span> i have never been disappointed by a thinkpad. if you want to save money, get a model from a few years ago, there isnt a huge difference anyway.</p>",
"contentMap": {
"en": "<p><span class=\"h-card\"><a href=\"https://mamot.fr/@retiolus\" class=\"u-url mention\">@<span>retiolus</span></a></span> i have neverbeendisappointed by a thinkpad. if you want to save money, get a model from a few years ago, there isnt a huge difference anyway.</p>"
},
"attachment": [],
"tag": [
{
"type": "Mention",
"href": "https://mamot.fr/users/retiolus",
"name": "@[email protected]"
}
],
"replies": {
"id": "https://mastodon.madrid/users/felix/statuses/107224289116410645/replies",
"type": "Collection",
"first": {
"type": "CollectionPage",
"next": "https://mastodon.madrid/users/felix/statuses/107224289116410645/replies?only_other_accounts=true&page=true",
"partOf": "https://mastodon.madrid/users/felix/statuses/107224289116410645/replies",
"items": []
}
}
}
}
7 changes: 7 additions & 0 deletions crates/apub/assets/mastodon/activities/follow.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://masto.asonix.dog/1ea87517-63c5-4118-8831-460ee641b2cf",
"type": "Follow",
"actor": "https://masto.asonix.dog/users/asonix",
"object": "https://ds9.lemmy.ml/c/testcom"
}
7 changes: 7 additions & 0 deletions crates/apub/assets/mastodon/activities/like_page.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://mastodon.madrid/users/felix#likes/212340",
"type": "Like",
"actor": "https://mastodon.madrid/users/felix",
"object": "https://ds9.lemmy.ml/post/147"
}
12 changes: 12 additions & 0 deletions crates/apub/assets/mastodon/activities/undo_follow.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://masto.asonix.dog/users/asonix#follows/449/undo",
"type": "Undo",
"actor": "https://masto.asonix.dog/users/asonix",
"object": {
"id": "https://masto.asonix.dog/1ea87517-63c5-4118-8831-460ee641b2cf",
"type": "Follow",
"actor": "https://masto.asonix.dog/users/asonix",
"object": "https://ds9.lemmy.ml/c/testcom"
}
}
12 changes: 12 additions & 0 deletions crates/apub/assets/mastodon/activities/undo_like_page.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://mastodon.madrid/users/felix#likes/212341/undo",
"type": "Undo",
"actor": "https://mastodon.madrid/users/felix",
"object": {
"id": "https://mastodon.madrid/users/felix#likes/212341",
"type": "Like",
"actor": "https://mastodon.madrid/users/felix",
"object": "https://ds9.lemmy.ml/post/147"
}
}
27 changes: 11 additions & 16 deletions crates/apub/src/activities/activity_lists.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,23 @@ use activitypub_federation::{config::Data, traits::ActivityHandler};
use serde::{Deserialize, Serialize};
use url::Url;

use crate::activities::{AcceptFollow, CreateOrUpdateNote, Follow, Like, UndoFollow, UndoLike};
use crate::activities::{
AcceptFollow,
CreateOrUpdateNote,
Follow,
LikeOrAnnounce,
UndoFollow,
UndoLikeOrAnnounce,
};

#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
#[enum_delegate::implement(ActivityHandler)]
pub enum SharedInboxActivities {
pub enum UserInboxActivities {
CreateOrUpdateNote(CreateOrUpdateNote),
Follow(Follow),
AcceptFollow(AcceptFollow),
UndoFollow(UndoFollow),
Like(Like),
UndoLike(UndoLike),
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
#[enum_delegate::implement(ActivityHandler)]
pub enum ServiceInboxActivities {
CreateOrUpdateNote(CreateOrUpdateNote),
Follow(Follow),
AcceptFollow(AcceptFollow),
UndoFollow(UndoFollow),
Like(Like),
UndoLike(UndoLike),
LikeOrAnnounce(LikeOrAnnounce),
UndoLikeOrAnnounce(UndoLikeOrAnnounce),
}
77 changes: 0 additions & 77 deletions crates/apub/src/activities/like/like.rs

This file was deleted.

9 changes: 0 additions & 9 deletions crates/apub/src/activities/like/mod.rs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use std::ops::Deref;

use hatsu_db_schema::received_announce::Model as DbReceivedAnnounce;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ApubReceivedAnnounce(pub(crate) DbReceivedAnnounce);

impl AsRef<DbReceivedAnnounce> for ApubReceivedAnnounce {
fn as_ref(&self) -> &DbReceivedAnnounce {
&self.0
}
}

impl Deref for ApubReceivedAnnounce {
type Target = DbReceivedAnnounce;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl From<DbReceivedAnnounce> for ApubReceivedAnnounce {
fn from(u: DbReceivedAnnounce) -> Self {
Self(u)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use activitypub_federation::kinds::activity::AnnounceType;
use hatsu_db_schema::received_announce::Model as DbReceivedAnnounce;
use hatsu_utils::AppError;
use url::Url;

use crate::activities::{ApubReceivedAnnounce, LikeOrAnnounce, LikeOrAnnounceType};

impl ApubReceivedAnnounce {
pub fn into_json(self) -> Result<LikeOrAnnounce, AppError> {
Ok(LikeOrAnnounce {
kind: LikeOrAnnounceType::AnnounceType(AnnounceType::Announce),
id: Url::parse(&self.id)?,
actor: Url::parse(&self.actor)?.into(),
object: Url::parse(&self.object)?.into(),
})
}

pub fn from_json(json: &LikeOrAnnounce) -> Result<Self, AppError> {
Ok(DbReceivedAnnounce {
id: json.id.to_string(),
actor: json.actor.to_string(),
object: json.object.to_string(),
}
.into())
}
}
Loading

0 comments on commit ac1b6ad

Please sign in to comment.