From 17f77c176905c8e4f0e43e6a5ab202a08139e60a Mon Sep 17 00:00:00 2001 From: alemi Date: Thu, 23 May 2024 16:28:18 +0200 Subject: [PATCH] feat: replace mentions with html hrefs --- Cargo.toml | 3 ++- src/server/outbox.rs | 24 +++++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a1cf73c..bb68814 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ openssl = "0.10" # TODO handle pubkeys with a smaller crate base64 = "0.22" chrono = { version = "0.4", features = ["serde"] } uuid = { version = "1.8", features = ["v4"] } +regex = "1.10" serde = { version = "1", features = ["derive"] } serde_json = "1" serde_default = "0.1" @@ -50,7 +51,7 @@ time = { version = "0.3", features = ["serde"], optional = true } async-recursion = "1.1" [features] -default = ["migrations", "cli"] +default = ["mastodon", "migrations", "cli"] cli = [] migrations = ["dep:sea-orm-migration"] mastodon = ["dep:mastodon-async-entities", "dep:time"] diff --git a/src/server/outbox.rs b/src/server/outbox.rs index e5d26ea..212ad20 100644 --- a/src/server/outbox.rs +++ b/src/server/outbox.rs @@ -1,6 +1,6 @@ use apb::{target::Addressed, Activity, ActivityMut, ActorMut, BaseMut, Node, Object, ObjectMut, PublicKeyMut}; use reqwest::StatusCode; -use sea_orm::{sea_query::Expr, ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter, Set}; +use sea_orm::{sea_query::Expr, ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter, QuerySelect, SelectColumns, Set}; use crate::{errors::UpubError, model, routes::activitypub::jsonld::LD}; @@ -14,15 +14,37 @@ impl apb::server::Outbox for Context { type Activity = serde_json::Value; async fn create_note(&self, uid: String, object: serde_json::Value) -> crate::Result { + let re = regex::Regex::new(r"@(\w+)@(\w+)").expect("failed compiling regex pattern"); let raw_oid = uuid::Uuid::new_v4().to_string(); let oid = self.oid(&raw_oid); let aid = self.aid(&uuid::Uuid::new_v4().to_string()); let activity_targets = object.addressed(); + + let mut content = object.content().map(|x| x.to_string()); + if let Some(c) = content { + let mut tmp = mdhtml::safe_markdown(&c); + for (full, [user, domain]) in re.captures_iter(&tmp.clone()).map(|x| x.extract()) { + if let Ok(Some(uid)) = model::user::Entity::find() + .filter(model::user::Column::PreferredUsername.eq(user)) + .filter(model::user::Column::Domain.eq(domain)) + .select_only() + .select_column(model::user::Column::Id) + .into_tuple::() + .one(self.db()) + .await + { + tmp = tmp.replacen(full, &format!("@{user}"), 1); + } + } + content = Some(tmp); + } + let object_model = self.insert_object( object .set_id(Some(&oid)) .set_attributed_to(Node::link(uid.clone())) .set_published(Some(chrono::Utc::now())) + .set_content(content.as_deref()) .set_url(Node::maybe_link(self.cfg().instance.frontend.as_ref().map(|x| format!("{x}/objects/{raw_oid}")))), Some(self.domain().to_string()), ).await?;