Skip to content

Commit dad1b73

Browse files
authored
Merge pull request #31 from afbase/afbase/implement-normalize-and-validate-handle
Afbase/implement normalize and validate handle
2 parents 04ca0a5 + 9ad069d commit dad1b73

File tree

17 files changed

+1795
-27
lines changed

17 files changed

+1795
-27
lines changed

rsky-pds/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ dotenvy = "0.15"
2121
rsky-lexicon = { workspace = true }
2222
rsky-identity = { workspace = true }
2323
rsky-crypto = { workspace = true }
24+
rsky-common = {workspace = true }
2425
rsky-syntax = { workspace = true }
2526
diesel = { version = "=2.1.5", features = ["chrono", "postgres"] }
2627
chrono = "0.4.26"

rsky-pds/src/apis/com/atproto/admin/get_account_info.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::account_manager::AccountManager;
33
use crate::auth_verifier::Moderator;
44
use crate::common::env::env_str;
55
use crate::models::{ErrorCode, ErrorMessageResponse};
6-
use crate::INVALID_HANDLE;
6+
use rsky_syntax::handle::INVALID_HANDLE;
77
use anyhow::{bail, Result};
88
use futures::try_join;
99
use rocket::http::Status;

rsky-pds/src/apis/com/atproto/identity/update_handle.rs

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ use crate::account_manager::AccountManager;
33
use crate::apis::com::atproto::server::get_keys_from_private_key_str;
44
use crate::auth_verifier::AccessStandardCheckTakedown;
55
use crate::common::env::env_str;
6+
use crate::config::ServerConfig;
7+
use crate::handle::{normalize_and_validate_handle, HandleValidationContext, HandleValidationOpts};
68
use crate::models::{ErrorCode, ErrorMessageResponse};
7-
use crate::{plc, SharedSequencer};
9+
use crate::{plc, SharedIdResolver, SharedSequencer};
810
use anyhow::{bail, Result};
911
use rocket::http::Status;
1012
use rocket::response::status;
@@ -16,12 +18,28 @@ use std::env;
1618
async fn inner_update_handle(
1719
body: Json<UpdateHandleInput>,
1820
sequencer: &State<SharedSequencer>,
21+
server_config: &State<ServerConfig>,
22+
id_resolver: &State<SharedIdResolver>,
1923
auth: AccessStandardCheckTakedown,
2024
) -> Result<()> {
2125
let UpdateHandleInput { handle } = body.into_inner();
2226
let requester = auth.access.credentials.unwrap().did.unwrap();
2327

24-
// @TODO: Implement normalizeAndValidateHandle()
28+
// Use the new normalize and validate function
29+
let validation_ctx = HandleValidationContext {
30+
server_config,
31+
id_resolver,
32+
};
33+
let handle = normalize_and_validate_handle(
34+
HandleValidationOpts {
35+
handle,
36+
did: Some(requester.clone()),
37+
allow_reserved: None,
38+
},
39+
validation_ctx,
40+
)
41+
.await?;
42+
2543
let account = AccountManager::get_account(
2644
&handle,
2745
Some(AvailabilityFlags {
@@ -51,24 +69,14 @@ async fn inner_update_handle(
5169
.await
5270
{
5371
Ok(_) => (),
54-
Err(error) => eprintln!(
55-
"Error: {}; DID: {}; Handle: {}",
56-
error.to_string(),
57-
&requester,
58-
&handle
59-
),
72+
Err(error) => eprintln!("Error: {}; DID: {}; Handle: {}", error, &requester, &handle),
6073
};
6174
match lock
6275
.sequence_handle_update(requester.clone(), handle.clone())
6376
.await
6477
{
6578
Ok(_) => (),
66-
Err(error) => eprintln!(
67-
"Error: {}; DID: {}; Handle: {}",
68-
error.to_string(),
69-
&requester,
70-
&handle
71-
),
79+
Err(error) => eprintln!("Error: {}; DID: {}; Handle: {}", error, &requester, &handle),
7280
};
7381
Ok(())
7482
}
@@ -81,20 +89,22 @@ async fn inner_update_handle(
8189
pub async fn update_handle(
8290
body: Json<UpdateHandleInput>,
8391
sequencer: &State<SharedSequencer>,
92+
server_config: &State<ServerConfig>,
93+
id_resolver: &State<SharedIdResolver>,
8494
auth: AccessStandardCheckTakedown,
8595
) -> Result<(), status::Custom<Json<ErrorMessageResponse>>> {
86-
match inner_update_handle(body, sequencer, auth).await {
96+
match inner_update_handle(body, sequencer, server_config, id_resolver, auth).await {
8797
Ok(_) => Ok(()),
8898
Err(error) => {
8999
eprintln!("@LOG: ERROR: {error}");
90100
let internal_error = ErrorMessageResponse {
91101
code: Some(ErrorCode::InternalServerError),
92102
message: Some(error.to_string()),
93103
};
94-
return Err(status::Custom(
104+
Err(status::Custom(
95105
Status::InternalServerError,
96106
Json(internal_error),
97-
));
107+
))
98108
}
99109
}
100110
}

rsky-pds/src/apis/com/atproto/repo/describe_repo.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::account_manager::AccountManager;
22
use crate::models::{ErrorCode, ErrorMessageResponse};
33
use crate::repo::aws::s3::S3BlobStore;
44
use crate::repo::ActorStore;
5-
use crate::INVALID_HANDLE;
5+
use rsky_syntax::handle::INVALID_HANDLE;
66
use crate::{common, SharedIdResolver};
77
use anyhow::{bail, Result};
88
use aws_config::SdkConfig;

rsky-pds/src/apis/com/atproto/server/activate_account.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::repo::cid_set::CidSet;
88
use crate::repo::types::CommitData;
99
use crate::repo::ActorStore;
1010
use crate::SharedSequencer;
11-
use crate::INVALID_HANDLE;
11+
use rsky_syntax::handle::INVALID_HANDLE;
1212
use anyhow::{bail, Result};
1313
use aws_config::SdkConfig;
1414
use rocket::http::Status;

rsky-pds/src/apis/com/atproto/server/create_session.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::account_manager::helpers::account::AvailabilityFlags;
22
use crate::account_manager::AccountManager;
33
use crate::models::{ErrorCode, ErrorMessageResponse};
4-
use crate::INVALID_HANDLE;
4+
use rsky_syntax::handle::INVALID_HANDLE;
55
use anyhow::bail;
66
use rocket::http::Status;
77
use rocket::response::status;

rsky-pds/src/apis/com/atproto/server/get_session.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::account_manager::AccountManager;
22
use crate::auth_verifier::AccessStandard;
33
use crate::models::{ErrorCode, ErrorMessageResponse};
4-
use crate::INVALID_HANDLE;
4+
use rsky_syntax::handle::INVALID_HANDLE;
55
use rocket::http::Status;
66
use rocket::response::status;
77
use rocket::serde::json::Json;

rsky-pds/src/apis/com/atproto/server/refresh_session.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::account_manager::helpers::account::AvailabilityFlags;
22
use crate::account_manager::AccountManager;
33
use crate::auth_verifier::{Credentials, Refresh};
44
use crate::models::{ErrorCode, ErrorMessageResponse};
5-
use crate::INVALID_HANDLE;
5+
use rsky_syntax::handle::INVALID_HANDLE;
66
use anyhow::{bail, Result};
77
use rocket::http::Status;
88
use rocket::response::status;

rsky-pds/src/handle/errors.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use thiserror::Error;
2+
3+
#[derive(Error, Debug)]
4+
pub enum ErrorKind {
5+
#[error("Invalid handle")]
6+
InvalidHandle,
7+
#[error("Handle not available")]
8+
HandleNotAvailable,
9+
#[error("Unsupported domain")]
10+
UnsupportedDomain,
11+
#[error("Internal error")]
12+
InternalError,
13+
}
14+
15+
#[derive(Error, Debug)]
16+
#[error("{kind}: {message}")]
17+
pub struct Error {
18+
pub kind: ErrorKind,
19+
pub message: String,
20+
}
21+
22+
impl Error {
23+
pub fn new(kind: ErrorKind, message: &str) -> Self {
24+
Self {
25+
kind,
26+
message: message.to_string(),
27+
}
28+
}
29+
}
30+
31+
pub type Result<T> = std::result::Result<T, Error>;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use lazy_static::lazy_static;
2+
use regex::Regex;
3+
use rsky_common::explicit_slurs::contains_explicit_slurs;
4+
5+
lazy_static! {
6+
static ref EXPLICIT_SLUR_REGEXES: Vec<Regex> = vec![
7+
// Note: These are the same patterns from the TypeScript code but compiled as Rust regex
8+
Regex::new(r"\b[cĆćĈĉČčĊċÇçḈḉȻȼꞒꞓꟄꞔƇƈɕ][hĤĥȞȟḦḧḢḣḨḩḤḥḪḫH̱ẖĦħⱧⱨꞪɦꞕΗНн][iÍíi̇́Ììi̇̀ĬĭÎîǏǐÏïḮḯĨĩi̇̃ĮįĮ́į̇́Į̃į̇̃ĪīĪ̀ī̀ỈỉȈȉI̋i̋ȊȋỊịꞼꞽḬḭƗɨᶖİiIıIi1lĺľļḷḹl̃ḽḻłŀƚꝉⱡɫɬꞎꬷꬸꬹᶅɭȴLl][nŃńǸǹŇňÑñṄṅŅņṆṇṊṋṈṉN̈n̈ƝɲŊŋꞐꞑꞤꞥᵰᶇɳȵꬻꬼИиПпNn][kḰḱǨǩĶķḲḳḴḵƘƙⱩⱪᶄꝀꝁꝂꝃꝄꝅꞢꞣ][sŚśṤṥŜŝŠšṦṧṠṡŞşṢṣṨṩȘșS̩s̩ꞨꞩⱾȿꟅʂᶊᵴ]?\b").unwrap(),
9+
Regex::new(r"\b[cĆćĈĉČčĊċÇçḈḉȻȼꞒꞓꟄꞔƇƈɕ][ÓóÒòŎŏÔôỐốỒồỖỗỔổǑǒÖöȪȫŐőÕõṌṍṎṏȬȭȮȯO͘o͘ȰȱØøǾǿǪǫǬǭŌōṒṓṐṑỎỏȌȍȎȏƠơỚớỜờỠỡỞởỢợỌọỘộO̩o̩Ò̩ò̩Ó̩ó̩ƟɵꝊꝋꝌꝍⱺOo0]{2}[nŃńǸǹŇňÑñṄṅŅņṆṇṊṋṈṉN̈n̈ƝɲŊŋꞐꞑꞤꞥᵰᶇɳȵꬻꬼИиПпNn][sŚśṤṥŜŝŠšṦṧṠṡŞşṢṣṨṩȘșS̩s̩ꞨꞩⱾȿꟅʂᶊᵴ]?\b").unwrap(),
10+
Regex::new(r"\b[fḞḟƑƒꞘꞙᵮᶂ][aÁáÀàĂăẮắẰằẴẵẲẳÂâẤấẦầẪẫẨẩǍǎÅåǺǻÄäǞǟÃãȦȧǠǡĄąĄ́ą́Ą̃ą̃ĀāĀ̀ā̀ẢảȀȁA̋a̋ȂȃẠạẶặẬậḀḁȺⱥꞺꞻᶏẚAa@4][gǴǵĞğĜĝǦǧĠġG̃g̃ĢģḠḡǤǥꞠꞡƓɠᶃꬶGgqꝖꝗꝘꝙɋʠ]{1,2}([ÓóÒòŎŏÔôỐốỒồỖỗỔổǑǒÖöȪȫŐőÕõṌṍṎṏȬȭȮȯO͘o͘ȰȱØøǾǿǪǫǬǭŌōṒṓṐṑỎỏȌȍȎȏƠơỚớỜờỠỡỞởỢợỌọỘộO̩o̩Ò̩ò̩Ó̩ó̩ƟɵꝊꝋꝌꝍⱺOo0e3ЄєЕеÉéÈèĔĕÊêẾếỀềỄễỂểÊ̄ê̄Ê̌ê̌ĚěËëẼẽĖėĖ́ė́Ė̃ė̃ȨȩḜḝĘęĘ́ę́Ę̃ę̃ĒēḖḗḔḕẺẻȄȅE̋e̋ȆȇẸẹỆệḘḙḚḛɆɇE̩e̩È̩è̩É̩é̩ᶒⱸꬴꬳEeiÍíi̇́Ììi̇̀ĬĭÎîǏǐÏïḮḯĨĩi̇̃ĮįĮ́į̇́Į̃į̇̃ĪīĪ̀ī̀ỈỉȈȉI̋i̋ȊȋỊịꞼꞽḬḭƗɨᶖİiIıIi1lĺľļḷḹl̃ḽḻłŀƚꝉⱡɫɬꞎꬷꬸꬹᶅɭȴLl][tŤťṪṫŢţṬṭȚțṰṱṮṯŦŧȾⱦƬƭƮʈT̈ẗᵵƫȶ]{1,2}([rŔŕŘřṘṙŖŗȐȑȒȓṚṛṜṝṞṟR̃r̃ɌɍꞦꞧⱤɽᵲᶉꭉ][yÝýỲỳŶŷY̊ẙŸÿỸỹẎẏȲȳỶỷỴỵɎɏƳƴỾỿ]|[rŔŕŘřṘṙŖŗȐȑȒȓṚṛṜṝṞṟR̃r̃ɌɍꞦꞧⱤɽᵲᶉꭉ][iÍíi̇́Ììi̇̀ĬĭÎîǏǐÏïḮḯĨĩi̇̃ĮįĮ́į̇́Į̃į̇̃ĪīĪ̀ī̀ỈỉȈȉI̋i̋ȊȋỊịꞼꞽḬḭƗɨᶖİiIıIi1lĺľļḷḹl̃ḽḻłŀƚꝉⱡɫɬꞎꬷꬸꬹᶅɭȴLl][e3ЄєЕеÉéÈèĔĕÊêẾếỀềỄễỂểÊ̄ê̄Ê̌ê̌ĚěËëẼẽĖėĖ́ė́Ė̃ė̃ȨȩḜḝĘęĘ́ę́Ę̃ę̃ĒēḖḗḔḕẺẻȄȅE̋e̋ȆȇẸẹỆệḘḙḚḛɆɇE̩e̩È̩è̩É̩é̩ᶒⱸꬴꬳEe])?)?[sŚśṤṥŜŝŠšṦṧṠṡŞşṢṣṨṩȘșS̩s̩ꞨꞩⱾȿꟅʂᶊᵴ]?\b").unwrap(),
11+
Regex::new(r"\b[kḰḱǨǩĶķḲḳḴḵƘƙⱩⱪᶄꝀꝁꝂꝃꝄꝅꞢꞣ][iÍíi̇́Ììi̇̀ĬĭÎîǏǐÏïḮḯĨĩi̇̃ĮįĮ́į̇́Į̃į̇̃ĪīĪ̀ī̀ỈỉȈȉI̋i̋ȊȋỊịꞼꞽḬḭƗɨᶖİiIıIi1lĺľļḷḹl̃ḽḻłŀƚꝉⱡɫɬꞎꬷꬸꬹᶅɭȴLlyÝýỲỳŶŷY̊ẙŸÿỸỹẎẏȲȳỶỷỴỵɎɏƳƴỾỿ][kḰḱǨǩĶķḲḳḴḵƘƙⱩⱪᶄꝀꝁꝂꝃꝄꝅꞢꞣ][e3ЄєЕеÉéÈèĔĕÊêẾếỀềỄễỂểÊ̄ê̄Ê̌ê̌ĚěËëẼẽĖėĖ́ė́Ė̃ė̃ȨȩḜḝĘęĘ́ę́Ę̃ę̃ĒēḖḗḔḕẺẻȄȅE̋e̋ȆȇẸẹỆệḘḙḚḛɆɇE̩e̩È̩è̩É̩é̩ᶒⱸꬴꬳEe]([rŔŕŘřṘṙŖŗȐȑȒȓṚṛṜṝṞṟR̃r̃ɌɍꞦꞧⱤɽᵲᶉꭉ][yÝýỲỳŶŷY̊ẙŸÿỸỹẎẏȲȳỶỷỴỵɎɏƳƴỾỿ]|[rŔŕŘřṘṙŖŗȐȑȒȓṚṛṜṝṞṟR̃r̃ɌɍꞦꞧⱤɽᵲᶉꭉ][iÍíi̇́Ììi̇̀ĬĭÎîǏǐÏïḮḯĨĩi̇̃ĮįĮ́į̇́Į̃į̇̃ĪīĪ̀ī̀ỈỉȈȉI̋i̋ȊȋỊịꞼꞽḬḭƗɨᶖİiIıIi1lĺľļḷḹl̃ḽḻłŀƚꝉⱡɫɬꞎꬷꬸꬹᶅɭȴLl][e3ЄєЕеÉéÈèĔĕÊêẾếỀềỄễỂểÊ̄ê̄Ê̌ê̌ĚěËëẼẽĖėĖ́ė́Ė̃ė̃ȨȩḜḝĘęĘ́ę́Ę̃ę̃ĒēḖḗḔḕẺẻȄȅE̋e̋ȆȇẸẹỆệḘḙḚḛɆɇE̩e̩È̩è̩É̩é̩ᶒⱸꬴꬳEe])?[sŚśṤṥŜŝŠšṦṧṠṡŞşṢṣṨṩȘșS̩s̩ꞨꞩⱾȿꟅʂᶊᵴ]*\b").unwrap(),
12+
Regex::new(r"\b[nŃńǸǹŇňÑñṄṅŅņṆṇṊṋṈṉN̈n̈ƝɲŊŋꞐꞑꞤꞥᵰᶇɳȵꬻꬼИиПпNn][iÍíi̇́Ììi̇̀ĬĭÎîǏǐÏïḮḯĨĩi̇̃ĮįĮ́į̇́Į̃į̇̃ĪīĪ̀ī̀ỈỉȈȉI̋i̋ȊȋỊịꞼꞽḬḭƗɨᶖİiIıIi1lĺľļḷḹl̃ḽḻłŀƚꝉⱡɫɬꞎꬷꬸꬹᶅɭȴLloÓóÒòŎŏÔôỐốỒồỖỗỔổǑǒÖöȪȫŐőÕõṌṍṎṏȬȭȮȯO͘o͘ȰȱØøǾǿǪǫǬǭŌōṒṓṐṑỎỏȌȍȎȏƠơỚớỜờỠỡỞởỢợỌọỘộO̩o̩Ò̩ò̩Ó̩ó̩ƟɵꝊꝋꝌꝍⱺOoІіa4ÁáÀàĂăẮắẰằẴẵẲẳÂâẤấẦầẪẫẨẩǍǎÅåǺǻÄäǞǟÃãȦȧǠǡĄąĄ́ą́Ą̃ą̃ĀāĀ̀ā̀ẢảȀȁA̋a̋ȂȃẠạẶặẬậḀḁȺⱥꞺꞻᶏẚAa][gǴǵĞğĜĝǦǧĠġG̃g̃ĢģḠḡǤǥꞠꞡƓɠᶃꬶGgqꝖꝗꝘꝙɋʠ]{2}(l[e3ЄєЕеÉéÈèĔĕÊêẾếỀềỄễỂểÊ̄ê̄Ê̌ê̌ĚěËëẼẽĖėĖ́ė́Ė̃ė̃ȨȩḜḝĘęĘ́ę́Ę̃ę̃ĒēḖḗḔḕẺẻȄȅE̋e̋ȆȇẸẹỆệḘḙḚḛɆɇE̩e̩È̩è̩É̩é̩ᶒⱸꬴꬳEe]t|[e3ЄєЕеÉéÈèĔĕÊêẾếỀềỄễỂểÊ̄ê̄Ê̌ê̌ĚěËëẼẽĖėĖ́ė́Ė̃ė̃ȨȩḜḝĘęĘ́ę́Ę̃ę̃ĒēḖḗḔḕẺẻȄȅE̋e̋ȆȇẸẹỆệḘḙḚḛɆɇE̩e̩È̩è̩É̩é̩ᶒⱸꬴꬳEeaÁáÀàĂăẮắẰằẴẵẲẳÂâẤấẦầẪẫẨẩǍǎÅåǺǻÄäǞǟÃãȦȧǠǡĄąĄ́ą́Ą̃ą̃ĀāĀ̀ā̀ẢảȀȁA̋a̋ȂȃẠạẶặẬậḀḁȺⱥꞺꞻᶏẚAa][rŔŕŘřṘṙŖŗȐȑȒȓṚṛṜṝṞṟR̃r̃ɌɍꞦꞧⱤɽᵲᶉꭉ]?|n[ÓóÒòŎŏÔôỐốỒồỖỗỔổǑǒÖöȪȫŐőÕõṌṍṎṏȬȭȮȯO͘o͘ȰȱØøǾǿǪǫǬǭŌōṒṓṐṑỎỏȌȍȎȏƠơỚớỜờỠỡỞởỢợỌọỘộO̩o̩Ò̩ò̩Ó̩ó̩ƟɵꝊꝋꝌꝍⱺOo0][gǴǵĞğĜĝǦǧĠġG̃g̃ĢģḠḡǤǥꞠꞡƓɠᶃꬶGgqꝖꝗꝘꝙɋʠ]|[a4ÁáÀàĂăẮắẰằẴẵẲẳÂâẤấẦầẪẫẨẩǍǎÅåǺǻÄäǞǟÃãȦȧǠǡĄąĄ́ą́Ą̃ą̃ĀāĀ̀ā̀ẢảȀȁA̋a̋ȂȃẠạẶặẬậḀḁȺⱥꞺꞻᶏẚAa]?)?[sŚśṤṥŜŝŠšṦṧṠṡŞşṢṣṨṩȘșS̩s̩ꞨꞩⱾȿꟅʂᶊᵴ]?\b").unwrap(),
13+
Regex::new(r"[nŃńǸǹŇňÑñṄṅŅņṆṇṊṋṈṉN̈n̈ƝɲŊŋꞐꞑꞤꞥᵰᶇɳȵꬻꬼИиПпNn][iÍíi̇́Ììi̇̀ĬĭÎîǏǐÏïḮḯĨĩi̇̃ĮįĮ́į̇́Į̃į̇̃ĪīĪ̀ī̀ỈỉȈȉI̋i̋ȊȋỊịꞼꞽḬḭƗɨᶖİiIıIi1lĺľļḷḹl̃ḽḻłŀƚꝉⱡɫɬꞎꬷꬸꬹᶅɭȴLloÓóÒòŎŏÔôỐốỒồỖỗỔổǑǒÖöȪȫŐőÕõṌṍṎṏȬȭȮȯO͘o͘ȰȱØøǾǿǪǫǬǭŌōṒṓṐṑỎỏȌȍȎȏƠơỚớỜờỠỡỞởỢợỌọỘộO̩o̩Ò̩ò̩Ó̩ó̩ƟɵꝊꝋꝌꝍⱺOoІіa4ÁáÀàĂăẮắẰằẴẵẲẳÂâẤấẦầẪẫẨẩǍǎÅåǺǻÄäǞǟÃãȦȧǠǡĄąĄ́ą́Ą̃ą̃ĀāĀ̀ā̀ẢảȀȁA̋a̋ȂȃẠạẶặẬậḀḁȺⱥꞺꞻᶏẚAa][gǴǵĞğĜĝǦǧĠġG̃g̃ĢģḠḡǤǥꞠꞡƓɠᶃꬶGgqꝖꝗꝘꝙɋʠ]{2}(l[e3ЄєЕеÉéÈèĔĕÊêẾếỀềỄễỂểÊ̄ê̄Ê̌ê̌ĚěËëẼẽĖėĖ́ė́Ė̃ė̃ȨȩḜḝĘęĘ́ę́Ę̃ę̃ĒēḖḗḔḕẺẻȄȅE̋e̋ȆȇẸẹỆệḘḙḚḛɆɇE̩e̩È̩è̩É̩é̩ᶒⱸꬴꬳEe]t|[e3ЄєЕеÉéÈèĔĕÊêẾếỀềỄễỂểÊ̄ê̄Ê̌ê̌ĚěËëẼẽĖėĖ́ė́Ė̃ė̃ȨȩḜḝĘęĘ́ę́Ę̃ę̃ĒēḖḗḔḕẺẻȄȅE̋e̋ȆȇẸẹỆệḘḙḚḛɆɇE̩e̩È̩è̩É̩é̩ᶒⱸꬴꬳEe][rŔŕŘřṘṙŖŗȐȑȒȓṚṛṜṝṞṟR̃r̃ɌɍꞦꞧⱤɽᵲᶉꭉ])[sŚśṤṥŜŝŠšṦṧṠṡŞşṢṣṨṩȘșS̩s̩ꞨꞩⱾȿꟅʂᶊᵴ]?").unwrap(),
14+
Regex::new(r"\b[tŤťṪṫŢţṬṭȚțṰṱṮṯŦŧȾⱦƬƭƮʈT̈ẗᵵƫȶ][rŔŕŘřṘṙŖŗȐȑȒȓṚṛṜṝṞṟR̃r̃ɌɍꞦꞧⱤɽᵲᶉꭉ][aÁáÀàĂăẮắẰằẴẵẲẳÂâẤấẦầẪẫẨẩǍǎÅåǺǻÄäǞǟÃãȦȧǠǡĄąĄ́ą́Ą̃ą̃ĀāĀ̀ā̀ẢảȀȁA̋a̋ȂȃẠạẶặẬậḀḁȺⱥꞺꞻᶏẚAa4]+[nŃńǸǹŇňÑñṄṅŅņṆṇṊṋṈṉN̈n̈ƝɲŊŋꞐꞑꞤꞥᵰᶇɳȵꬻꬼИиПпNn]{1,2}([iÍíi̇́Ììi̇̀ĬĭÎîǏǐÏïḮḯĨĩi̇̃ĮįĮ́į̇́Į̃į̇̃ĪīĪ̀ī̀ỈỉȈȉI̋i̋ȊȋỊịꞼꞽḬḭƗɨᶖİiIıIi1lĺľļḷḹl̃ḽḻłŀƚꝉⱡɫɬꞎꬷꬸꬹᶅɭȴLl][e3ЄєЕеÉéÈèĔĕÊêẾếỀềỄễỂểÊ̄ê̄Ê̌ê̌ĚěËëẼẽĖėĖ́ė́Ė̃ė̃ȨȩḜḝĘęĘ́ę́Ę̃ę̃ĒēḖḗḔḕẺẻȄȅE̋e̋ȆȇẸẹỆệḘḙḚḛɆɇE̩e̩È̩è̩É̩é̩ᶒⱸꬴꬳEe]|[yÝýỲỳŶŷY̊ẙŸÿỸỹẎẏȲȳỶỷỴỵɎɏƳƴỾỿ]|[e3ЄєЕеÉéÈèĔĕÊêẾếỀềỄễỂểÊ̄ê̄Ê̌ê̌ĚěËëẼẽĖėĖ́ė́Ė̃ė̃ȨȩḜḝĘęĘ́ę́Ę̃ę̃ĒēḖḗḔḕẺẻȄȅE̋e̋ȆȇẸẹỆệḘḙḚḛɆɇE̩e̩È̩è̩É̩é̩ᶒⱸꬴꬳEe][rŔŕŘřṘṙŖŗȐȑȒȓṚṛṜṝṞṟR̃r̃ɌɍꞦꞧⱤɽᵲᶉꭉ])[sŚśṤṥŜŝŠšṦṧṠṡŞşṢṣṨṩȘșS̩s̩ꞨꞩⱾȿꟅʂᶊᵴ]?\b").unwrap(),
15+
].to_vec();
16+
}
17+
18+
pub fn has_explicit_slur(handle: &str) -> bool {
19+
// Create a cleaned version of the handle with dots, hyphens and underscores removed
20+
let cleaned_handle = handle.replace(['.', '-', '_'], "");
21+
// Check both the original handle and the cleaned version
22+
EXPLICIT_SLUR_REGEXES
23+
.iter()
24+
.any(|regex| regex.is_match(handle) || regex.is_match(&cleaned_handle))
25+
|| contains_explicit_slurs(handle)
26+
|| contains_explicit_slurs(&cleaned_handle)
27+
}

0 commit comments

Comments
 (0)