diff options
Diffstat (limited to 'nameblocker.sp')
| -rw-r--r-- | nameblocker.sp | 205 |
1 files changed, 0 insertions, 205 deletions
diff --git a/nameblocker.sp b/nameblocker.sp index ebfd116..2850ee2 100644 --- a/nameblocker.sp +++ b/nameblocker.sp | |||
| @@ -1,8 +1,6 @@ | |||
| 1 | #pragma newdecls required | 1 | #pragma newdecls required |
| 2 | 2 | ||
| 3 | #include <sourcemod> | 3 | #include <sourcemod> |
| 4 | #include <regex> | ||
| 5 | #include <convars> | ||
| 6 | 4 | ||
| 7 | public Plugin myinfo = { | 5 | public Plugin myinfo = { |
| 8 | name = "SM Name Blocker", | 6 | name = "SM Name Blocker", |
| @@ -11,206 +9,3 @@ public Plugin myinfo = { | |||
| 11 | version = "alpha-0.1", | 9 | version = "alpha-0.1", |
| 12 | url = "git.dabikers.online/smnameblocker" | 10 | url = "git.dabikers.online/smnameblocker" |
| 13 | }; | 11 | }; |
| 14 | |||
| 15 | // Planned features: | ||
| 16 | // Soft-ban: Kick players with offending names, but don't ban them. A name change will prevent the blocking | ||
| 17 | // Hard-ban: Ban players with offending names | ||
| 18 | // Moderation commands: A set of admin commands to add, modify, or remove name filters | ||
| 19 | |||
| 20 | enum { | ||
| 21 | OPMODE_UNDEF, | ||
| 22 | |||
| 23 | OPMODE_DISABLED, | ||
| 24 | OPMODE_KICK, | ||
| 25 | OPMODE_BAN, | ||
| 26 | |||
| 27 | OPMODE_TOOBIG | ||
| 28 | }; | ||
| 29 | |||
| 30 | static const int DEFAULT_OPERATING_MODE = OPMODE_KICK; | ||
| 31 | static const int DEFAULT_REGEX_FLAGS = (PCRE_CASELESS | PCRE_EXTENDED | PCRE_NOTEMPTY | PCRE_UTF8); | ||
| 32 | static const int DEFAULT_ADMIN_IMMUNITY = ADMFLAG_GENERIC; | ||
| 33 | static const char DEFAULT_FAILED_NAME_MSG[] = "Failed name check"; | ||
| 34 | |||
| 35 | ConVar nb_cvarOperatingMode; static const char nb_cvarOM[] = "nb_OPERATINGMODE"; | ||
| 36 | ConVar nb_cvarRegexFlags; static const char nb_cvarDRFNAME[] = "nb_REGEXFLAGS"; | ||
| 37 | ConVar nb_cvarAdminImmunity; static const char nb_cvarDAINAME[] = "nb_ADMINIMMUNITY"; | ||
| 38 | ConVar nb_cvarFailedNameMessage; static const char nb_cvarFNMNAME[] = "nb_FAILEDNAMEMSG"; | ||
| 39 | |||
| 40 | ArrayList nb_filterlist; | ||
| 41 | |||
| 42 | public void OnPluginStart() { | ||
| 43 | // Initialize data types | ||
| 44 | nb_filterlist = new ArrayList(); | ||
| 45 | |||
| 46 | // Read config and populate filterlist | ||
| 47 | // TODO: This | ||
| 48 | |||
| 49 | // Register ConVars | ||
| 50 | char tmp[4 + 1]; IntToString(DEFAULT_OPERATING_MODE, tmp, sizeof(tmp)); | ||
| 51 | nb_cvarOperatingMode = CreateConVar(nb_cvarOM, tmp, "Operating mode", 0, true, 0.0, true, 2.0); | ||
| 52 | nb_cvarOperatingMode.SetInt(DEFAULT_OPERATING_MODE); | ||
| 53 | |||
| 54 | IntToString(DEFAULT_REGEX_FLAGS, tmp, sizeof(tmp)); | ||
| 55 | nb_cvarRegexFlags = CreateConVar(nb_cvarDRFNAME, tmp, "Regex compilation flags"); | ||
| 56 | nb_cvarRegexFlags.SetInt(DEFAULT_REGEX_FLAGS); | ||
| 57 | |||
| 58 | IntToString(DEFAULT_ADMIN_IMMUNITY, tmp, sizeof(tmp)); | ||
| 59 | nb_cvarAdminImmunity = CreateConVar(nb_cvarDAINAME, tmp, "Admin immunity flags"); | ||
| 60 | nb_cvarAdminImmunity.SetInt(DEFAULT_ADMIN_IMMUNITY); | ||
| 61 | |||
| 62 | nb_cvarFailedNameMessage = CreateConVar(nb_cvarFNMNAME, DEFAULT_FAILED_NAME_MSG, "Message delivered to users when failing a name check"); | ||
| 63 | nb_cvarFailedNameMessage.SetString(DEFAULT_FAILED_NAME_MSG); | ||
| 64 | |||
| 65 | AutoExecConfig(true, "nameblocker_cvars"); | ||
| 66 | |||
| 67 | // Register admin commands | ||
| 68 | RegAdminCmd("nb_addpattern", nb_addpattern, ADMFLAG_BAN, "Add a regex pattern to the filter-list"); | ||
| 69 | RegAdminCmd("nb_delpattern", nb_delpattern, ADMFLAG_BAN, "Remove a regex pattern from the filter-list"); | ||
| 70 | RegAdminCmd("nb_modpattern", nb_modpattern, ADMFLAG_BAN, "Modify (replace) an existing regex pattern in the filter-list"); | ||
| 71 | |||
| 72 | return; | ||
| 73 | } | ||
| 74 | |||
| 75 | public void OnClientPostAdminCheck(int client) { | ||
| 76 | checkName(client); | ||
| 77 | |||
| 78 | } | ||
| 79 | |||
| 80 | public void OnClientSettingsChanged(int client) { | ||
| 81 | // I'm not sure exactly what settings changes cause this to fire, but supposedly a name change would cause this to run, if | ||
| 82 | // that one user on AlliedForums is correct about OnClientSettingsChanged being Sourcemod's implementation of the player_info | ||
| 83 | // event. If this doesn't work, I can hook player_changename or player_info messages, and barring that I can still just poll | ||
| 84 | // everyone's names every minute or so | ||
| 85 | |||
| 86 | // Also, if checkName becomes too expensive, I'll create an adt_trie to keep track of users via the GetClientSerial and | ||
| 87 | // GetClientName functions (<K:V> -> <Serial:Name>) | ||
| 88 | |||
| 89 | checkName(client); | ||
| 90 | } | ||
| 91 | |||
| 92 | void checkName(int client) { | ||
| 93 | // Skip if checking is turned off | ||
| 94 | if(GetConVarInt(nb_cvarOperatingMode) <= OPMODE_DISABLED) | ||
| 95 | return; | ||
| 96 | |||
| 97 | // Don't bother checking / kicking the client if they're an admin / meet the immunity convar | ||
| 98 | if(CheckCommandAccess(client, "", GetConVarInt(nb_cvarAdminImmunity), true)) | ||
| 99 | return; | ||
| 100 | |||
| 101 | char name[32 + 1]; | ||
| 102 | GetClientName(client, name, sizeof(name)); | ||
| 103 | if(strlen(name) <= 0) { | ||
| 104 | LogError("Tried getting a client's name and couldn't for some reason"); | ||
| 105 | return; | ||
| 106 | } | ||
| 107 | |||
| 108 | for(int i = 0; i < nb_filterlist.Length; i++) { | ||
| 109 | Regex cur = nb_filterlist.Get(i); RegexError reerr; | ||
| 110 | if(MatchRegex(cur, name, reerr) > 0) | ||
| 111 | handleNameFail(client); | ||
| 112 | } | ||
| 113 | |||
| 114 | return; | ||
| 115 | } | ||
| 116 | |||
| 117 | void handleNameFail(int client) { | ||
| 118 | int mode = GetConVarInt(nb_cvarOperatingMode); | ||
| 119 | switch(mode) { | ||
| 120 | case OPMODE_KICK: { | ||
| 121 | char failmsg[512 + 1]; | ||
| 122 | GetConVarString(nb_cvarFailedNameMessage, failmsg, sizeof(failmsg)); | ||
| 123 | KickClient(client, failmsg); | ||
| 124 | } | ||
| 125 | |||
| 126 | case OPMODE_BAN: { | ||
| 127 | // TODO: Figure out how to interface with other banning tools | ||
| 128 | } | ||
| 129 | |||
| 130 | default: { | ||
| 131 | LogError("Tried to handle a failed name, but operating mode is invalid") | ||
| 132 | return; | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | return; | ||
| 137 | } | ||
| 138 | |||
| 139 | public Action nb_addpattern(int client, int args) { | ||
| 140 | if(args < 1) { | ||
| 141 | ReplyToCommand(client, "Error: No pattern specified"); | ||
| 142 | return Plugin_Handled; | ||
| 143 | } | ||
| 144 | |||
| 145 | char pattern[512 + 1]; | ||
| 146 | GetCmdArg(1, pattern, sizeof(pattern)); | ||
| 147 | if(strlen(pattern) <= 0) { | ||
| 148 | ReplyToCommand(client, "Error: Pattern is null or empty"); | ||
| 149 | return Plugin_Handled; | ||
| 150 | } | ||
| 151 | |||
| 152 | int reflags = GetConVarInt(nb_cvarRegexFlags); | ||
| 153 | if(args < 2) { | ||
| 154 | char flagholder[1 + 1]; // "Array", lol | ||
| 155 | GetCmdArg(2, flagholder, sizeof(flagholder)); | ||
| 156 | reflags = StringToInt(flagholder, 2); | ||
| 157 | } | ||
| 158 | |||
| 159 | RegexError reerr = REGEX_ERROR_NONE; Regex re = null; char errbuf[512 + 1]; | ||
| 160 | re = CompileRegex(pattern, reflags, errbuf, sizeof(errbuf), reerr); | ||
| 161 | if(re == INVALID_HANDLE) { | ||
| 162 | ReplyToCommand(client, "Error: Could not compile pattern: %s (%d)", errbuf, reerr); | ||
| 163 | return Plugin_Handled; | ||
| 164 | } | ||
| 165 | |||
| 166 | nb_filterlist.Push(re); | ||
| 167 | |||
| 168 | char id[8 + 1]; | ||
| 169 | GetClientAuthId(client, AuthId_SteamID64, id, sizeof(id)); id[8] = '\0'; | ||
| 170 | LogAction(client, -1, "Added %s to nameblocker filter list. (Added by: %s)", pattern, id); | ||
| 171 | ShowActivity2(client, "NameBlocker:", "Added %s to nameblocker filter list", pattern); | ||
| 172 | |||
| 173 | return Plugin_Handled; | ||
| 174 | } | ||
| 175 | |||
| 176 | public Action nb_delpattern(int client, int args) { | ||
| 177 | if(args < 1) { | ||
| 178 | ReplyToCommand(client, "Error: No pattern targeted"); | ||
| 179 | return Plugin_Handled; | ||
| 180 | } | ||
| 181 | |||
| 182 | // Get index via GetCmdArg | ||
| 183 | // Check if it's valid | ||
| 184 | // Remove it from the filter-list | ||
| 185 | |||
| 186 | int index = GetCmdArgInt(1); | ||
| 187 | if(index < 0 || index >= nb_filterlist.Length) { | ||
| 188 | ReplyToCommand(client, "Error: Index out of range. Expected: [0, %d], Got: %d", (nb_filterlist.Length - 1), index); | ||
| 189 | return Plugin_Handled; | ||
| 190 | } | ||
| 191 | |||
| 192 | char id[8]; | ||
| 193 | GetClientAuthId(client, AuthId_SteamID64, id, sizeof(id)); | ||
| 194 | LogAction(client, -1, "Removed pattern #%d from filter list. (Removed by: %s)", index, id); | ||
| 195 | ShowActivity2(client, "NameBlocker:", "Removed pattern #%d from filter list", index); | ||
| 196 | CloseHandle(nb_filterlist.Get(index)); // Not sure if this is needed. (Source)Pawn is not a garbage collected language, so dropping handles should be bad, but I'm not certain whether CompileRegex allocates memory or not | ||
| 197 | nb_filterlist.Erase(index); | ||
| 198 | |||
| 199 | return Plugin_Handled; | ||
| 200 | } | ||
| 201 | |||
| 202 | public Action nb_modpattern(int client, int args) { | ||
| 203 | if(args < 2) { | ||
| 204 | if(args < 1) | ||
| 205 | ReplyToCommand(client, "Error: No pattern targeted for modification"); | ||
| 206 | else | ||
| 207 | ReplyToCommand(client, "Error: No new pattern specified"); | ||
| 208 | |||
| 209 | return Plugin_Handled; | ||
| 210 | } | ||
| 211 | |||
| 212 | // Get arguments and check if they're valid | ||
| 213 | // Replace regex handle at index with new regex | ||
| 214 | |||
| 215 | return Plugin_Handled; | ||
| 216 | } \ No newline at end of file | ||
