148 lines
3.2 KiB
Rust
148 lines
3.2 KiB
Rust
//! ActivityPub implementation code and related abstractions.
|
|
|
|
use futures::prelude::*;
|
|
use serde::Serialize;
|
|
|
|
use crate::{ Id, Activity, err, Result, Error, sign, ctx::Context, db::{ Post, Privacy } };
|
|
|
|
/// Represents the creation of an object.
|
|
///
|
|
/// Valid object types: [`Note`].
|
|
#[derive(Clone, Serialize)]
|
|
pub enum Create {
|
|
Note {
|
|
id: Id,
|
|
actor: Id,
|
|
object: Note,
|
|
}
|
|
}
|
|
|
|
impl TryFrom<Create> for Post {
|
|
type Error = Error;
|
|
|
|
fn try_from (value: Create) -> Result<Post> {
|
|
let privacy = Privacy::infer(&value);
|
|
match value {
|
|
Create::Note { id: create, object, actor } => {
|
|
let Note { content, id, .. } = object;
|
|
Ok (Post {
|
|
author: actor,
|
|
content,
|
|
privacy,
|
|
create,
|
|
id,
|
|
})
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub enum Invalid {}
|
|
|
|
/// A follow request.
|
|
///
|
|
/// Valid object types: [`Actor`].
|
|
#[derive(Clone, Serialize)]
|
|
pub enum Follow {
|
|
Actor {
|
|
id: Id,
|
|
object: Actor,
|
|
}
|
|
}
|
|
|
|
/// Signal that a [follow request][Follow] has been accepted.
|
|
///
|
|
/// Valid object types: [`Follow`].
|
|
#[derive(Clone, Serialize)]
|
|
pub enum Accept {
|
|
Follow {
|
|
id: Id,
|
|
object: Follow,
|
|
}
|
|
}
|
|
|
|
/// An entity that publishes activities.
|
|
#[derive(Clone, Serialize, sqlx::FromRow, sqlx::Type)]
|
|
pub struct Actor {
|
|
id: Id,
|
|
}
|
|
|
|
/// Represents a [`Post`] for federation purposes.
|
|
#[derive(Clone, Serialize)]
|
|
pub struct Note {
|
|
id: Id,
|
|
content: Option<String>,
|
|
}
|
|
|
|
impl From<Post> for Note {
|
|
fn from (Post { id, content, .. }: Post) -> Note {
|
|
Note { id, content }
|
|
}
|
|
}
|
|
|
|
impl Activity {
|
|
|
|
/// Deliver the activity to all its targets through the ActivityPub
|
|
/// delivery mechanism.
|
|
pub async fn deliver <S> (self, signer: &S) -> Result<()>
|
|
where
|
|
S: sign::Sign + ?Sized,
|
|
{
|
|
// Create a shared client #efficiency
|
|
let client = reqwest::Client::new();
|
|
|
|
// the function that does the delivery to a target. It creates
|
|
// a request with the proper headers and signs it using the
|
|
// `signer`.
|
|
let do_delivery = |url| async {
|
|
let req = {
|
|
let mut r = client.get(url).build()?;
|
|
signer.sign(&mut r)?;
|
|
r
|
|
};
|
|
client
|
|
.execute(req)
|
|
.map_err(err)
|
|
.await
|
|
};
|
|
|
|
// Collect only the errors, since we don't need to do anything
|
|
// with a successful delivery.
|
|
let errors = self
|
|
.delivery_targets()
|
|
.await?
|
|
.into_iter()
|
|
.map(do_delivery)
|
|
.collect::<stream::FuturesUnordered<_>>()
|
|
.filter_map(|r: Result<_>| async {
|
|
r.err().map(err)
|
|
})
|
|
.collect::<Vec<Error>>()
|
|
.await;
|
|
|
|
for err in errors {
|
|
// Failure to deliver is not a fatal error per se,
|
|
// so we log and move on.
|
|
println!("Failed to deliver activity: {:?}", err);
|
|
}
|
|
|
|
Ok (())
|
|
}
|
|
|
|
/// Get all delivery targets as urls.
|
|
async fn delivery_targets (&self) -> Result<Vec<reqwest::Url>> {
|
|
let unId = |Id (x)| x;
|
|
Ok (self.to().chain(self.cc()).map(unId).collect())
|
|
}
|
|
|
|
/// Perform the activity.
|
|
pub async fn perform <S> (self, _: &mut Context<S>) -> Result<()>
|
|
where
|
|
S: sign::Sign
|
|
{
|
|
todo!()
|
|
}
|
|
|
|
}
|