C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
// Requires: BetterChat
// Requires: BetterChatMute
using System;
using System.Collections.Generic;
using System.Linq;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using System.Text.RegularExpressions;
using Oxide.Game.Rust.Libraries;
namespace Oxide.Plugins
{
[Info("Better Chat Filter", "NooBlet", "1.7.4", ResourceId = 2403)]
[Description("Filter for Better Chat")]
public class BetterChatFilter : CovalencePlugin
{
[PluginReference] private Plugin BetterChat, BetterChatMute, EnhancedBanSystem;
//////////////////////////////////////////////////////////////////////////////////
#region BetterChatHook
private object OnBetterChat(Dictionary<string, object> messageData) => Filter(messageData);
private object Filter(Dictionary<string, object> messageData)
{
IPlayer player = (IPlayer)messageData["Player"];
if (BlockSpecialCharacters && hasSpecialChar((string)messageData["Message"]))
{
player.Message(GetLang("CantUseSpecialCharacters", player.Id));
messageData["CancelOption"] = 2;
return messageData;
}
if (ExcludeTeamChat)
{
if (messageData["ChatChannel"].ToString() == "Team") { return messageData; }
}
if (MustExclude(player)) { return messageData; }
if (WordFilter_Enabled)
{
var message = (string)messageData["Message"];
if (!GetIsMuted(ref player)) { messageData["Message"] = FilterText(player, message); }
if ((string)messageData["Message"] == "")
{
messageData["CancelOption"] = 2;
}
return messageData;
}
return messageData;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////
#region Data
private readonly Player Player = Interface.Oxide.GetLibrary<Player>();
private static OffenseData offensedata;
public Dictionary<string, OffenseData> PlayerOffenses = new Dictionary<string, OffenseData>();
public class OffenseData
{
public int offenses { get; set; }
public DateTime timesinsoffense { get; set; }
public OffenseData()
{
offenses = 1;
}
public OffenseData(int offenses)
{
this.offenses = offenses;
}
}
#endregion
#region Cached Variables
private bool FilterAll = false;
private bool WordFilter_Enabled = true;
private string WordFilter_Replacement = "*";
private bool WordFilter_UseCustomReplacement = false;
private string WordFilter_CustomReplacement = "Unicorn";
private List<object> WordFilter_Phrases = new List<object> {
"bitch",
"cunt",
"nigger",
"nig",
"faggot",
"fuck"
};
private List<object> GroupsToExclude = new List<object> {
"admin",
};
private List<object> WordWhiteList = new List<object> {
"night",
};
private int MuteCount = 3;
private int KickCount = 3;
private int BanCount = 20;
private int BanTimeMin = 30;
private bool BroadcastKick = true;
private bool BroadcastBan = true;
private int TimeToMute = 300;
private bool UseRegex = false;
private string regextouse = @"";
private int clear = 0;
private bool ExcludeTeamChat = false;
private bool warnoffenseamount = false;
private bool BlockSpecialCharacters = false;
#endregion
#region Plugin General
public static bool hasSpecialChar(string input)
{
Regex r = new Regex("^[a-zA-Z0-9.,-_ \\[\\]\\\\'~`?<>;:/!()*&%$#@=+|{}\"-]*$");
if (!r.IsMatch(input))
{
return true;
}
return false;
}
private string ListToString<T>(List<T> list, int first = 0, string seperator = ", ")
{
return string.Join(seperator, (from val in list select val.ToString()).Skip(first).ToArray());
}
private void SaveData() => Interface.Oxide.DataFileSystem.WriteObject("BetterChatFilter", PlayerOffenses);
private string GetLang(string key, string id) => lang.GetMessage(key, this, id);
private void Loaded()
{
LoadConfiguration();
LoadData();
permission.RegisterPermission(Name + ".admin", this);
}
void Unload()
{
}
private void Offsense(IPlayer player)
{
int offenseCount = 0;
if (PlayerOffenses.ContainsKey(player.Id))
{
PlayerOffenses[player.Id].offenses++;
offenseCount = PlayerOffenses[player.Id].offenses;
SaveData();
}
else if (!(PlayerOffenses.ContainsKey(player.Id)))
{
PlayerOffenses.Add(player.Id, new OffenseData());
offenseCount = PlayerOffenses[player.Id].offenses;
SaveData();
}
if (offenseCount >= MuteCount && MuteCount != 0)
{
if (clear == 1 || clear == 3)
{
ClearOffense(player);
}
//mute check suggested OuTSMoKE
if (!GetIsMuted(ref player))
{
server.Command("mute", player.Id, $"{TimeToMute}s", $"{GetLang("MuteReason", player.Id)}");
}
}
if (offenseCount >= KickCount && KickCount != 0)
{
if (clear == 1 || clear == 2)
{
ClearOffense(player);
}
if (BroadcastKick) server.Broadcast(string.Format(GetLang("BroadcastKickFormat", null), player.Name, GetLang("KickReason", null)));
player.Kick(GetLang("KickReason", player.Id));
}
if (offenseCount >= BanCount && BanCount != 0)
{
if (clear == 1 || clear == 4)
{
ClearOffense(player);
}
if (BroadcastBan) server.Broadcast(string.Format(GetLang("BroadcastBanFormat", null), player.Name, GetLang("BanReason", null)));
BanPlayer(player, BanTimeMin, GetLang("BanReason", player.Id));
}
SaveData();
return;
}
private void BanPlayer(IPlayer player, int time, string reason)
{
if (BanTimeMin == 0)
{
if (EnhancedBanSystem) { server.Command($"ban {player.Id} {reason}"); return; }
player.Ban(reason);
}
else
{
if (EnhancedBanSystem) { server.Command($"ban {player.Id} {time}m {reason}"); return; }
Player.Ban(player.Id, reason);
float t = (float)BanTimeMin * 60;
timer.Once(t, () =>
{
Player.Unban(player.Id);
});
}
}
private void WarnPlayer(IPlayer player)
{
if (warnoffenseamount)
{
if (PlayerOffenses.ContainsKey(player.Id))
{
if (MuteCount != 0)
{
player.Reply(string.Format(GetLang("OffenseWarning", player.Id), PlayerOffenses[player.Id].offenses + 1));
}
if (KickCount >= PlayerOffenses[player.Id].offenses)
{
if (BroadcastKick)
{
if (KickCount != 0)
{
player.Reply(string.Format(GetLang("OffenseWarningKick", player.Id), PlayerOffenses[player.Id].offenses + 1, KickCount));
}
}
}
if (BroadcastBan)
{
if (BanCount != 0)
{
player.Reply(string.Format(GetLang("OffenseWarningBan", player.Id), PlayerOffenses[player.Id].offenses + 1, BanCount));
}
}
}
}
}
private bool MustExclude(IPlayer player)
{
var list = GroupsToExclude.ToList();
if (list == null) { return false; }
if (list.Count < 1) { return false; }
foreach (var group in list)
{
if (group == null) { continue; }
if (group.ToString() == "" || group.ToString() == " ") { continue; }
if (permission.UserHasGroup(player.Id, group.ToString()))
{
return true;
}
}
return false;
}
private bool GetIsMuted(ref IPlayer aPlayer)
{
bool isServerMuted = (bool)BetterChatMute.Call("API_IsMuted", aPlayer);
return isServerMuted;
}
private void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["KickReason"] = "Bad Language",
["BanReason"] = "Bad Language",
["MuteReason"] = "Bad Language",
["BroadcastKickFormat"] = "{0} was kicked for {1}",
["BroadcastBanFormat"] = "{0} was banned for {1}",
["NoOffenses"] = "You have no offenses.",
["OffenseCount"] = "You have {0} offenses.",
["SyntaxError"] = "Invalid Syntax",
["HasOffenseCount"] = "{0} has {1} offenses.",
["HasNoOffenses"] = "{0} has no offenses.",
["Cleared"] = "Offenses for {0} cleared.",
["SelfCleared"] = "Your offenses have been cleared by {0}",
["NoPermission"] = "You do not have permission to use this.",
["OffenseWarning"] = "You currently have {0} Bad language offenses",
["OffenseWarningKick"] = "You currently have {0} of {1} Bad language Offenses till kick",
["OffenseWarningBan"] = "You currently have {0} of {1} Bad language Offenses till Ban",
["CantUseSpecialCharacters"] = "Special characters in your chat not allowed"
}, this, "en");
}
private void ClearOffense(IPlayer player)
{
if (PlayerOffenses.ContainsKey(player.Id))
{
PlayerOffenses[player.Id].offenses = 0;
SaveData();
}
else
{
return;
}
}
private void LoadData()
{
PlayerOffenses = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<string, OffenseData>>(Name);
}
protected override void LoadDefaultConfig()
{
PrintWarning("Generating new config file...");
}
private void LoadConfiguration()
{
CheckCfg<bool>("Exclude Team Chat", ref ExcludeTeamChat);
CheckCfg<bool>("Warn Players with offense amount", ref warnoffenseamount);
CheckCfg<int>("Offenses - Time To Mute", ref TimeToMute);
CheckCfg<int>("Offenses - Count To Mute", ref MuteCount);
CheckCfg<int>("Offenses - Count To Kick", ref KickCount);
CheckCfg<int>("Offenses - Count To Ban", ref BanCount);
CheckCfg<int>("Time to Ban in Minutes", ref BanTimeMin);
CheckCfg<bool>("Word Filter - Enabled", ref WordFilter_Enabled);
CheckCfg<bool>("Whole Message Filter - Enabled", ref FilterAll);
CheckCfg<string>("Word Filter - Replacement", ref WordFilter_Replacement);
CheckCfg<bool>("Word Filter - Use Custom Replacement", ref WordFilter_UseCustomReplacement);
CheckCfg<string>("Word Filter - Custom Replacement", ref WordFilter_CustomReplacement);
CheckCfg<bool>("Advanced - Use REGEX", ref UseRegex);
CheckCfg<string>("Advanced - Regex to use", ref regextouse);
CheckCfg<List<object>>("Word Filter List", ref WordFilter_Phrases);
CheckCfg<List<object>>("Word To White List", ref WordWhiteList);
CheckCfg<List<object>>("UserGroups to exclude", ref GroupsToExclude);
CheckCfg<int>("Clear Offense After (0 - Disabled, 1 - All Kick/Mute/ban, 2 - Kick, 3 - Mute, 4 - Ban", ref clear);
CheckCfg<bool>("Offenses - Broadcast kick", ref BroadcastKick);
CheckCfg<bool>("Offenses - Broadcast Ban", ref BroadcastBan);
CheckCfg<bool>("Block Special Characters in Chat", ref BlockSpecialCharacters);
SaveConfig();
}
private void CheckCfg<T>(string Key, ref T var)
{
if (Config[Key] is T)
var = (T)Config[Key];
else
Config[Key] = var;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////
#region Command
[Command("clearfilters")]
private void ClearFilter(IPlayer player, string command, string[] args)
{
if (player.HasPermission("betterchatfilter.admin"))
{
foreach (var p in players.Connected)
{
ClearOffense(p);
player.Reply(string.Format(GetLang("Cleared", player.Id), p.Name));
p.Reply(string.Format(GetLang("SelfCleared", player.Id), player.Name));
}
}
}
[Command("filter")]
private void CmdFilter(IPlayer player, string command, string[] args)
{
if (player.HasPermission("betterchatfilter.admin"))
{
if (args.Length == 0)
{
int offenseCount = PlayerOffenses.ContainsKey(player.Id) == true ? PlayerOffenses[player.Id].offenses : 0;
if (offenseCount == 0)
{
player.Reply(GetLang("NoOffenses", player.Id));
}
else
{
player.Reply(string.Format(GetLang("OffenseCount", player.Id), offenseCount));
}
return;
}
if (args.Length != 2)
{
player.Reply(GetLang("SyntaxError", player.Id));
return;
}
IPlayer target = GetPlayer(args[1], player);
if (target == null)
{
return;
}
switch (args[0])
{
case "check":
case "info":
int offenseCount = PlayerOffenses.ContainsKey(target.Id) == true ? PlayerOffenses[target.Id].offenses : 0;
if (offenseCount == 0)
{
player.Reply(string.Format(GetLang("HasNoOffenses", player.Id), target.Name));
}
else
{
player.Reply(string.Format(GetLang("HasOffenseCount", player.Id), target.Name, offenseCount));
}
break;
case "remove":
case "clear":
case "delete":
ClearOffense(target);
player.Reply(string.Format(GetLang("Cleared", player.Id), target.Name));
target.Reply(string.Format(GetLang("SelfCleared", player.Id), player.Name));
break;
}
}
else
{
if (args.Length > 0)
{
player.Reply(GetLang("NoPermission", player.Id));
return;
}
int offenseCount = PlayerOffenses.ContainsKey(player.Id) == true ? PlayerOffenses[player.Id].offenses : 0;
if (offenseCount == 0)
{
player.Reply(GetLang("NoOffenses", player.Id));
}
else
{
player.Reply(string.Format(GetLang("OffenseCount", player.Id), offenseCount));
}
}
}
#endregion
#region Word Filter
private string FilterText(IPlayer player, string original)
{
var filtered = original;
int count = 0;
bool WordMatch = false;
Regex r = new Regex(regextouse, RegexOptions.IgnoreCase);
foreach (var word in original.Split(' '))
{
if (DoWhiteList(word.ToLower())) { continue; }
if (UseRegex)
{
Match m = r.Match(word);
if (m.Success)
{
Puts($"REGEX MATCH : {player.Name} said: \"{original}\" which contained a bad word: \"{word}\"");
filtered = filtered.Replace(word, Replace(word));
count++;
}
}
foreach (string bannedword in WordFilter_Phrases)
if (TranslateLeet(word).ToLower().Contains(bannedword.ToLower()))
{
Puts($"BANNED WORDS MATCH :| {player.Name} said: \"{original}\" which contained a bad word: \"{word}\"");
filtered = filtered.Replace(word, Replace(word));
if (FilterAll)
{
filtered = "";
}
WordMatch = true;
}
}
if (WordMatch)
{
count++;
WarnPlayer(player);
}
if (count > 0)
{
Offsense(player);
}
return filtered;
}
private bool DoWhiteList(string word)
{
if (word == null || word == "" || word == " ") { return false; }
var endchar = word.Last();
if (endchar == 0) { return false; }
var _whitelist = WordWhiteList.ToList();
if (Char.IsPunctuation(endchar))
{
foreach (var w in _whitelist)
{
if (word.Contains(w.ToString().ToLower())) { return true; }
}
return false;
}
if (_whitelist.Contains(word.ToLower()))
{
return true;
}
foreach (var w in _whitelist)
{
if (word.Contains(w.ToString().ToLower())) { return true; }
}
return false;
}
private string Replace(string original)
{
var filtered = string.Empty;
if (!WordFilter_UseCustomReplacement && WordFilter_Replacement == "") { return original; }
if (!WordFilter_UseCustomReplacement)
for (; filtered.Count() < original.Count();)
filtered += WordFilter_Replacement;
else
filtered = WordFilter_CustomReplacement;
return filtered;
}
private string TranslateLeet(string original)
{
var translated = original;
var leetTable = new Dictionary<string, string>
{
{"}{", "h"},
{"|-|", "h"},
{"]-[", "h"},
{"/-/", "h"},
{"|{", "k"},
{"/\\/\\", "m"},
{"|\\|", "n"},
{"/\\/", "n"},
{"()", "o"},
{"[]", "o"},
{"vv", "w"},
{"\\/\\/", "w"},
{"><", "x"},
{"2", "z"},
{"4", "a"},
{"@", "a"},
{"8", "b"},
{"ß", "b"},
{"(", "c"},
{"<", "c"},
{"{", "c"},
{"3", "e"},
{"€", "e"},
{"6", "g"},
{"9", "g"},
{"&", "g"},
{"#", "h"},
{"$", "s"},
{"7", "t"},
{"|", "l"},
{"1", "i"},
{"!", "i"},
{"0", "o"}
};
foreach (var leet in leetTable)
translated = translated.Replace(leet.Key, leet.Value);
return translated;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////
#region Finding Helper
private IPlayer GetPlayer(string nameOrID, IPlayer player)
{
if (IsParseableTo<string, ulong>(nameOrID) && nameOrID.StartsWith("7656119") && nameOrID.Length == 17)
{
IPlayer result = players.All.ToList().Find((p) => p.Id == nameOrID);
if (result == null)
player.Reply($"Could not find player with ID '{nameOrID}'");
return result;
}
List<IPlayer> foundPlayers = new List<IPlayer>();
foreach (IPlayer current in players.Connected)
{
if (current.Name.ToLower() == nameOrID.ToLower())
return current;
if (current.Name.ToLower().Contains(nameOrID.ToLower()))
foundPlayers.Add(current);
}
switch (foundPlayers.Count)
{
case 0:
player.Reply($"Could not find player with name '{nameOrID}'");
break;
case 1:
return foundPlayers[0];
default:
string[] names = (from current in foundPlayers select current.Name).ToArray();
player.Reply("Multiple matching players found: \n" + string.Join(", ", names));
break;
}
return null;
}
#endregion
#region Convert Helper
private bool IsParseableTo<S, R>(S s)
{
R result;
return TryParse(s, out result);
}
private bool TryParse<S, R>(S s, out R c)
{
try
{
c = (R)Convert.ChangeType(s, typeof(R));
return true;
}
catch
{
c = default(R);
return false;
}
}
#endregion
}
}