Skip to content

Commit

Permalink
Adds the ability to soft filter words, also fixes some word filter bu…
Browse files Browse the repository at this point in the history
…gs (#62158)
  • Loading branch information
NamelessFairy authored Oct 26, 2021
1 parent 6e8872f commit 1238ce2
Show file tree
Hide file tree
Showing 27 changed files with 186 additions and 27 deletions.
35 changes: 34 additions & 1 deletion code/__HELPERS/chat_filter.dm
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// [2] is the group index of the blocked term when it is not using word bounds.
// This is sanity checked by unit tests.
#define GET_MATCHED_GROUP(regex) (regex.group[2] || regex.match)
#define GET_MATCHED_GROUP(regex) (lowertext(regex.group[2] || regex.match))

/// Given a text, will return what word is on the IC filter, with the reason.
/// Returns null if the message is OK.
Expand Down Expand Up @@ -35,4 +35,37 @@

return null

/// Given a text, will return what word is on the soft IC filter, with the reason.
/// Returns null if the message is OK.
/proc/is_soft_ic_filtered(message)
if (config.soft_ic_filter_regex?.Find(message))
var/matched_group = GET_MATCHED_GROUP(config.soft_ic_filter_regex)
return list(
matched_group,
config.soft_ic_filter_reasons[matched_group] || config.soft_ic_outside_pda_filter_reasons[matched_group] || config.soft_shared_filter_reasons[matched_group],
)

return null

/// Given a text, will return what word is on the soft IC filter, ignoring words allowed on the PDA, with the reason.
/// Returns null if the message is OK.
/proc/is_soft_ic_filtered_for_pdas(message)
if (config.soft_ic_outside_pda_filter_regex?.Find(message))
var/matched_group = GET_MATCHED_GROUP(config.soft_ic_outside_pda_filter_regex)
return list(
matched_group,
config.soft_ic_filter_reasons[matched_group] || config.soft_shared_filter_reasons[matched_group],
)

return null

///Given a text, will return that word is on the soft OOC filter, with the reason.
/// Returns null if the message is OK.
/proc/is_soft_ooc_filtered(message)
if (config.soft_ooc_filter_regex?.Find(message))
var/matched_group = GET_MATCHED_GROUP(config.soft_ooc_filter_regex)
return list(matched_group, config.soft_shared_filter_reasons[matched_group])

return null

#undef GET_MATCHED_GROUP
4 changes: 2 additions & 2 deletions code/__HELPERS/text.dm
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

///returns nothing with an alert instead of the message if it contains something in the ic filter, and sanitizes normally if the name is fine. It returns nothing so it backs out of the input the same way as if you had entered nothing.
/proc/sanitize_name(t,allow_numbers=FALSE)
if(is_ic_filtered(t))
if(is_ic_filtered(t) || is_soft_ic_filtered(t))
tgui_alert(usr, "You cannot set a name that contains a word prohibited in IC chat!")
return ""
var/r = reject_bad_name(t,allow_numbers=allow_numbers,strict=TRUE)
Expand Down Expand Up @@ -221,7 +221,7 @@
return //(not case sensitive)

// Protects against names containing IC chat prohibited words.
if(is_ic_filtered(t_out))
if(is_ic_filtered(t_out) || is_soft_ic_filtered(t_out))
return

return t_out
Expand Down
33 changes: 32 additions & 1 deletion code/controllers/configuration/configuration.dm
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@

/// A regex that matches words blocked IC, but not in PDAs
var/static/regex/ic_outside_pda_filter_regex

/// A regex that matches words soft blocked IC
var/static/regex/soft_ic_filter_regex

/// A regex that matches words soft blocked OOC
var/static/regex/soft_ooc_filter_regex

/// A regex that matches words soft blocked IC, but not in PDAs
var/static/regex/soft_ic_outside_pda_filter_regex

/// An assoc list of blocked IC words to their reasons
var/static/list/ic_filter_reasons
Expand All @@ -42,6 +51,15 @@
/// An assoc list of words that are blocked both IC and OOC to their reasons
var/static/list/shared_filter_reasons

/// An assoc list of soft blocked IC words to their reasons
var/static/list/soft_ic_filter_reasons

/// An assoc list of words that are soft blocked IC, but not in PDAs, to their reasons
var/static/list/soft_ic_outside_pda_filter_reasons

/// An assoc list of words that are soft blocked both IC and OOC to their reasons
var/static/list/soft_shared_filter_reasons

/datum/controller/configuration/proc/admin_reload()
if(IsAdminAdvancedProcCall())
return
Expand Down Expand Up @@ -370,6 +388,9 @@ Example config:
ic_filter_reasons = try_extract_from_word_filter(word_filter, "ic")
ic_outside_pda_filter_reasons = try_extract_from_word_filter(word_filter, "ic_outside_pda")
shared_filter_reasons = try_extract_from_word_filter(word_filter, "shared")
soft_ic_filter_reasons = try_extract_from_word_filter(word_filter, "soft_ic")
soft_ic_outside_pda_filter_reasons = try_extract_from_word_filter(word_filter, "soft_ic_outside_pda")
soft_shared_filter_reasons = try_extract_from_word_filter(word_filter, "soft_shared")

update_chat_filter_regexes()

Expand All @@ -382,6 +403,9 @@ Example config:
ic_filter_reasons = list()
ic_outside_pda_filter_reasons = list()
shared_filter_reasons = list()
soft_ic_filter_reasons = list()
soft_ic_outside_pda_filter_reasons = list()
soft_shared_filter_reasons = list()

for (var/line in world.file2list("[directory]/in_character_filter.txt"))
if (!line)
Expand All @@ -398,6 +422,9 @@ Example config:
ic_filter_regex = compile_filter_regex(ic_filter_reasons + ic_outside_pda_filter_reasons + shared_filter_reasons)
ic_outside_pda_filter_regex = compile_filter_regex(ic_filter_reasons + shared_filter_reasons)
ooc_filter_regex = compile_filter_regex(shared_filter_reasons)
soft_ic_filter_regex = compile_filter_regex(soft_ic_filter_reasons + soft_ic_outside_pda_filter_reasons + soft_shared_filter_reasons)
soft_ic_outside_pda_filter_regex = compile_filter_regex(soft_ic_filter_reasons + soft_shared_filter_reasons)
soft_ooc_filter_regex = compile_filter_regex(soft_shared_filter_reasons)

/datum/controller/configuration/proc/try_extract_from_word_filter(list/word_filter, key)
var/list/banned_words = word_filter[key]
Expand All @@ -410,7 +437,11 @@ Example config:
DelayedMessageAdmins(message)
return list()

return banned_words
var/list/formatted_banned_words = list()

for (var/banned_word in banned_words)
formatted_banned_words[lowertext(banned_word)] = banned_words[banned_word]
return formatted_banned_words

/datum/controller/configuration/proc/compile_filter_regex(list/banned_words)
if (isnull(banned_words) || banned_words.len == 0)
Expand Down
2 changes: 1 addition & 1 deletion code/datums/brain_damage/imaginary_friend.dm
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@
client.images.Remove(human_image)
return ..()

/mob/camera/imaginary_friend/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
/mob/camera/imaginary_friend/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null)
if (!message)
return

Expand Down
2 changes: 1 addition & 1 deletion code/datums/brain_damage/split_personality.dm
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@
to_chat(src, span_notice("As a split personality, you cannot do anything but observe. However, you will eventually gain control of your body, switching places with the current personality."))
to_chat(src, span_warning("<b>Do not commit suicide or put the body in a deadly position. Behave like you care about it as much as the owner.</b>"))

/mob/living/split_personality/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
/mob/living/split_personality/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null)
to_chat(src, span_warning("You cannot speak, your other self is controlling your body!"))
return FALSE

Expand Down
2 changes: 1 addition & 1 deletion code/game/atoms.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1313,7 +1313,7 @@
if(href_list[VV_HK_AUTO_RENAME] && check_rights(R_VAREDIT))
var/newname = input(usr, "What do you want to rename this to?", "Automatic Rename") as null|text
// Check the new name against the chat filter. If it triggers the IC chat filter, give an option to confirm.
if(newname && !(is_ic_filtered(newname) && tgui_alert(usr, "Your selected name contains words restricted by IC chat filters. Confirm this new name?", "IC Chat Filter Conflict", list("Confirm", "Cancel")) != "Confirm"))
if(newname && !(is_ic_filtered(newname) || is_soft_ic_filtered(newname) && tgui_alert(usr, "Your selected name contains words restricted by IC chat filters. Confirm this new name?", "IC Chat Filter Conflict", list("Confirm", "Cancel")) != "Confirm"))
vv_auto_rename(newname)

if(href_list[VV_HK_EDIT_FILTERS] && check_rights(R_VAREDIT))
Expand Down
18 changes: 18 additions & 0 deletions code/game/objects/items/AI_modules.dm
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,12 @@ AI MODULES
if(is_ic_filtered(targName))
to_chat(user, span_warning("Error: Law contains invalid text.")) // AI LAW 2 SAY U W U WITHOUT THE SPACES
return
var/list/soft_filter_result = is_soft_ooc_filtered(targName)
if(soft_filter_result)
if(tgui_alert(user,"Your law contains \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\". \"[soft_filter_result[CHAT_FILTER_INDEX_REASON]]\", Are you sure you want to use it?", "Soft Blocked Word", list("Yes", "No")) != "Yes")
return
message_admins("[ADMIN_LOOKUPFLW(user)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term for an AI law. Law: \"[html_encode(targName)]\"")
log_admin_private("[key_name(user)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term for an AI law. Law: \"[targName]\"")
laws[1] = targName
..()

Expand Down Expand Up @@ -468,6 +474,12 @@ AI MODULES
if(is_ic_filtered(targName))
to_chat(user, span_warning("Error: Law contains invalid text."))
return
var/list/soft_filter_result = is_soft_ooc_filtered(targName)
if(soft_filter_result)
if(tgui_alert(user,"Your law contains \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\". \"[soft_filter_result[CHAT_FILTER_INDEX_REASON]]\", Are you sure you want to use it?", "Soft Blocked Word", list("Yes", "No")) != "Yes")
return
message_admins("[ADMIN_LOOKUPFLW(user)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term for an AI law. Law: \"[html_encode(targName)]\"")
log_admin_private("[key_name(user)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term for an AI law. Law: \"[targName]\"")
laws[1] = targName
..()

Expand All @@ -490,6 +502,12 @@ AI MODULES
if(is_ic_filtered(targName)) // not even the syndicate can uwu
to_chat(user, span_warning("Error: Law contains invalid text."))
return
var/list/soft_filter_result = is_soft_ooc_filtered(targName)
if(soft_filter_result)
if(tgui_alert(user,"Your law contains \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\". \"[soft_filter_result[CHAT_FILTER_INDEX_REASON]]\", Are you sure you want to use it?", "Soft Blocked Word", list("Yes", "No")) != "Yes")
return
message_admins("[ADMIN_LOOKUPFLW(user)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term for an AI law. Law: \"[html_encode(targName)]\"")
log_admin_private("[key_name(user)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term for an AI law. Law: \"[targName]\"")
laws[1] = targName
..()

Expand Down
7 changes: 7 additions & 0 deletions code/game/objects/items/devices/PDA/PDA.dm
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,13 @@ GLOBAL_LIST_EMPTY(PDAs)
REPORT_CHAT_FILTER_TO_USER(user, filter_result)
return

var/list/soft_filter_result = is_soft_ic_filtered_for_pdas(message)
if (soft_filter_result)
if(tgui_alert(usr,"Your message contains \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\". \"[soft_filter_result[CHAT_FILTER_INDEX_REASON]]\", Are you sure you want to send it?", "Soft Blocked Word", list("Yes", "No")) != "Yes")
return
message_admins("[ADMIN_LOOKUPFLW(usr)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term in PDA messages. Message: \"[html_encode(message)]\"")
log_admin_private("[key_name(usr)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term in PDA messages. Message: \"[message]\"")

if(prob(1))
message += "\nSent from my PDA"
// Send the signal
Expand Down
2 changes: 1 addition & 1 deletion code/game/say.dm
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ GLOBAL_LIST_INIT(freqtospan, list(
"[FREQ_CTF_YELLOW]" = "yellowteamradio"
))

/atom/movable/proc/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
/atom/movable/proc/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null)
if(!can_speak())
return
if(sanitize)
Expand Down
2 changes: 1 addition & 1 deletion code/modules/admin/view_variables/topic.dm
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

// If the new name is something that would be restricted by IC chat filters,
// give the admin a warning but allow them to do it anyway if they want.
if(is_ic_filtered(new_name) && tgui_alert(usr, "Your selected name contains words restricted by IC chat filters. Confirm this new name?", "IC Chat Filter Conflict", list("Confirm", "Cancel")) == "Cancel")
if(is_ic_filtered(new_name) || is_soft_ic_filtered(new_name) && tgui_alert(usr, "Your selected name contains words restricted by IC chat filters. Confirm this new name?", "IC Chat Filter Conflict", list("Confirm", "Cancel")) == "Cancel")
return

if( !new_name || !M )
Expand Down
2 changes: 1 addition & 1 deletion code/modules/antagonists/blob/blob_mobs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
return 1
return ..()

/mob/living/simple_animal/hostile/blob/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
/mob/living/simple_animal/hostile/blob/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null)
if(sanitize)
message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN))
var/spanned_message = say_quote(message)
Expand Down
2 changes: 1 addition & 1 deletion code/modules/antagonists/blob/overmind.dm
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
blob_points = clamp(blob_points + points, 0, max_blob_points)
hud_used.blobpwrdisplay.maptext = MAPTEXT("<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#e36600'>[round(blob_points)]</font></div>")

/mob/camera/blob/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
/mob/camera/blob/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null)
if (!message)
return

Expand Down
9 changes: 8 additions & 1 deletion code/modules/antagonists/cult/cult_comms.dm
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,21 @@
if(filter_result)
REPORT_CHAT_FILTER_TO_USER(usr, filter_result)
return

var/list/soft_filter_result = is_soft_ic_filtered(input)
if(soft_filter_result)
if(tgui_alert(usr,"Your message contains \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\". \"[soft_filter_result[CHAT_FILTER_INDEX_REASON]]\", Are you sure you want to say it?", "Soft Blocked Word", list("Yes", "No")) != "Yes")
return
message_admins("[ADMIN_LOOKUPFLW(usr)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term. Message: \"[html_encode(input)]\"")
log_admin_private("[key_name(usr)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term. Message: \"[input]\"")
cultist_commune(usr, input)

/datum/action/innate/cult/comm/proc/cultist_commune(mob/living/user, message)
var/my_message
if(!message)
return
user.whisper("O bidai nabora se[pick("'","`")]sma!", language = /datum/language/common)
user.whisper(html_decode(message))
user.whisper(html_decode(message), filterproof = TRUE)
var/title = "Acolyte"
var/span = "cult italic"
if(user.mind && user.mind.has_antag_datum(/datum/antagonist/cult/master))
Expand Down
2 changes: 1 addition & 1 deletion code/modules/antagonists/disease/disease_mob.dm
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ the new instance inside the host to be updated to the template's stats.
for(var/datum/disease_ability/ability in purchased_abilities)
. += span_notice("[ability.name]")

/mob/camera/disease/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
/mob/camera/disease/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null)
if(!message)
return
if(sanitize)
Expand Down
2 changes: 1 addition & 1 deletion code/modules/antagonists/revenant/revenant.dm
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@
/mob/living/simple_animal/revenant/med_hud_set_status()
return //we use no hud

/mob/living/simple_animal/revenant/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
/mob/living/simple_animal/revenant/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null)
if(!message)
return
if(sanitize)
Expand Down
7 changes: 7 additions & 0 deletions code/modules/client/verbs/ooc.dm
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8")
REPORT_CHAT_FILTER_TO_USER(usr, filter_result)
return

var/list/soft_filter_result = is_soft_ooc_filtered(msg)
if (soft_filter_result)
if(tgui_alert(usr,"Your message contains \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\". \"[soft_filter_result[CHAT_FILTER_INDEX_REASON]]\", Are you sure you want to say it?", "Soft Blocked Word", list("Yes", "No")) != "Yes")
return
message_admins("[ADMIN_LOOKUPFLW(usr)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term. Message: \"[msg]\"")
log_admin_private("[key_name(usr)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term. Message: \"[msg]\"")

if(!msg)
return

Expand Down
15 changes: 14 additions & 1 deletion code/modules/mob/dead/observer/observer_say.dm
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,21 @@
mods[RADIO_EXTENSION] = GLOB.department_radio_keys[mods[RADIO_KEY]]
return message

/mob/dead/observer/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
/mob/dead/observer/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null)
message = trim(message) //trim now and sanitize after checking for special admin radio keys

var/list/filter_result = is_ooc_filtered(message)
if (filter_result)
REPORT_CHAT_FILTER_TO_USER(usr, filter_result)
return

var/list/soft_filter_result = is_soft_ooc_filtered(message)
if (soft_filter_result)
if(tgui_alert(usr,"Your message contains \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\". \"[soft_filter_result[CHAT_FILTER_INDEX_REASON]]\", Are you sure you want to say it?", "Soft Blocked Word", list("Yes", "No")) != "Yes")
return
message_admins("[ADMIN_LOOKUPFLW(usr)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term. Message: \"[message]\"")
log_admin_private("[key_name(usr)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term. Message: \"[message]\"")

if(!message)
return
var/list/message_mods = list()
Expand Down
2 changes: 1 addition & 1 deletion code/modules/mob/living/brain/brain_say.dm
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/mob/living/brain/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
/mob/living/brain/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterpoof = null)
if(!(container && istype(container, /obj/item/mmi)))
return //No MMI, can't speak, bucko./N
else
Expand Down
Loading

0 comments on commit 1238ce2

Please sign in to comment.