Mention all subscribers when creating subscribers-only post

This commit is contained in:
silverpill 2022-09-11 14:24:50 +00:00
parent 5122fe2b78
commit 9f3b95c41d
5 changed files with 27 additions and 42 deletions

View file

@ -20,7 +20,6 @@ use crate::errors::DatabaseError;
use crate::frontend::get_tag_page_url; use crate::frontend::get_tag_page_url;
use crate::models::posts::queries::get_post_author; use crate::models::posts::queries::get_post_author;
use crate::models::posts::types::{Post, Visibility}; use crate::models::posts::types::{Post, Visibility};
use crate::models::profiles::types::DbActorProfile;
use crate::models::relationships::queries::{get_followers, get_subscribers}; use crate::models::relationships::queries::{get_followers, get_subscribers};
use crate::models::users::types::User; use crate::models::users::types::User;
use crate::utils::files::get_file_url; use crate::utils::files::get_file_url;
@ -59,7 +58,6 @@ pub fn build_note(
instance_host: &str, instance_host: &str,
instance_url: &str, instance_url: &str,
post: &Post, post: &Post,
subscribers: Vec<DbActorProfile>,
) -> Note { ) -> Note {
let object_id = local_object_id(instance_url, &post.id); let object_id = local_object_id(instance_url, &post.id);
let actor_id = local_actor_id(instance_url, &post.author.username); let actor_id = local_actor_id(instance_url, &post.author.username);
@ -95,13 +93,7 @@ pub fn build_note(
}; };
let mut tags = vec![]; let mut tags = vec![];
let mut mentions = post.mentions.clone(); for profile in &post.mentions {
if post.visibility == Visibility::Subscribers {
// Mention all subscribers
// (for recipients that don't support subscribers-only posts)
mentions.extend(subscribers);
};
for profile in mentions {
let tag_name = format!("@{}", profile.actor_address(instance_host)); let tag_name = format!("@{}", profile.actor_address(instance_host));
let actor_id = profile.actor_id(instance_url); let actor_id = profile.actor_id(instance_url);
if !primary_audience.contains(&actor_id) { if !primary_audience.contains(&actor_id) {
@ -154,9 +146,8 @@ pub fn build_create_note(
instance_host: &str, instance_host: &str,
instance_url: &str, instance_url: &str,
post: &Post, post: &Post,
subscribers: Vec<DbActorProfile>,
) -> Activity { ) -> Activity {
let object = build_note(instance_host, instance_url, post, subscribers); let object = build_note(instance_host, instance_url, post);
let primary_audience = object.to.clone(); let primary_audience = object.to.clone();
let secondary_audience = object.cc.clone(); let secondary_audience = object.cc.clone();
let activity_id = format!("{}/create", object.id); let activity_id = format!("{}/create", object.id);
@ -212,16 +203,10 @@ pub async fn prepare_create_note(
post: &Post, post: &Post,
) -> Result<OutgoingActivity<Activity>, DatabaseError> { ) -> Result<OutgoingActivity<Activity>, DatabaseError> {
assert_eq!(author.id, post.author.id); assert_eq!(author.id, post.author.id);
let subscribers = if post.visibility == Visibility::Subscribers {
get_subscribers(db_client, &author.id).await?
} else {
vec![]
};
let activity = build_create_note( let activity = build_create_note(
&instance.host(), &instance.host(),
&instance.url(), &instance.url(),
post, post,
subscribers,
); );
let recipients = get_note_recipients(db_client, author, post).await?; let recipients = get_note_recipients(db_client, author, post).await?;
Ok(OutgoingActivity { Ok(OutgoingActivity {
@ -235,6 +220,7 @@ pub async fn prepare_create_note(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use serde_json::json; use serde_json::json;
use crate::models::profiles::types::DbActorProfile;
use super::*; use super::*;
const INSTANCE_HOST: &str = "example.com"; const INSTANCE_HOST: &str = "example.com";
@ -247,7 +233,7 @@ mod tests {
..Default::default() ..Default::default()
}; };
let post = Post { author, ..Default::default() }; let post = Post { author, ..Default::default() };
let note = build_note(INSTANCE_HOST, INSTANCE_URL, &post, vec![]); let note = build_note(INSTANCE_HOST, INSTANCE_URL, &post);
assert_eq!( assert_eq!(
note.id, note.id,
@ -272,7 +258,7 @@ mod tests {
visibility: Visibility::Followers, visibility: Visibility::Followers,
..Default::default() ..Default::default()
}; };
let note = build_note(INSTANCE_HOST, INSTANCE_URL, &post, vec![]); let note = build_note(INSTANCE_HOST, INSTANCE_URL, &post);
assert_eq!(note.to, vec![ assert_eq!(note.to, vec![
local_actor_followers(INSTANCE_URL, &post.author.username), local_actor_followers(INSTANCE_URL, &post.author.username),
@ -282,10 +268,6 @@ mod tests {
#[test] #[test]
fn test_build_note_subscribers_only() { fn test_build_note_subscribers_only() {
let post = Post {
visibility: Visibility::Subscribers,
..Default::default()
};
let subscriber_id = "https://test.com/users/3"; let subscriber_id = "https://test.com/users/3";
let subscriber = DbActorProfile { let subscriber = DbActorProfile {
username: "subscriber".to_string(), username: "subscriber".to_string(),
@ -296,7 +278,12 @@ mod tests {
actor_id: Some(subscriber_id.to_string()), actor_id: Some(subscriber_id.to_string()),
..Default::default() ..Default::default()
}; };
let note = build_note(INSTANCE_HOST, INSTANCE_URL, &post, vec![subscriber]); let post = Post {
visibility: Visibility::Subscribers,
mentions: vec![subscriber],
..Default::default()
};
let note = build_note(INSTANCE_HOST, INSTANCE_URL, &post);
assert_eq!(note.to, vec![ assert_eq!(note.to, vec![
local_actor_subscribers(INSTANCE_URL, &post.author.username), local_actor_subscribers(INSTANCE_URL, &post.author.username),
@ -322,7 +309,7 @@ mod tests {
mentions: vec![mentioned], mentions: vec![mentioned],
..Default::default() ..Default::default()
}; };
let note = build_note(INSTANCE_HOST, INSTANCE_URL, &post, vec![]); let note = build_note(INSTANCE_HOST, INSTANCE_URL, &post);
assert_eq!(note.to, vec![mentioned_id]); assert_eq!(note.to, vec![mentioned_id]);
assert_eq!(note.cc.is_empty(), true); assert_eq!(note.cc.is_empty(), true);
@ -336,7 +323,7 @@ mod tests {
in_reply_to: Some(Box::new(parent.clone())), in_reply_to: Some(Box::new(parent.clone())),
..Default::default() ..Default::default()
}; };
let note = build_note(INSTANCE_HOST, INSTANCE_URL, &post, vec![]); let note = build_note(INSTANCE_HOST, INSTANCE_URL, &post);
assert_eq!( assert_eq!(
note.in_reply_to.unwrap(), note.in_reply_to.unwrap(),
@ -374,7 +361,7 @@ mod tests {
mentions: vec![parent_author], mentions: vec![parent_author],
..Default::default() ..Default::default()
}; };
let note = build_note(INSTANCE_HOST, INSTANCE_URL, &post, vec![]); let note = build_note(INSTANCE_HOST, INSTANCE_URL, &post);
assert_eq!( assert_eq!(
note.in_reply_to.unwrap(), note.in_reply_to.unwrap(),
@ -399,7 +386,6 @@ mod tests {
INSTANCE_HOST, INSTANCE_HOST,
INSTANCE_URL, INSTANCE_URL,
&post, &post,
vec![],
); );
assert_eq!( assert_eq!(

View file

@ -7,9 +7,7 @@ use crate::activitypub::deliverer::OutgoingActivity;
use crate::activitypub::vocabulary::{DELETE, NOTE, TOMBSTONE}; use crate::activitypub::vocabulary::{DELETE, NOTE, TOMBSTONE};
use crate::config::Instance; use crate::config::Instance;
use crate::errors::DatabaseError; use crate::errors::DatabaseError;
use crate::models::posts::types::{Post, Visibility}; use crate::models::posts::types::Post;
use crate::models::profiles::types::DbActorProfile;
use crate::models::relationships::queries::get_subscribers;
use crate::models::users::types::User; use crate::models::users::types::User;
use super::create_note::{ use super::create_note::{
build_note, build_note,
@ -21,7 +19,6 @@ fn build_delete_note(
instance_host: &str, instance_host: &str,
instance_url: &str, instance_url: &str,
post: &Post, post: &Post,
subscribers: Vec<DbActorProfile>,
) -> Activity { ) -> Activity {
let object_id = post.get_object_id(instance_url); let object_id = post.get_object_id(instance_url);
let object = Object { let object = Object {
@ -36,7 +33,6 @@ fn build_delete_note(
instance_host, instance_host,
instance_url, instance_url,
post, post,
subscribers,
); );
let activity = create_activity( let activity = create_activity(
instance_url, instance_url,
@ -57,16 +53,10 @@ pub async fn prepare_delete_note(
post: &Post, post: &Post,
) -> Result<OutgoingActivity<Activity>, DatabaseError> { ) -> Result<OutgoingActivity<Activity>, DatabaseError> {
assert_eq!(author.id, post.author.id); assert_eq!(author.id, post.author.id);
let subscribers = if post.visibility == Visibility::Subscribers {
get_subscribers(db_client, &author.id).await?
} else {
vec![]
};
let activity = build_delete_note( let activity = build_delete_note(
&instance.host(), &instance.host(),
&instance.url(), &instance.url(),
post, post,
subscribers,
); );
let recipients = get_note_recipients(db_client, author, post).await?; let recipients = get_note_recipients(db_client, author, post).await?;
Ok(OutgoingActivity { Ok(OutgoingActivity {
@ -84,6 +74,7 @@ mod tests {
constants::AP_PUBLIC, constants::AP_PUBLIC,
identifiers::local_actor_followers, identifiers::local_actor_followers,
}; };
use crate::models::profiles::types::DbActorProfile;
use super::*; use super::*;
const INSTANCE_HOST: &str = "example.com"; const INSTANCE_HOST: &str = "example.com";
@ -100,7 +91,6 @@ mod tests {
INSTANCE_HOST, INSTANCE_HOST,
INSTANCE_URL, INSTANCE_URL,
&post, &post,
vec![],
); );
assert_eq!( assert_eq!(

View file

@ -146,7 +146,6 @@ async fn outbox(
&instance.host(), &instance.host(),
&instance.url(), &instance.url(),
post, post,
vec![], // subscribers-only posts are not included
); );
Some(activity) Some(activity)
}).collect(); }).collect();
@ -300,7 +299,6 @@ pub async fn object_view(
&config.instance().host(), &config.instance().host(),
&config.instance().url(), &config.instance().url(),
&post, &post,
vec![], // subscribers-only posts are not accessible
); );
let response = HttpResponse::Ok() let response = HttpResponse::Ok()
.content_type(ACTIVITY_CONTENT_TYPE) .content_type(ACTIVITY_CONTENT_TYPE)

View file

@ -38,6 +38,7 @@ use crate::models::reactions::queries::{
create_reaction, create_reaction,
delete_reaction, delete_reaction,
}; };
use crate::models::relationships::queries::get_subscribers;
use crate::utils::currencies::Currency; use crate::utils::currencies::Currency;
use super::helpers::{ use super::helpers::{
build_status, build_status,
@ -71,6 +72,15 @@ async fn create_status(
); );
post_data.mentions.extend(mention_map.values() post_data.mentions.extend(mention_map.values()
.map(|profile| profile.id)); .map(|profile| profile.id));
if post_data.visibility == Visibility::Subscribers {
// Mention all subscribers.
// This makes post accessible only to active subscribers
// and is required for sending activities to subscribers
// on other instances.
let subscribers = get_subscribers(db_client, &current_user.id).await?
.into_iter().map(|profile| profile.id);
post_data.mentions.extend(subscribers);
};
post_data.mentions.sort(); post_data.mentions.sort();
post_data.mentions.dedup(); post_data.mentions.dedup();
// Hashtags // Hashtags

View file

@ -263,6 +263,7 @@ fn build_visibility_filter() -> String {
AND target_id = post.author_id AND target_id = post.author_id
AND relationship_type = {relationship_follow} AND relationship_type = {relationship_follow}
) )
-- TODO: remove and rely on mentions instead
OR post.visibility = {visibility_subscribers} AND EXISTS ( OR post.visibility = {visibility_subscribers} AND EXISTS (
SELECT 1 FROM relationship SELECT 1 FROM relationship
WHERE WHERE