1 use crate::objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson};
2 use activitypub_federation::{
4 fetch::{object_id::ObjectId, webfinger::webfinger_resolve_actor},
5 kinds::link::MentionType,
8 use lemmy_api_common::context::LemmyContext;
10 source::{comment::Comment, person::Person, post::Post},
14 use lemmy_utils::{error::LemmyError, utils::mention::scrape_text_for_mentions};
15 use serde::{Deserialize, Serialize};
16 use serde_json::Value;
19 #[derive(Clone, Debug, Deserialize, Serialize)]
21 pub enum MentionOrValue {
26 #[derive(Clone, Debug, Deserialize, Serialize)]
30 #[serde(rename = "type")]
31 pub kind: MentionType,
34 pub struct MentionsAndAddresses {
36 pub tags: Vec<MentionOrValue>,
39 /// This takes a comment, and builds a list of to_addresses, inboxes,
40 /// and mention tags, so they know where to be sent to.
41 /// Addresses are the persons / addresses that go in the cc field.
42 #[tracing::instrument(skip(comment, community_id, context))]
43 pub async fn collect_non_local_mentions(
44 comment: &ApubComment,
45 community_id: ObjectId<ApubCommunity>,
46 context: &Data<LemmyContext>,
47 ) -> Result<MentionsAndAddresses, LemmyError> {
48 let parent_creator = get_comment_parent_creator(context.pool(), comment).await?;
49 let mut addressed_ccs: Vec<Url> = vec![community_id.into(), parent_creator.id()];
51 // Add the mention tag
52 let parent_creator_tag = Mention {
53 href: parent_creator.actor_id.clone().into(),
57 &parent_creator.id().domain().expect("has domain")
59 kind: MentionType::Mention,
61 let mut tags = vec![parent_creator_tag];
63 // Get the person IDs for any mentions
64 let mentions = scrape_text_for_mentions(&comment.content)
66 // Filter only the non-local ones
67 .filter(|m| !m.is_local(&context.settings().hostname));
69 for mention in mentions {
70 let identifier = format!("{}@{}", mention.name, mention.domain);
71 let person = webfinger_resolve_actor::<LemmyContext, ApubPerson>(&identifier, context).await;
72 if let Ok(person) = person {
73 addressed_ccs.push(person.actor_id.to_string().parse()?);
75 let mention_tag = Mention {
77 name: Some(mention.full_name()),
78 kind: MentionType::Mention,
80 tags.push(mention_tag);
84 let tags = tags.into_iter().map(MentionOrValue::Mention).collect();
85 Ok(MentionsAndAddresses {
91 /// Returns the apub ID of the person this comment is responding to. Meaning, in case this is a
92 /// top-level comment, the creator of the post, otherwise the creator of the parent comment.
93 #[tracing::instrument(skip(pool, comment))]
94 async fn get_comment_parent_creator(
97 ) -> Result<ApubPerson, LemmyError> {
98 let parent_creator_id = if let Some(parent_comment_id) = comment.parent_comment_id() {
99 let parent_comment = Comment::read(pool, parent_comment_id).await?;
100 parent_comment.creator_id
102 let parent_post_id = comment.post_id;
103 let parent_post = Post::read(pool, parent_post_id).await?;
104 parent_post.creator_id
106 Ok(Person::read(pool, parent_creator_id).await?.into())