"diesel",
"lazy_static",
"lemmy_api_common",
- "lemmy_apub_lib",
+ "lemmy_apub",
"lemmy_db_schema",
"lemmy_db_views",
"lemmy_db_views_actor",
site::*,
};
use lemmy_apub::{
- fetcher::search::{search_by_apub_id, SearchableObjects},
- get_actor_id_from_name,
+ fetcher::{
+ search::{search_by_apub_id, SearchableObjects},
+ webfinger::webfinger_resolve,
+ },
+ objects::community::ApubCommunity,
+ EndpointType,
};
-use lemmy_apub_lib::webfinger::WebfingerType;
use lemmy_db_schema::{
from_opt_str_to_opt_enum,
newtypes::PersonId,
let search_type: SearchType = from_opt_str_to_opt_enum(&data.type_).unwrap_or(SearchType::All);
let community_id = data.community_id;
let community_actor_id = if let Some(name) = &data.community_name {
- get_actor_id_from_name(WebfingerType::Group, name, context)
+ webfinger_resolve::<ApubCommunity>(name, EndpointType::Community, context, &mut 0)
.await
.ok()
} else {
use crate::PerformCrud;
use actix_web::web::Data;
use lemmy_api_common::{blocking, comment::*, get_local_user_view_from_jwt_opt};
-use lemmy_apub::get_actor_id_from_name;
-use lemmy_apub_lib::webfinger::WebfingerType;
+use lemmy_apub::{
+ fetcher::webfinger::webfinger_resolve,
+ objects::community::ApubCommunity,
+ EndpointType,
+};
use lemmy_db_schema::{
from_opt_str_to_opt_enum,
traits::DeleteableOrRemoveable,
let community_id = data.community_id;
let community_actor_id = if let Some(name) = &data.community_name {
- get_actor_id_from_name(WebfingerType::Group, name, context)
+ webfinger_resolve::<ApubCommunity>(name, EndpointType::Community, context, &mut 0)
.await
.ok()
} else {
use crate::PerformCrud;
use actix_web::web::Data;
use lemmy_api_common::{blocking, community::*, get_local_user_view_from_jwt_opt};
-use lemmy_apub::{get_actor_id_from_name, objects::community::ApubCommunity};
-use lemmy_apub_lib::{object_id::ObjectId, webfinger::WebfingerType};
+use lemmy_apub::{
+ fetcher::webfinger::webfinger_resolve,
+ objects::community::ApubCommunity,
+ EndpointType,
+};
+use lemmy_apub_lib::object_id::ObjectId;
use lemmy_db_schema::{
from_opt_str_to_opt_enum,
traits::DeleteableOrRemoveable,
None => {
let name = data.name.to_owned().unwrap_or_else(|| "main".to_string());
let community_actor_id =
- get_actor_id_from_name(WebfingerType::Group, &name, context).await?;
+ webfinger_resolve::<ApubCommunity>(&name, EndpointType::Community, context, &mut 0)
+ .await?;
ObjectId::<ApubCommunity>::new(community_actor_id)
.dereference(context, &mut 0)
use crate::PerformCrud;
use actix_web::web::Data;
use lemmy_api_common::{blocking, get_local_user_view_from_jwt_opt, mark_post_as_read, post::*};
-use lemmy_apub::get_actor_id_from_name;
-use lemmy_apub_lib::webfinger::WebfingerType;
+use lemmy_apub::{
+ fetcher::webfinger::webfinger_resolve,
+ objects::community::ApubCommunity,
+ EndpointType,
+};
use lemmy_db_schema::{
from_opt_str_to_opt_enum,
traits::DeleteableOrRemoveable,
let limit = data.limit;
let community_id = data.community_id;
let community_actor_id = if let Some(name) = &data.community_name {
- get_actor_id_from_name(WebfingerType::Group, name, context)
+ webfinger_resolve::<ApubCommunity>(name, EndpointType::Community, context, &mut 0)
.await
.ok()
} else {
use crate::PerformCrud;
use actix_web::web::Data;
use lemmy_api_common::{blocking, get_local_user_view_from_jwt_opt, person::*};
-use lemmy_apub::{get_actor_id_from_name, objects::person::ApubPerson};
-use lemmy_apub_lib::{object_id::ObjectId, webfinger::WebfingerType};
+use lemmy_apub::{
+ fetcher::webfinger::webfinger_resolve,
+ objects::person::ApubPerson,
+ EndpointType,
+};
+use lemmy_apub_lib::object_id::ObjectId;
use lemmy_db_schema::{from_opt_str_to_opt_enum, SortType};
use lemmy_db_views::{comment_view::CommentQueryBuilder, post_view::PostQueryBuilder};
use lemmy_db_views_actor::{
.username
.to_owned()
.unwrap_or_else(|| "admin".to_string());
- let actor_id = get_actor_id_from_name(WebfingerType::Person, &name, context).await?;
+ let actor_id =
+ webfinger_resolve::<ApubPerson>(&name, EndpointType::Person, context, &mut 0).await?;
let person = ObjectId::<ApubPerson>::new(actor_id)
.dereference(context, &mut 0)
-use crate::objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson};
use activitystreams::{
base::BaseExt,
link::{LinkExt, Mention},
};
use anyhow::anyhow;
use itertools::Itertools;
+use log::debug;
+use url::Url;
+
use lemmy_api_common::blocking;
-use lemmy_apub_lib::{object_id::ObjectId, traits::ActorType, webfinger::WebfingerResponse};
+use lemmy_apub_lib::{object_id::ObjectId, traits::ActorType};
use lemmy_db_schema::{
newtypes::LocalUserId,
source::{comment::Comment, person::Person, post::Post},
LemmyError,
};
use lemmy_websocket::{send::send_local_notifs, LemmyContext};
-use log::debug;
-use url::Url;
+
+use crate::{
+ fetcher::webfinger::WebfingerResponse,
+ objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson},
+};
pub mod create_or_update;
insert_activity(activity_id, object_value, true, sensitive, context.pool()).await?;
send_activity(
- serialised_activity,
+ activity_id,
actor,
inboxes,
+ serialised_activity,
context.client(),
context.activity_queue(),
)
pub mod post_or_comment;
pub mod search;
pub mod user_or_community;
+pub mod webfinger;
use crate::{
+ fetcher::webfinger::webfinger_resolve,
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
protocol::objects::{group::Group, note::Note, page::Page, person::Person},
+ EndpointType,
};
use anyhow::anyhow;
use chrono::NaiveDateTime;
-use itertools::Itertools;
-use lemmy_api_common::blocking;
-use lemmy_apub_lib::{
- object_id::ObjectId,
- traits::ApubObject,
- webfinger::{webfinger_resolve_actor, WebfingerType},
-};
-use lemmy_db_schema::{
- source::{community::Community, person::Person as DbPerson},
- DbPool,
-};
+use lemmy_apub_lib::{object_id::ObjectId, traits::ApubObject};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::Deserialize;
query: &str,
context: &LemmyContext,
) -> Result<SearchableObjects, LemmyError> {
- let query_url = match Url::parse(query) {
- Ok(u) => u,
+ let request_counter = &mut 0;
+ match Url::parse(query) {
+ Ok(url) => {
+ ObjectId::new(url)
+ .dereference(context, request_counter)
+ .await
+ }
Err(_) => {
- let (kind, name) = query.split_at(1);
- let kind = match kind {
- "@" => WebfingerType::Person,
- "!" => WebfingerType::Group,
- _ => return Err(anyhow!("invalid query").into()),
- };
- // remote actor, use webfinger to resolve url
- if name.contains('@') {
- let (name, domain) = name.splitn(2, '@').collect_tuple().expect("invalid query");
- webfinger_resolve_actor(
- name,
- domain,
- kind,
- context.client(),
- context.settings().get_protocol_string(),
- )
- .await?
- }
- // local actor, read from database and return
- else {
- return find_local_actor_by_name(name, kind, context.pool()).await;
+ let (kind, identifier) = query.split_at(1);
+ match kind {
+ "@" => {
+ let id = webfinger_resolve::<ApubPerson>(
+ identifier,
+ EndpointType::Person,
+ context,
+ request_counter,
+ )
+ .await?;
+ Ok(SearchableObjects::Person(
+ ObjectId::new(id)
+ .dereference(context, request_counter)
+ .await?,
+ ))
+ }
+ "!" => {
+ let id = webfinger_resolve::<ApubCommunity>(
+ identifier,
+ EndpointType::Community,
+ context,
+ request_counter,
+ )
+ .await?;
+ Ok(SearchableObjects::Community(
+ ObjectId::new(id)
+ .dereference(context, request_counter)
+ .await?,
+ ))
+ }
+ _ => Err(anyhow!("invalid query").into()),
}
}
- };
-
- let request_counter = &mut 0;
- ObjectId::new(query_url)
- .dereference(context, request_counter)
- .await
-}
-
-async fn find_local_actor_by_name(
- name: &str,
- kind: WebfingerType,
- pool: &DbPool,
-) -> Result<SearchableObjects, LemmyError> {
- let name: String = name.into();
- Ok(match kind {
- WebfingerType::Group => SearchableObjects::Community(
- blocking(pool, move |conn| Community::read_from_name(conn, &name))
- .await??
- .into(),
- ),
- WebfingerType::Person => SearchableObjects::Person(
- blocking(pool, move |conn| DbPerson::find_by_name(conn, &name))
- .await??
- .into(),
- ),
- })
+ }
}
/// The types of ActivityPub objects that can be fetched directly by searching for their ID.
impl ActorType for UserOrCommunity {
fn actor_id(&self) -> Url {
- todo!()
+ match self {
+ UserOrCommunity::User(p) => p.actor_id(),
+ UserOrCommunity::Community(p) => p.actor_id(),
+ }
}
fn public_key(&self) -> Option<String> {
--- /dev/null
+use crate::{generate_local_apub_endpoint, EndpointType};
+use anyhow::anyhow;
+use itertools::Itertools;
+use lemmy_apub_lib::{
+ object_id::ObjectId,
+ traits::{ActorType, ApubObject},
+};
+use lemmy_db_schema::newtypes::DbUrl;
+use lemmy_utils::{
+ request::{retry, RecvError},
+ LemmyError,
+};
+use lemmy_websocket::LemmyContext;
+use log::debug;
+use serde::{Deserialize, Serialize};
+use url::Url;
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct WebfingerLink {
+ pub rel: Option<String>,
+ #[serde(rename(serialize = "type", deserialize = "type"))]
+ pub type_: Option<String>,
+ pub href: Option<Url>,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct WebfingerResponse {
+ pub subject: String,
+ pub links: Vec<WebfingerLink>,
+}
+
+/// Takes in a shortname of the type dessalines@xyz.tld or dessalines (assumed to be local), and
+/// outputs the actor id. Used in the API for communities and users.
+///
+/// TODO: later provide a method in ApubObject to generate the endpoint, so that we dont have to
+/// pass in EndpointType
+pub async fn webfinger_resolve<Kind>(
+ identifier: &str,
+ endpoint_type: EndpointType,
+ context: &LemmyContext,
+ request_counter: &mut i32,
+) -> Result<DbUrl, LemmyError>
+where
+ Kind: ApubObject<DataType = LemmyContext> + ActorType + Send + 'static,
+ for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>,
+{
+ // remote actor
+ if identifier.contains('@') {
+ webfinger_resolve_actor::<Kind>(identifier, context, request_counter).await
+ }
+ // local actor
+ else {
+ let domain = context.settings().get_protocol_and_hostname();
+ Ok(generate_local_apub_endpoint(
+ endpoint_type,
+ identifier,
+ &domain,
+ )?)
+ }
+}
+
+/// Turns a person id like `@name@example.com` into an apub ID, like `https://example.com/user/name`,
+/// using webfinger.
+async fn webfinger_resolve_actor<Kind>(
+ identifier: &str,
+ context: &LemmyContext,
+ request_counter: &mut i32,
+) -> Result<DbUrl, LemmyError>
+where
+ Kind: ApubObject<DataType = LemmyContext> + ActorType + Send + 'static,
+ for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>,
+{
+ let protocol = context.settings().get_protocol_string();
+ let (_, domain) = identifier
+ .splitn(2, '@')
+ .collect_tuple()
+ .expect("invalid query");
+ let fetch_url = format!(
+ "{}://{}/.well-known/webfinger?resource=acct:{}",
+ protocol, domain, identifier
+ );
+ debug!("Fetching webfinger url: {}", &fetch_url);
+
+ let response = retry(|| context.client().get(&fetch_url).send()).await?;
+
+ let res: WebfingerResponse = response
+ .json()
+ .await
+ .map_err(|e| RecvError(e.to_string()))?;
+
+ let links: Vec<Url> = res
+ .links
+ .iter()
+ .filter(|l| l.type_.eq(&Some("application/activity+json".to_string())))
+ .map(|l| l.href.clone())
+ .flatten()
+ .collect();
+ for l in links {
+ let object = ObjectId::<Kind>::new(l)
+ .dereference(context, request_counter)
+ .await;
+ if object.is_ok() {
+ return object.map(|o| o.actor_id().into());
+ }
+ }
+ Err(anyhow!("Failed to resolve actor for {}", identifier).into())
+}
+use crate::fetcher::post_or_comment::PostOrComment;
+use anyhow::{anyhow, Context};
+use lemmy_api_common::blocking;
+use lemmy_db_schema::{newtypes::DbUrl, source::activity::Activity, DbPool};
+use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
+use std::net::IpAddr;
+use url::{ParseError, Url};
+
pub mod activities;
pub(crate) mod activity_lists;
pub(crate) mod collections;
#[macro_use]
extern crate lazy_static;
-use crate::fetcher::post_or_comment::PostOrComment;
-use anyhow::{anyhow, Context};
-use lemmy_api_common::blocking;
-use lemmy_apub_lib::webfinger::{webfinger_resolve_actor, WebfingerType};
-use lemmy_db_schema::{newtypes::DbUrl, source::activity::Activity, DbPool};
-use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
-use lemmy_websocket::LemmyContext;
-use std::net::IpAddr;
-use url::{ParseError, Url};
-
/// Checks if the ID is allowed for sending or receiving.
///
/// In particular, it checks for:
Ok(Url::parse(&format!("{}/moderators", community_id))?.into())
}
-/// Takes in a shortname of the type dessalines@xyz.tld or dessalines (assumed to be local), and outputs the actor id.
-/// Used in the API for communities and users.
-pub async fn get_actor_id_from_name(
- webfinger_type: WebfingerType,
- short_name: &str,
- context: &LemmyContext,
-) -> Result<DbUrl, LemmyError> {
- let split = short_name.split('@').collect::<Vec<&str>>();
-
- let name = split[0];
-
- // If there's no @, its local
- if split.len() == 1 {
- let domain = context.settings().get_protocol_and_hostname();
- let endpoint_type = match webfinger_type {
- WebfingerType::Person => EndpointType::Person,
- WebfingerType::Group => EndpointType::Community,
- };
- Ok(generate_local_apub_endpoint(endpoint_type, name, &domain)?)
- } else {
- let protocol = context.settings().get_protocol_string();
- Ok(
- webfinger_resolve_actor(name, split[1], webfinger_type, context.client(), protocol)
- .await?
- .into(),
- )
- }
-}
-
/// Store a sent or received activity in the database, for logging purposes. These records are not
/// persistent.
async fn insert_activity(
WorkerConfig,
};
use lemmy_utils::{location_info, LemmyError};
-use log::warn;
+use log::{info, warn};
use reqwest::Client;
use serde::{Deserialize, Serialize};
use std::{env, fmt::Debug, future::Future, pin::Pin};
use url::Url;
pub async fn send_activity(
- activity: String,
+ activity_id: &Url,
actor: &dyn ActorType,
inboxes: Vec<&Url>,
+ activity: String,
client: &Client,
activity_queue: &QueueHandle,
) -> Result<(), LemmyError> {
for i in inboxes {
let message = SendActivityTask {
- activity: activity.clone(),
+ activity_id: activity_id.clone(),
inbox: i.to_owned(),
actor_id: actor.actor_id(),
+ activity: activity.clone(),
private_key: actor.private_key().context(location_info!())?,
};
if env::var("APUB_TESTING_SEND_SYNC").is_ok() {
#[derive(Clone, Debug, Deserialize, Serialize)]
struct SendActivityTask {
- activity: String,
+ activity_id: Url,
inbox: Url,
actor_id: Url,
+ activity: String,
private_key: String,
}
}
async fn do_send(task: SendActivityTask, client: &Client) -> Result<(), Error> {
+ info!("Sending {} to {}", task.activity_id, task.inbox);
let result = sign_and_send(
client,
&task.inbox,
)
.await;
- if let Err(e) = result {
- warn!("{}", e);
- return Err(anyhow!(
- "Failed to send activity {} to {}",
- &task.activity,
- task.inbox
- ));
+ match result {
+ Ok(o) => {
+ if !o.status().is_success() {
+ warn!(
+ "Send {} to {} failed with status {}: {}",
+ task.activity_id,
+ task.inbox,
+ o.status(),
+ o.text().await?
+ );
+ }
+ }
+ Err(e) => {
+ return Err(anyhow!(
+ "Failed to send activity {} to {}: {}",
+ &task.activity_id,
+ task.inbox,
+ e
+ ));
+ }
}
Ok(())
}
pub mod traits;
pub mod values;
pub mod verify;
-pub mod webfinger;
pub static APUB_JSON_CONTENT_TYPE: &str = "application/activity+json";
+++ /dev/null
-use anyhow::anyhow;
-use lemmy_utils::{
- request::{retry, RecvError},
- LemmyError,
-};
-use log::debug;
-use reqwest::Client;
-use serde::{Deserialize, Serialize};
-use url::Url;
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct WebfingerLink {
- pub rel: Option<String>,
- #[serde(rename(serialize = "type", deserialize = "type"))]
- pub type_: Option<String>,
- pub href: Option<Url>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub template: Option<String>,
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct WebfingerResponse {
- pub subject: String,
- pub aliases: Vec<Url>,
- pub links: Vec<WebfingerLink>,
-}
-
-pub enum WebfingerType {
- Person,
- Group,
-}
-
-/// Turns a person id like `@name@example.com` into an apub ID, like `https://example.com/user/name`,
-/// using webfinger.
-pub async fn webfinger_resolve_actor(
- name: &str,
- domain: &str,
- webfinger_type: WebfingerType,
- client: &Client,
- protocol_string: &str,
-) -> Result<Url, LemmyError> {
- let webfinger_type = match webfinger_type {
- WebfingerType::Person => "acct",
- WebfingerType::Group => "group",
- };
- let fetch_url = format!(
- "{}://{}/.well-known/webfinger?resource={}:{}@{}",
- protocol_string, domain, webfinger_type, name, domain
- );
- debug!("Fetching webfinger url: {}", &fetch_url);
-
- let response = retry(|| client.get(&fetch_url).send()).await?;
-
- let res: WebfingerResponse = response
- .json()
- .await
- .map_err(|e| RecvError(e.to_string()))?;
-
- let link = res
- .links
- .iter()
- .find(|l| l.type_.eq(&Some("application/activity+json".to_string())))
- .ok_or_else(|| anyhow!("No application/activity+json link found."))?;
- link
- .href
- .to_owned()
- .ok_or_else(|| anyhow!("No href found.").into())
-}
lemmy_db_views_actor = { version = "=0.14.0-rc.1", path = "../db_views_actor" }
lemmy_db_schema = { version = "=0.14.0-rc.1", path = "../db_schema" }
lemmy_api_common = { version = "=0.14.0-rc.1", path = "../api_common" }
-lemmy_apub_lib = { version = "=0.14.0-rc.1", path = "../apub_lib" }
+lemmy_apub = { version = "=0.14.0-rc.1", path = "../apub" }
diesel = "1.4.8"
actix = "0.12.0"
actix-web = { version = "4.0.0-beta.9", default-features = false, features = ["rustls"] }
use actix_web::{web, web::Query, HttpResponse};
-use anyhow::anyhow;
+use anyhow::Context;
use lemmy_api_common::blocking;
-use lemmy_apub_lib::webfinger::{WebfingerLink, WebfingerResponse};
+use lemmy_apub::fetcher::webfinger::{WebfingerLink, WebfingerResponse};
use lemmy_db_schema::source::{community::Community, person::Person};
-use lemmy_utils::{settings::structs::Settings, ApiError, LemmyError};
+use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
use lemmy_websocket::LemmyContext;
use serde::Deserialize;
+use url::Url;
#[derive(Deserialize)]
struct Params {
info: Query<Params>,
context: web::Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
- let community_regex_parsed = context
+ let name = context
.settings()
- .webfinger_community_regex()
+ .webfinger_regex()
.captures(&info.resource)
.map(|c| c.get(1))
- .flatten();
+ .flatten()
+ .context(location_info!())?
+ .as_str()
+ .to_string();
- let username_regex_parsed = context
- .settings()
- .webfinger_username_regex()
- .captures(&info.resource)
- .map(|c| c.get(1))
- .flatten();
-
- let url = if let Some(community_name) = community_regex_parsed {
- let community_name = community_name.as_str().to_owned();
- // Make sure the requested community exists.
- blocking(context.pool(), move |conn| {
- Community::read_from_name(conn, &community_name)
- })
- .await?
- .map_err(|e| ApiError::err("not_found", e))?
- .actor_id
- } else if let Some(person_name) = username_regex_parsed {
- let person_name = person_name.as_str().to_owned();
- // Make sure the requested person exists.
- blocking(context.pool(), move |conn| {
- Person::find_by_name(conn, &person_name)
- })
- .await?
- .map_err(|e| ApiError::err("not_found", e))?
- .actor_id
- } else {
- return Err(LemmyError::from(anyhow!("not_found")));
- };
+ let name_ = name.clone();
+ let community_id: Option<Url> = blocking(context.pool(), move |conn| {
+ Community::read_from_name(conn, &name_)
+ })
+ .await?
+ .ok()
+ .map(|c| c.actor_id.into());
+ let user_id: Option<Url> = blocking(context.pool(), move |conn| {
+ Person::find_by_name(conn, &name)
+ })
+ .await?
+ .ok()
+ .map(|c| c.actor_id.into());
+ let links = vec![
+ webfinger_link_for_actor(community_id),
+ webfinger_link_for_actor(user_id),
+ ]
+ .into_iter()
+ .flatten()
+ .collect();
let json = WebfingerResponse {
subject: info.resource.to_owned(),
- aliases: vec![url.to_owned().into()],
- links: vec![
+ links,
+ };
+
+ Ok(HttpResponse::Ok().json(json))
+}
+
+fn webfinger_link_for_actor(url: Option<Url>) -> Vec<WebfingerLink> {
+ if let Some(url) = url {
+ vec![
WebfingerLink {
rel: Some("http://webfinger.net/rel/profile-page".to_string()),
type_: Some("text/html".to_string()),
- href: Some(url.to_owned().into()),
- template: None,
+ href: Some(url.to_owned()),
},
WebfingerLink {
rel: Some("self".to_string()),
type_: Some("application/activity+json".to_string()),
- href: Some(url.into()),
- template: None,
- }, // TODO: this also needs to return the subscribe link once that's implemented
- //{
- // "rel": "http://ostatus.org/schema/1.0/subscribe",
- // "template": "https://my_instance.com/authorize_interaction?uri={uri}"
- //}
- ],
- };
-
- Ok(HttpResponse::Ok().json(json))
+ href: Some(url),
+ },
+ ]
+ } else {
+ vec![]
+ }
}
lazy_static! {
static ref SETTINGS: RwLock<Settings> =
RwLock::new(Settings::init().expect("Failed to load settings file"));
- static ref WEBFINGER_COMMUNITY_REGEX: Regex = Regex::new(&format!(
- "^group:([a-z0-9_]{{3,}})@{}$",
- Settings::get().hostname
- ))
- .expect("compile webfinger regex");
- static ref WEBFINGER_USER_REGEX: Regex = Regex::new(&format!(
+ static ref WEBFINGER_REGEX: Regex = Regex::new(&format!(
"^acct:([a-z0-9_]{{3,}})@{}$",
Settings::get().hostname
))
Ok(Self::read_config_file()?)
}
- pub fn webfinger_community_regex(&self) -> Regex {
- WEBFINGER_COMMUNITY_REGEX.to_owned()
- }
-
- pub fn webfinger_username_regex(&self) -> Regex {
- WEBFINGER_USER_REGEX.to_owned()
+ pub fn webfinger_regex(&self) -> Regex {
+ WEBFINGER_REGEX.to_owned()
}
pub fn slur_regex(&self) -> Option<Regex> {