2021-12-02 18:49:19 -06:00
|
|
|
|
using System.Diagnostics.CodeAnalysis;
|
2024-10-17 09:01:32 -05:00
|
|
|
|
using System.Text.RegularExpressions;
|
2021-12-02 18:49:19 -06:00
|
|
|
|
using Content.Shared.CCVar;
|
|
|
|
|
|
using Robust.Shared.Configuration;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Content.Server.Chat.Managers;
|
|
|
|
|
|
|
2024-10-17 09:01:32 -05:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Sanitizes messages!
|
|
|
|
|
|
/// It currently ony removes the shorthands for emotes (like "lol" or "^-^") from a chat message and returns the last
|
|
|
|
|
|
/// emote in their message
|
|
|
|
|
|
/// </summary>
|
2022-02-16 00:23:23 -07:00
|
|
|
|
public sealed class ChatSanitizationManager : IChatSanitizationManager
|
2021-12-02 18:49:19 -06:00
|
|
|
|
{
|
2024-10-17 09:01:32 -05:00
|
|
|
|
private static readonly Dictionary<string, string> ShorthandToEmote = new()
|
2021-12-02 18:49:19 -06:00
|
|
|
|
{
|
2024-06-07 01:16:04 +10:00
|
|
|
|
// CP14-RU-Localization-Start
|
|
|
|
|
|
{ "лол", "chatsan-laughs" },
|
|
|
|
|
|
{ "хд", "chatsan-laughs" },
|
|
|
|
|
|
{ "о-о", "chatsan-wide-eyed" }, // cyrillic о
|
|
|
|
|
|
{ "о.о", "chatsan-wide-eyed" }, // cyrillic о
|
|
|
|
|
|
{ "0_о", "chatsan-wide-eyed" }, // cyrillic о
|
|
|
|
|
|
{ "о/", "chatsan-waves" }, // cyrillic о
|
|
|
|
|
|
{ "о7", "chatsan-salutes" }, // cyrillic о
|
|
|
|
|
|
{ "0_o", "chatsan-wide-eyed" },
|
|
|
|
|
|
{ "лмао", "chatsan-laughs" },
|
|
|
|
|
|
{ "рофл", "chatsan-laughs" },
|
|
|
|
|
|
{ "яхз", "chatsan-shrugs" },
|
|
|
|
|
|
{ ":0", "chatsan-surprised" },
|
|
|
|
|
|
{ ":р", "chatsan-stick-out-tongue" }, // cyrillic р
|
|
|
|
|
|
{ "кек", "chatsan-laughs" },
|
|
|
|
|
|
{ "T_T", "chatsan-cries" },
|
|
|
|
|
|
{ "Т_Т", "chatsan-cries" }, // cyrillic T
|
|
|
|
|
|
{ "=_(", "chatsan-cries" },
|
|
|
|
|
|
{ "!с", "chatsan-laughs" },
|
|
|
|
|
|
{ "!в", "chatsan-sighs" },
|
|
|
|
|
|
{ "!х", "chatsan-claps" },
|
|
|
|
|
|
{ "!щ", "chatsan-snaps" },
|
|
|
|
|
|
{ "))", "chatsan-smiles-widely" },
|
|
|
|
|
|
{ ")", "chatsan-smiles" },
|
|
|
|
|
|
{ "((", "chatsan-frowns-deeply" },
|
|
|
|
|
|
{ "(", "chatsan-frowns" },
|
|
|
|
|
|
// CP14-RU-Localization-End
|
2021-12-02 18:49:19 -06:00
|
|
|
|
// I could've done this with regex, but felt it wasn't the right idea.
|
|
|
|
|
|
{ ":)", "chatsan-smiles" },
|
|
|
|
|
|
{ ":]", "chatsan-smiles" },
|
|
|
|
|
|
{ "=)", "chatsan-smiles" },
|
|
|
|
|
|
{ "=]", "chatsan-smiles" },
|
|
|
|
|
|
{ "(:", "chatsan-smiles" },
|
|
|
|
|
|
{ "[:", "chatsan-smiles" },
|
|
|
|
|
|
{ "(=", "chatsan-smiles" },
|
|
|
|
|
|
{ "[=", "chatsan-smiles" },
|
2022-10-17 07:54:51 +03:00
|
|
|
|
{ "^^", "chatsan-smiles" },
|
|
|
|
|
|
{ "^-^", "chatsan-smiles" },
|
2021-12-02 18:49:19 -06:00
|
|
|
|
{ ":(", "chatsan-frowns" },
|
|
|
|
|
|
{ ":[", "chatsan-frowns" },
|
|
|
|
|
|
{ "=(", "chatsan-frowns" },
|
|
|
|
|
|
{ "=[", "chatsan-frowns" },
|
|
|
|
|
|
{ "):", "chatsan-frowns" },
|
|
|
|
|
|
{ ")=", "chatsan-frowns" },
|
|
|
|
|
|
{ "]:", "chatsan-frowns" },
|
|
|
|
|
|
{ "]=", "chatsan-frowns" },
|
|
|
|
|
|
{ ":D", "chatsan-smiles-widely" },
|
|
|
|
|
|
{ "D:", "chatsan-frowns-deeply" },
|
|
|
|
|
|
{ ":O", "chatsan-surprised" },
|
2024-10-24 00:10:13 -05:00
|
|
|
|
{ ":3", "chatsan-smiles" },
|
2021-12-02 18:49:19 -06:00
|
|
|
|
{ ":S", "chatsan-uncertain" },
|
|
|
|
|
|
{ ":>", "chatsan-grins" },
|
|
|
|
|
|
{ ":<", "chatsan-pouts" },
|
|
|
|
|
|
{ "xD", "chatsan-laughs" },
|
2024-02-13 23:43:20 +08:00
|
|
|
|
{ ":'(", "chatsan-cries" },
|
|
|
|
|
|
{ ":'[", "chatsan-cries" },
|
|
|
|
|
|
{ "='(", "chatsan-cries" },
|
|
|
|
|
|
{ "='[", "chatsan-cries" },
|
|
|
|
|
|
{ ")':", "chatsan-cries" },
|
|
|
|
|
|
{ "]':", "chatsan-cries" },
|
|
|
|
|
|
{ ")'=", "chatsan-cries" },
|
|
|
|
|
|
{ "]'=", "chatsan-cries" },
|
2021-12-02 18:49:19 -06:00
|
|
|
|
{ ";-;", "chatsan-cries" },
|
|
|
|
|
|
{ ";_;", "chatsan-cries" },
|
2022-01-05 20:51:48 -05:00
|
|
|
|
{ "qwq", "chatsan-cries" },
|
2021-12-02 18:49:19 -06:00
|
|
|
|
{ ":u", "chatsan-smiles-smugly" },
|
|
|
|
|
|
{ ":v", "chatsan-smiles-smugly" },
|
|
|
|
|
|
{ ">:i", "chatsan-annoyed" },
|
|
|
|
|
|
{ ":i", "chatsan-sighs" },
|
|
|
|
|
|
{ ":|", "chatsan-sighs" },
|
|
|
|
|
|
{ ":p", "chatsan-stick-out-tongue" },
|
2024-02-13 23:43:20 +08:00
|
|
|
|
{ ";p", "chatsan-stick-out-tongue" },
|
2021-12-02 18:49:19 -06:00
|
|
|
|
{ ":b", "chatsan-stick-out-tongue" },
|
|
|
|
|
|
{ "0-0", "chatsan-wide-eyed" },
|
|
|
|
|
|
{ "o-o", "chatsan-wide-eyed" },
|
|
|
|
|
|
{ "o.o", "chatsan-wide-eyed" },
|
|
|
|
|
|
{ "._.", "chatsan-surprised" },
|
|
|
|
|
|
{ ".-.", "chatsan-confused" },
|
|
|
|
|
|
{ "-_-", "chatsan-unimpressed" },
|
2023-10-21 15:43:11 -04:00
|
|
|
|
{ "smh", "chatsan-unimpressed" },
|
2021-12-02 18:49:19 -06:00
|
|
|
|
{ "o/", "chatsan-waves" },
|
|
|
|
|
|
{ "^^/", "chatsan-waves" },
|
|
|
|
|
|
{ ":/", "chatsan-uncertain" },
|
|
|
|
|
|
{ ":\\", "chatsan-uncertain" },
|
2021-12-13 19:33:46 -06:00
|
|
|
|
{ "lmao", "chatsan-laughs" },
|
2024-08-20 00:03:07 +10:00
|
|
|
|
{ "lmfao", "chatsan-laughs" },
|
2021-12-13 19:33:46 -06:00
|
|
|
|
{ "lol", "chatsan-laughs" },
|
2022-01-05 20:51:48 -05:00
|
|
|
|
{ "lel", "chatsan-laughs" },
|
|
|
|
|
|
{ "kek", "chatsan-laughs" },
|
2023-10-21 15:43:11 -04:00
|
|
|
|
{ "rofl", "chatsan-laughs" },
|
2022-01-05 20:51:48 -05:00
|
|
|
|
{ "o7", "chatsan-salutes" },
|
2024-10-17 09:01:32 -05:00
|
|
|
|
{ ";_;7", "chatsan-tearfully-salutes" },
|
2023-10-15 17:06:55 -04:00
|
|
|
|
{ "idk", "chatsan-shrugs" },
|
2024-02-13 23:43:20 +08:00
|
|
|
|
{ ";)", "chatsan-winks" },
|
|
|
|
|
|
{ ";]", "chatsan-winks" },
|
|
|
|
|
|
{ "(;", "chatsan-winks" },
|
|
|
|
|
|
{ "[;", "chatsan-winks" },
|
|
|
|
|
|
{ ":')", "chatsan-tearfully-smiles" },
|
|
|
|
|
|
{ ":']", "chatsan-tearfully-smiles" },
|
|
|
|
|
|
{ "=')", "chatsan-tearfully-smiles" },
|
|
|
|
|
|
{ "=']", "chatsan-tearfully-smiles" },
|
|
|
|
|
|
{ "(':", "chatsan-tearfully-smiles" },
|
|
|
|
|
|
{ "[':", "chatsan-tearfully-smiles" },
|
|
|
|
|
|
{ "('=", "chatsan-tearfully-smiles" },
|
2024-10-17 09:01:32 -05:00
|
|
|
|
{ "['=", "chatsan-tearfully-smiles" }
|
2021-12-02 18:49:19 -06:00
|
|
|
|
};
|
|
|
|
|
|
|
2024-10-17 09:01:32 -05:00
|
|
|
|
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
|
|
|
|
|
[Dependency] private readonly ILocalizationManager _loc = default!;
|
|
|
|
|
|
|
2022-01-11 06:48:18 -08:00
|
|
|
|
private bool _doSanitize;
|
2021-12-02 18:49:19 -06:00
|
|
|
|
|
|
|
|
|
|
public void Initialize()
|
|
|
|
|
|
{
|
2022-01-11 06:48:18 -08:00
|
|
|
|
_configurationManager.OnValueChanged(CCVars.ChatSanitizerEnabled, x => _doSanitize = x, true);
|
2021-12-02 18:49:19 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-17 09:01:32 -05:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Remove the shorthands from the message, returning the last one found as the emote
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="message">The pre-sanitized message</param>
|
|
|
|
|
|
/// <param name="speaker">The speaker</param>
|
|
|
|
|
|
/// <param name="sanitized">The sanitized message with shorthands removed</param>
|
|
|
|
|
|
/// <param name="emote">The localized emote</param>
|
|
|
|
|
|
/// <returns>True if emote has been sanitized out</returns>
|
|
|
|
|
|
public bool TrySanitizeEmoteShorthands(string message,
|
|
|
|
|
|
EntityUid speaker,
|
|
|
|
|
|
out string sanitized,
|
|
|
|
|
|
[NotNullWhen(true)] out string? emote)
|
2021-12-02 18:49:19 -06:00
|
|
|
|
{
|
2024-10-17 09:01:32 -05:00
|
|
|
|
emote = null;
|
|
|
|
|
|
sanitized = message;
|
|
|
|
|
|
|
2022-01-11 06:48:18 -08:00
|
|
|
|
if (!_doSanitize)
|
2021-12-02 18:49:19 -06:00
|
|
|
|
return false;
|
|
|
|
|
|
|
2024-10-17 09:01:32 -05:00
|
|
|
|
// -1 is just a canary for nothing found yet
|
|
|
|
|
|
var lastEmoteIndex = -1;
|
2021-12-02 18:49:19 -06:00
|
|
|
|
|
2024-10-17 09:01:32 -05:00
|
|
|
|
foreach (var (shorthand, emoteKey) in ShorthandToEmote)
|
2021-12-02 18:49:19 -06:00
|
|
|
|
{
|
2024-10-17 09:01:32 -05:00
|
|
|
|
// We have to escape it because shorthands like ":)" or "-_-" would break the regex otherwise.
|
|
|
|
|
|
var escaped = Regex.Escape(shorthand);
|
|
|
|
|
|
|
|
|
|
|
|
// So there are 2 cases:
|
|
|
|
|
|
// - If there is whitespace before it and after it is either punctuation, whitespace, or the end of the line
|
|
|
|
|
|
// Delete the word and the whitespace before
|
|
|
|
|
|
// - If it is at the start of the string and is followed by punctuation, whitespace, or the end of the line
|
|
|
|
|
|
// Delete the word and the punctuation if it exists.
|
|
|
|
|
|
var pattern =
|
|
|
|
|
|
$@"\s{escaped}(?=\p{{P}}|\s|$)|^{escaped}(?:\p{{P}}|(?=\s|$))";
|
|
|
|
|
|
|
|
|
|
|
|
var r = new Regex(pattern, RegexOptions.RightToLeft | RegexOptions.IgnoreCase);
|
|
|
|
|
|
|
|
|
|
|
|
// We're using sanitized as the original message until the end so that we can make sure the indices of
|
|
|
|
|
|
// the emotes are accurate.
|
|
|
|
|
|
var lastMatch = r.Match(sanitized);
|
|
|
|
|
|
|
|
|
|
|
|
if (!lastMatch.Success)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
if (lastMatch.Index > lastEmoteIndex)
|
2021-12-02 18:49:19 -06:00
|
|
|
|
{
|
2024-10-17 09:01:32 -05:00
|
|
|
|
lastEmoteIndex = lastMatch.Index;
|
|
|
|
|
|
emote = _loc.GetString(emoteKey, ("ent", speaker));
|
2021-12-02 18:49:19 -06:00
|
|
|
|
}
|
2024-10-17 09:01:32 -05:00
|
|
|
|
|
|
|
|
|
|
message = r.Replace(message, string.Empty);
|
2021-12-02 18:49:19 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-17 09:01:32 -05:00
|
|
|
|
sanitized = message.Trim();
|
|
|
|
|
|
return emote is not null;
|
2021-12-02 18:49:19 -06:00
|
|
|
|
}
|
|
|
|
|
|
}
|