From f008564c328f70da90d210c31e201ec2420f1122 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Fri, 15 May 2026 05:26:56 +0200 Subject: [PATCH] feat(ap): add hashtag tag array to outbound Notes --- .../adapters/activitypub-base/src/service.rs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/crates/adapters/activitypub-base/src/service.rs b/crates/adapters/activitypub-base/src/service.rs index 0b11eff..93c30ff 100644 --- a/crates/adapters/activitypub-base/src/service.rs +++ b/crates/adapters/activitypub-base/src/service.rs @@ -46,6 +46,26 @@ fn content_to_html(text: &str) -> String { } } +fn extract_hashtag_tags(content: &str, base_url: &str) -> Vec { + let mut seen = std::collections::HashSet::new(); + let mut tags = Vec::new(); + for word in content.split_whitespace() { + let tag = word.trim_matches(|c: char| !c.is_alphanumeric() && c != '#'); + if let Some(name) = tag.strip_prefix('#') + && !name.is_empty() + && seen.insert(name.to_lowercase()) + { + let lower = name.to_lowercase(); + tags.push(serde_json::json!({ + "type": "Hashtag", + "name": format!("#{}", lower), + "href": format!("{}/tags/{}", base_url, lower), + })); + } + } + tags +} + fn thought_note_json( thought: &domain::models::thought::Thought, local_actor: &crate::actors::DbActor, @@ -89,6 +109,10 @@ fn thought_note_json( if let Some(updated_at) = thought.updated_at { note["updated"] = serde_json::json!(updated_at.to_rfc3339()); } + let hashtag_tags = extract_hashtag_tags(thought.content.as_str(), base_url); + if !hashtag_tags.is_empty() { + note["tag"] = serde_json::json!(hashtag_tags); + } Ok((ap_id, note)) }