Make User follow change of activitystreams

This commit is contained in:
Kitaiti Makoto 2022-01-30 00:22:01 +09:00
parent 00d31e323f
commit c37e115470

View File

@ -4,12 +4,15 @@ use crate::{
safe_string::SafeString, schema::users, timeline::Timeline, Connection, Error, Result,
UserEvent::*, CONFIG, ITEMS_PER_PAGE, USER_CHAN,
};
use activitypub::{
activity::Delete,
actor::Person,
use activitystreams::{
activity::{ActorAndObject, Create, Delete},
actor::{ApActor, Endpoints, Person},
base::{AnyBase, AsBase, Base},
collection::{OrderedCollection, OrderedCollectionPage},
object::{Image, Tombstone},
Activity, CustomObject, Endpoint,
iri,
object::{kind::ImageType, ApObject, Image, Tombstone},
prelude::*,
primitives::OneOrMany, // CustomObject,
};
use chrono::{NaiveDateTime, Utc};
use diesel::{self, BelongingToDsl, ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl};
@ -42,7 +45,7 @@ use std::{
use url::Url;
use webfinger::*;
pub type CustomPerson = CustomObject<ApSignature, Person>;
// pub type CustomPerson = CustomObject<ApSignature, Person>;
pub enum Role {
Admin = 0,
@ -242,13 +245,16 @@ impl User {
.ok_or(Error::Webfinger)
}
fn fetch(url: &str) -> Result<CustomPerson> {
// fn fetch(url: &str) -> Result<CustomPerson> {
fn fetch(url: &str) -> Result<Person> {
let mut res = get(url, Self::get_sender(), CONFIG.proxy().cloned())?;
let text = &res.text()?;
// without this workaround, publicKey is not correctly deserialized
let ap_sign = serde_json::from_str::<ApSignature>(text)?;
let mut json = serde_json::from_str::<CustomPerson>(text)?;
json.custom_props = ap_sign;
// TODO: Implement
// let ap_sign = serde_json::from_str::<ApSignature>(text)?;
// let mut json = serde_json::from_str::<CustomPerson>(text)?;
let json = serde_json::from_str::<Person>(text)?;
// json.custom_props = ap_sign; // TODO: implement
Ok(json)
}
@ -260,35 +266,69 @@ impl User {
User::fetch(&self.ap_url.clone()).and_then(|json| {
let avatar = Media::save_remote(
conn,
json.object
.object_props
.icon_image()? // FIXME: Fails when icon is not set
.object_props
.url_string()?,
json.icon()
.and_then(|icon| {
Some(
icon.as_one()
.expect("only")
.extend::<Image, ImageType>()
.expect("possible")
.expect("exists")
.url()
.ok_or(Error::MissingApProperty)
.ok()?
.as_one()
.expect("one")
.as_xsd_string()
.expect("possible"),
)
})
.ok_or(Error::MissingApProperty)?
.into(), // FIXME: Fails when icon is not set,
self,
)
.ok();
let person =
serde_json::from_value::<ApActor<Person>>(serde_json::to_value(json)?.into())?;
diesel::update(self)
.set((
users::username.eq(json.object.ap_actor_props.preferred_username_string()?),
users::display_name.eq(json.object.object_props.name_string()?),
users::outbox_url.eq(json.object.ap_actor_props.outbox_string()?),
users::inbox_url.eq(json.object.ap_actor_props.inbox_string()?),
users::username.eq(person
.preferred_username()
.ok_or(Error::MissingApProperty)?),
users::display_name.eq(person
.name()
.ok_or(Error::MissingApProperty)?
.as_one()
.expect("only")
.as_xsd_string()
.expect("possible")),
users::outbox_url.eq(person.outbox().ok_or(Error::MissingApProperty)?.as_str()),
users::inbox_url.eq(person.inbox().as_str()),
users::summary.eq(SafeString::new(
&json
.object
.object_props
.summary_string()
&person
.summary()
.and_then(|summary| {
Some(
summary
.as_one()
.expect("only")
.as_xsd_string()
.expect("possible"),
)
})
.unwrap_or_default(),
)),
users::followers_endpoint.eq(json.object.ap_actor_props.followers_string()?),
users::followers_endpoint
.eq(person.followers().ok_or(Error::MissingApProperty)?.as_str()),
users::avatar_id.eq(avatar.map(|a| a.id)),
users::last_fetched_date.eq(Utc::now().naive_utc()),
users::public_key.eq(json
.custom_props
.public_key_publickey()?
.public_key_pem_string()?),
// TODO:
// users::public_key.eq(json
// .custom_props
// .public_key_publickey()?
// .public_key_pem_string()?),
))
.execute(conn)
.map(|_| ())
@ -428,31 +468,32 @@ impl User {
.load::<User>(conn)
.map_err(Error::from)
}
pub fn outbox(&self, conn: &Connection) -> Result<ActivityStream<OrderedCollection>> {
Ok(ActivityStream::new(self.outbox_collection(conn)?))
pub fn outbox(&self, conn: &Connection) -> Result<ActivityStream<ApObject<OrderedCollection>>> {
Ok(ActivityStream::new(ApObject::new(
self.outbox_collection(conn)?,
)))
}
pub fn outbox_collection(&self, conn: &Connection) -> Result<OrderedCollection> {
let mut coll = OrderedCollection::default();
let mut coll = OrderedCollection::new();
let first = &format!("{}?page=1", &self.outbox_url);
let last = &format!(
"{}?page={}",
&self.outbox_url,
self.get_activities_count(conn) / i64::from(ITEMS_PER_PAGE) + 1
);
coll.collection_props.set_first_link(Id::new(first))?;
coll.collection_props.set_last_link(Id::new(last))?;
coll.collection_props
.set_total_items_u64(self.get_activities_count(conn) as u64)?;
coll.set_first(iri!(first))
.set_last(iri!(last))
.set_total_items(self.get_activities_count(conn) as u64);
Ok(coll)
}
pub fn outbox_page(
&self,
conn: &Connection,
(min, max): (i32, i32),
) -> Result<ActivityStream<OrderedCollectionPage>> {
Ok(ActivityStream::new(
) -> Result<ActivityStream<ApObject<OrderedCollectionPage>>> {
Ok(ActivityStream::new(ApObject::new(
self.outbox_collection_page(conn, (min, max))?,
))
)))
}
pub fn outbox_collection_page(
&self,
@ -461,27 +502,33 @@ impl User {
) -> Result<OrderedCollectionPage> {
let acts = self.get_activities_page(conn, (min, max))?;
let n_acts = self.get_activities_count(conn);
let mut coll = OrderedCollectionPage::default();
let mut coll = OrderedCollectionPage::new();
if n_acts - i64::from(min) >= i64::from(ITEMS_PER_PAGE) {
coll.collection_page_props.set_next_link(Id::new(&format!(
coll.set_next(iri!(&format!(
"{}?page={}",
&self.outbox_url,
min / ITEMS_PER_PAGE + 2
)))?;
)));
}
if min > 0 {
coll.collection_page_props.set_prev_link(Id::new(&format!(
coll.set_prev(iri!(&format!(
"{}?page={}",
&self.outbox_url,
min / ITEMS_PER_PAGE
)))?;
)));
}
coll.collection_props.items = serde_json::to_value(acts)?;
coll.collection_page_props
.set_part_of_link(Id::new(&self.outbox_url))?;
coll.set_many_items(
acts.iter().map(|create| {
AnyBase::from_base(create.base_ref().into_generic().expect("possible"))
}),
)
.set_part_of(iri!(&self.outbox_url));
Ok(coll)
}
fn fetch_outbox_page<T: Activity>(&self, url: &str) -> Result<(Vec<T>, Option<String>)> {
fn fetch_outbox_page<T: serde::de::DeserializeOwned>(
&self,
url: &str,
) -> Result<(Vec<ActorAndObject<T>>, Option<String>)> {
let mut res = get(url, Self::get_sender(), CONFIG.proxy().cloned())?;
let text = &res.text()?;
let json: serde_json::Value = serde_json::from_str(text)?;
@ -490,12 +537,12 @@ impl User {
.unwrap_or(&vec![])
.iter()
.filter_map(|j| serde_json::from_value(j.clone()).ok())
.collect::<Vec<T>>();
.collect::<Vec<ActorAndObject<T>>>();
let next = json.get("next").map(|x| x.as_str().unwrap().to_owned());
Ok((items, next))
}
pub fn fetch_outbox<T: Activity>(&self) -> Result<Vec<T>> {
pub fn fetch_outbox<T: serde::de::DeserializeOwned>(&self) -> Result<Vec<ActorAndObject<T>>> {
let mut res = get(
&self.outbox_url[..],
Self::get_sender(),
@ -504,7 +551,7 @@ impl User {
let text = &res.text()?;
let json: serde_json::Value = serde_json::from_str(text)?;
if let Some(first) = json.get("first") {
let mut items: Vec<T> = Vec::new();
let mut items: Vec<ActorAndObject<T>> = Vec::new();
let mut next = first.as_str().unwrap().to_owned();
while let Ok((mut page, nxt)) = self.fetch_outbox_page(&next) {
if page.is_empty() {
@ -527,7 +574,7 @@ impl User {
.unwrap_or(&vec![])
.iter()
.filter_map(|j| serde_json::from_value(j.clone()).ok())
.collect::<Vec<T>>())
.collect::<Vec<ActorAndObject<T>>>())
}
}
@ -561,7 +608,7 @@ impl User {
&self,
conn: &Connection,
(min, max): (i32, i32),
) -> Result<Vec<serde_json::Value>> {
) -> Result<Vec<Create>> {
use crate::schema::post_authors;
use crate::schema::posts;
let posts_by_self = PostAuthor::belonging_to(self).select(post_authors::post_id);
@ -574,12 +621,8 @@ impl User {
.load::<Post>(conn)?;
Ok(posts
.into_iter()
.filter_map(|p| {
p.create_activity(conn)
.ok()
.and_then(|a| serde_json::to_value(a).ok())
})
.collect::<Vec<serde_json::Value>>())
.filter_map(|p| p.create_activity(conn).ok())
.collect())
}
pub fn get_followers(&self, conn: &Connection) -> Result<Vec<User>> {
@ -739,72 +782,57 @@ impl User {
}
}
pub fn to_activity(&self, conn: &Connection) -> Result<CustomPerson> {
let mut actor = Person::default();
actor.object_props.set_id_string(self.ap_url.clone())?;
actor
.object_props
.set_name_string(self.display_name.clone())?;
actor
.object_props
.set_summary_string(self.summary_html.get().clone())?;
actor.object_props.set_url_string(self.ap_url.clone())?;
actor
.ap_actor_props
.set_inbox_string(self.inbox_url.clone())?;
actor
.ap_actor_props
.set_outbox_string(self.outbox_url.clone())?;
actor
.ap_actor_props
.set_preferred_username_string(self.username.clone())?;
actor
.ap_actor_props
.set_followers_string(self.followers_endpoint.clone())?;
// pub fn to_activity(&self, conn: &Connection) -> Result<CustomPerson> {
pub fn to_activity(&self, conn: &Connection) -> Result<ApActor<Person>> {
let mut actor = ApActor::new(
iri!(self.inbox_url),
*Person::new()
.set_name(self.display_name)
.set_summary(self.summary_html.get().to_owned())
.set_url(self.ap_url),
)
.set_outbox(iri!(self.outbox_url))
.set_preferred_username(self.username)
.set_followers(iri!(self.followers_endpoint));
if let Some(shared_inbox_url) = self.shared_inbox_url.clone() {
let mut endpoints = Endpoint::default();
endpoints.set_shared_inbox_string(shared_inbox_url)?;
actor.ap_actor_props.set_endpoints_endpoint(endpoints)?;
let endpoints = Endpoints {
shared_inbox: Some(iri!(shared_inbox_url)),
..Endpoints::default()
};
actor.set_endpoints(endpoints);
}
let mut public_key = PublicKey::default();
public_key.set_id_string(format!("{}#main-key", self.ap_url))?;
public_key.set_owner_string(self.ap_url.clone())?;
public_key.set_public_key_pem_string(self.public_key.clone())?;
let mut ap_signature = ApSignature::default();
ap_signature.set_public_key_publickey(public_key)?;
// FIXME
// let mut public_key = PublicKey::default();
// public_key.set_id_string(format!("{}#main-key", self.ap_url))?;
// public_key.set_owner_string(self.ap_url.clone())?;
// public_key.set_public_key_pem_string(self.public_key.clone())?;
// let mut ap_signature = ApSignature::default();
// ap_signature.set_public_key_publickey(public_key)?;
if let Some(avatar_id) = self.avatar_id {
let mut avatar = Image::default();
avatar
.object_props
.set_url_string(Media::get(conn, avatar_id)?.url()?)?;
actor.object_props.set_icon_object(avatar)?;
let avatar = Image::new().set_url(iri!(Media::get(conn, avatar_id)?.url()?));
let base = Base::retract(*avatar)?.into_generic()?;
actor.set_icon(AnyBase::from_base(base));
}
Ok(CustomPerson::new(actor, ap_signature))
// Ok(CustomPerson::new(actor, ap_signature))
Ok(*actor)
}
pub fn delete_activity(&self, conn: &Connection) -> Result<Delete> {
let mut del = Delete::default();
let mut tombstone = Tombstone::default();
tombstone.object_props.set_id_string(self.ap_url.clone())?;
del.delete_props
.set_actor_link(Id::new(self.ap_url.clone()))?;
del.delete_props.set_object_object(tombstone)?;
del.object_props
.set_id_string(format!("{}#delete", self.ap_url))?;
del.object_props
.set_to_link_vec(vec![Id::new(PUBLIC_VISIBILITY)])?;
del.object_props.set_cc_link_vec(
self.get_followers(conn)?
.into_iter()
.map(|f| Id::new(f.ap_url))
.collect(),
)?;
let followers = self.get_followers(conn)?;
let ccs = Vec::with_capacity(followers.len());
for addr in followers.into_iter() {
ccs.push(iri!(addr.ap_url));
}
let tombstone = Tombstone::new()
.set_id(iri!(format!("{}#delete", self.ap_url)))
.set_many_tos([iri!(PUBLIC_VISIBILITY)])
.set_many_ccs(ccs);
let base = Base::retract(*tombstone)?.into_generic()?;
let del = Delete::new::<_, OneOrMany<AnyBase>>(iri!(self.ap_url), base.into());
Ok(del)
}
@ -923,14 +951,16 @@ impl Eq for User {}
impl FromId<DbConn> for User {
type Error = Error;
type Object = CustomPerson;
// type Object = CustomPerson;
type Object = Person;
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
Self::find_by_ap_url(conn, id)
}
fn from_activity(conn: &DbConn, acct: CustomPerson) -> Result<Self> {
let url = Url::parse(&acct.object.object_props.id_string()?)?;
// fn from_activity(conn: &DbConn, acct: CustomPerson) -> Result<Self> {
fn from_activity(conn: &DbConn, acct: Person) -> Result<Self> {
let url = Url::parse(&acct.id().ok_or(Error::MissingApProperty)?.as_str())?;
let inst = url.host_str().ok_or(Error::Url)?;
let instance = Instance::find_by_domain(conn, inst).or_else(|_| {
Instance::insert(
@ -949,8 +979,12 @@ impl FromId<DbConn> for User {
},
)
})?;
let person = serde_json::from_value::<ApActor<Person>>(serde_json::to_value(acct)?.into())?;
let username = acct.object.ap_actor_props.preferred_username_string()?;
let username = person
.preferred_username()
.ok_or(Error::MissingApProperty)?
.to_string();
if username.contains(&['<', '>', '&', '@', '\'', '"', ' ', '\t'][..]) {
return Err(Error::InvalidValue);
@ -966,50 +1000,77 @@ impl FromId<DbConn> for User {
conn,
NewUser {
display_name: acct
.object
.object_props
.name_string()
.unwrap_or_else(|_| username.clone()),
.name()
.map(|name| {
name.as_one()
.expect("only")
.as_xsd_string()
.expect("exists")
})
.unwrap_or_else(|| &username)
.to_string(),
username,
outbox_url: acct.object.ap_actor_props.outbox_string()?,
inbox_url: acct.object.ap_actor_props.inbox_string()?,
outbox_url: person.outbox().ok_or(Error::MissingApProperty)?.to_string(),
inbox_url: person.inbox().to_string(),
role: 2,
summary: acct
.object
.object_props
.summary_string()
.unwrap_or_default(),
summary: person
.summary()
.map(|summary| {
summary
.as_one()
.expect("only")
.as_xsd_string()
.expect("exists")
})
.unwrap_or_default()
.to_string(),
summary_html: SafeString::new(
&acct
.object
.object_props
.summary_string()
&person
.summary()
.map(|summary| {
summary
.as_one()
.expect("only")
.as_xsd_string()
.expect("exists")
})
.unwrap_or_default(),
),
email: None,
hashed_password: None,
instance_id: instance.id,
ap_url: acct.object.object_props.id_string()?,
public_key: acct
.custom_props
.public_key_publickey()?
.public_key_pem_string()?,
ap_url: person.id().ok_or(Error::MissingApProperty)?.to_string(),
// public_key: acct
// .custom_props
// .public_key_publickey()?
// .public_key_pem_string()?,
public_key: "".to_string(), // FIXME
private_key: None,
shared_inbox_url: acct
.object
.ap_actor_props
.endpoints_endpoint()
.and_then(|e| e.shared_inbox_string())
.ok(),
followers_endpoint: acct.object.ap_actor_props.followers_string()?,
shared_inbox_url: person
.endpoints()
.and_then(|e| e.shared_inbox.map(|shared_inbox| shared_inbox.to_string())),
followers_endpoint: person
.followers()
.ok_or(Error::MissingApProperty)?
.to_string(),
fqn,
avatar_id: None,
},
)?;
if let Ok(icon) = acct.object.object_props.icon_image() {
if let Ok(url) = icon.object_props.url_string() {
let avatar = Media::save_remote(conn, url, &user);
if let Some(icon) = acct.icon() {
let icon_image = icon
.as_one()
.expect("only")
.extend::<Image, ImageType>()
.expect("possible")
.expect("exists");
if let Some(url) = icon_image.url() {
let avatar = Media::save_remote(
conn,
url.as_single_xsd_string().expect("exists").into(),
&user,
);
if let Ok(avatar) = avatar {
user.set_avatar(conn, avatar.id)?;