Plume/src/activity_pub/inbox.rs

164 lines
6.1 KiB
Rust
Raw Normal View History

2018-05-18 10:04:40 +02:00
use activitystreams_traits::Actor;
2018-05-16 20:20:44 +02:00
use activitystreams_types::{
actor::Person,
2018-05-23 19:09:59 +02:00
activity::{Accept, Announce, Create, Follow, Like, Undo},
2018-05-16 20:20:44 +02:00
object::{Article, Note}
};
2018-05-01 16:00:29 +02:00
use diesel::PgConnection;
2018-05-16 20:20:44 +02:00
use failure::Error;
2018-05-01 16:00:29 +02:00
use serde_json;
2018-05-19 09:39:59 +02:00
use activity_pub::{
broadcast, Id, IntoId,
actor::Actor as APActor,
sign::*
};
use models::{
blogs::Blog,
comments::*,
follows,
likes,
posts::*,
2018-05-23 19:09:59 +02:00
reshares::*,
2018-05-19 09:39:59 +02:00
users::User
};
2018-05-01 16:00:29 +02:00
2018-05-16 20:20:44 +02:00
#[derive(Fail, Debug)]
enum InboxError {
#[fail(display = "The `type` property is required, but was not present")]
NoType,
#[fail(display = "Invalid activity type")]
InvalidType,
#[fail(display = "Couldn't undo activity")]
CantUndo
}
2018-05-13 19:39:18 +02:00
pub trait Inbox {
2018-05-01 16:00:29 +02:00
fn received(&self, conn: &PgConnection, act: serde_json::Value);
2018-05-16 20:20:44 +02:00
fn new_article(&self, conn: &PgConnection, article: Article) -> Result<(), Error> {
Post::insert(conn, NewPost {
blog_id: 0, // TODO
slug: String::from(""), // TODO
title: article.object_props.name_string().unwrap(),
content: article.object_props.content_string().unwrap(),
published: true,
license: String::from("CC-0"),
ap_url: article.object_props.url_string()?
});
Ok(())
}
fn new_comment(&self, conn: &PgConnection, note: Note, actor_id: String) -> Result<(), Error> {
let previous_url = note.object_props.in_reply_to.clone().unwrap().as_str().unwrap().to_string();
let previous_comment = Comment::find_by_ap_url(conn, previous_url.clone());
Comment::insert(conn, NewComment {
content: note.object_props.content_string().unwrap(),
spoiler_text: note.object_props.summary_string().unwrap(),
ap_url: note.object_props.id_string().ok(),
in_response_to_id: previous_comment.clone().map(|c| c.id),
post_id: previous_comment
.map(|c| c.post_id)
.unwrap_or_else(|| Post::find_by_ap_url(conn, previous_url).unwrap().id),
author_id: User::from_url(conn, actor_id).unwrap().id,
sensitive: false // "sensitive" is not a standard property, we need to think about how to support it with the activitystreams crate
});
Ok(())
}
fn follow(&self, conn: &PgConnection, follow: Follow) -> Result<(), Error> {
let from = User::from_url(conn, follow.actor.as_str().unwrap().to_string()).unwrap();
match User::from_url(conn, follow.object.as_str().unwrap().to_string()) {
2018-05-18 10:04:40 +02:00
Some(u) => self.accept_follow(conn, &from, &u, follow, from.id, u.id),
2018-05-16 20:20:44 +02:00
None => {
let blog = Blog::from_url(conn, follow.object.as_str().unwrap().to_string()).unwrap();
2018-05-18 10:04:40 +02:00
self.accept_follow(conn, &from, &blog, follow, from.id, blog.id)
2018-05-01 20:02:29 +02:00
}
2018-05-16 20:20:44 +02:00
};
Ok(())
}
fn like(&self, conn: &PgConnection, like: Like) -> Result<(), Error> {
let liker = User::from_url(conn, like.actor.as_str().unwrap().to_string());
let post = Post::find_by_ap_url(conn, like.object.as_str().unwrap().to_string());
likes::Like::insert(conn, likes::NewLike {
post_id: post.unwrap().id,
user_id: liker.unwrap().id,
ap_url: like.object_props.id_string()?
});
Ok(())
}
fn unlike(&self, conn: &PgConnection, undo: Undo) -> Result<(), Error> {
let like = likes::Like::find_by_ap_url(conn, undo.object_object::<Like>()?.object_props.id_string()?).unwrap();
like.delete(conn);
Ok(())
}
2018-05-23 19:09:59 +02:00
fn announce(&self, conn: &PgConnection, announce: Announce) -> Result<(), Error> {
let user = User::from_url(conn, announce.actor.as_str().unwrap().to_string());
let post = Post::find_by_ap_url(conn, announce.object.as_str().unwrap().to_string());
Reshare::insert(conn, NewReshare {
post_id: post.unwrap().id,
user_id: user.unwrap().id,
ap_url: announce.object_props.id_string()?
});
Ok(())
}
2018-05-16 20:20:44 +02:00
fn save(&self, conn: &PgConnection, act: serde_json::Value) -> Result<(), Error> {
match act["type"].as_str() {
Some(t) => {
match t {
2018-05-23 19:09:59 +02:00
"Announce" => self.announce(conn, serde_json::from_value(act.clone())?),
2018-05-16 20:20:44 +02:00
"Create" => {
let act: Create = serde_json::from_value(act.clone())?;
match act.object["type"].as_str().unwrap() {
2018-05-24 13:20:11 +02:00
"Article" => self.new_article(conn, act.object_object()?),
"Note" => self.new_comment(conn, act.object_object()?, act.actor_object::<Person>()?.object_props.id_string()?),
2018-05-16 20:20:44 +02:00
_ => Err(InboxError::InvalidType)?
}
},
"Follow" => self.follow(conn, serde_json::from_value(act.clone())?),
"Like" => self.like(conn, serde_json::from_value(act.clone())?),
"Undo" => {
let act: Undo = serde_json::from_value(act.clone())?;
match act.object["type"].as_str().unwrap() {
"Like" => self.unlike(conn, act),
_ => Err(InboxError::CantUndo)?
}
2018-05-13 12:44:05 +02:00
}
2018-05-16 20:20:44 +02:00
_ => Err(InboxError::InvalidType)?
2018-05-13 12:44:05 +02:00
}
},
2018-05-16 20:20:44 +02:00
None => Err(InboxError::NoType)?
2018-05-01 16:00:29 +02:00
}
}
2018-05-01 20:02:29 +02:00
2018-05-19 00:04:30 +02:00
fn accept_follow<A: Signer + IntoId + Clone, B: Clone + WithInbox + Actor>(
&self,
2018-05-18 10:04:40 +02:00
conn: &PgConnection,
from: &A,
target: &B,
follow: Follow,
from_id: i32,
target_id: i32
) {
2018-05-18 10:04:40 +02:00
follows::Follow::insert(conn, follows::NewFollow {
follower_id: from_id,
following_id: target_id
});
2018-05-01 20:02:29 +02:00
2018-05-19 00:04:30 +02:00
let mut accept = Accept::default();
accept.set_actor_link::<Id>(from.clone().into_id()).unwrap();
2018-05-18 10:04:40 +02:00
accept.set_object_object(follow).unwrap();
broadcast(conn, &*from, accept, vec![target.clone()]);
2018-05-01 20:02:29 +02:00
}
2018-05-01 16:00:29 +02:00
}
2018-05-18 10:04:40 +02:00
pub trait WithInbox {
fn get_inbox_url(&self) -> String;
fn get_shared_inbox_url(&self) -> Option<String>;
}