Skip to content

Commit 3d44668

Browse files
feat: spam prevention
1 parent ea7d2eb commit 3d44668

File tree

2 files changed

+106
-3
lines changed

2 files changed

+106
-3
lines changed

README.md

+22-3
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,23 @@ Default configuration:
2727

2828
```json
2929
{
30+
{
3031
"Commands": {
3132
"Use": "code"
3233
},
3334
"Options": {
34-
"displayPermissionErrors": true
35+
"displayPermissionErrors": true,
36+
"Spam Prevention": {
37+
"Attempts": 5,
38+
"Enable": true,
39+
"Exponential Lock Out Time": true,
40+
"Exponential Lockout Time": true,
41+
"Lock Out Reset Factor": 5.0,
42+
"Lock Out Time": 5.0,
43+
"Lockout Reset Factor": 5.0,
44+
"Lockout Time": 5.0,
45+
"Window Time": 30.0
46+
}
3547
}
3648
}
3749
```
@@ -40,8 +52,15 @@ Default configuration:
4052

4153
- `Commands` - Change the commands this plugin uses.
4254
- `Use` - Used to set the player's code.
43-
- `displayPermissionErrors` - If set to false, players won't be notified if
44-
they don't have the right permissions to use this plugin.
55+
- `Options`
56+
- `displayPermissionErrors` - If set to false, players won't be notified if they don't have the right permissions to use this plugin.
57+
- `Spam Prevention` - Prevent players from changing their code too often to prevent abuse.
58+
- `Enable` - Whether spam protection is enabled or not.
59+
- `Attempts` - The number of code changes the player can make with in `Window Time` before being marked as spamming.
60+
- `Window Time` - The time frame (in seconds) to count the number of code changes the player has made.
61+
- `Lock Out Time` - How long (in seconds) a player will be locked out for. This number should be low if using exponential lock out times.
62+
- `Exponential Lock Out Time` - If true, each time the player is locked out, they will be locked out for double the amount of time they were previously locked out for.
63+
- `Lock Out Reset Factor` - Determines how long (as a multiples of lock out time) before the player is forgive for all previous lockout offenses (should be greater than 1 - has no effect if not using exponential lock out time).
4564

4665
## API
4766

src/AutoCode.cs

+84
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ protected override void LoadDefaultMessages()
143143
{ "CodeUpdated", "Your new code is {0}." },
144144
{ "InvalidArgsTooMany", "No additional arguments expected." },
145145
{ "SyntaxError", "Syntax Error: expected \"{0}\"" },
146+
{ "SpamPrevention", "Too many recent code sets. Please wait {0} and try again." },
146147
}, this);
147148
}
148149

@@ -189,7 +190,62 @@ public void SetCode(BasePlayer player, string code)
189190
return;
190191
}
191192

193+
double currentTime = Utils.CurrentTime();
194+
195+
if (AutoCodeConfig.SpamPreventionEnabled)
196+
{
197+
double timePassed = currentTime - settings.lastSet;
198+
bool lockedOut = currentTime < settings.lockedOutUntil;
199+
double lockOutFor = AutoCodeConfig.SpamLockOutTime * Math.Pow(2, (AutoCodeConfig.SpamLockOutTimeExponential ? settings.lockedOutTimes : 0));
200+
201+
if (!lockedOut)
202+
{
203+
// Called again within spam window time?
204+
if (timePassed < AutoCodeConfig.SpamWindowTime)
205+
{
206+
settings.timesSetInSpamWindow++;
207+
}
208+
else
209+
{
210+
settings.timesSetInSpamWindow = 1;
211+
}
212+
213+
// Too many recent changes?
214+
if (settings.timesSetInSpamWindow > AutoCodeConfig.SpamAttempts)
215+
{
216+
// Locked them out.
217+
settings.lockedOutUntil = currentTime + lockOutFor;
218+
settings.lastLockedOut = currentTime;
219+
settings.lockedOutTimes++;
220+
settings.timesSetInSpamWindow = 0;
221+
lockedOut = true;
222+
}
223+
}
224+
225+
// Locked out?
226+
if (lockedOut)
227+
{
228+
player.ChatMessage(
229+
string.Format(
230+
lang.GetMessage("SpamPrevention", this, player.UserIDString),
231+
TimeSpan.FromSeconds(Math.Ceiling(settings.lockedOutUntil - currentTime)).ToString(@"d\d\ h\h\ mm\m\ ss\s").TrimStart(' ', 'd', 'h', 'm', 's', '0'),
232+
AutoCodeConfig.SpamLockOutTime,
233+
AutoCodeConfig.SpamWindowTime
234+
)
235+
);
236+
return;
237+
}
238+
239+
// Haven't been locked out for a long time?
240+
if (currentTime > settings.lastLockedOut + AutoCodeConfig.SpamLockOutResetFactor * lockOutFor)
241+
{
242+
// Reset their lockOuts.
243+
settings.lockedOutTimes = 0;
244+
}
245+
}
246+
192247
settings.code = code;
248+
settings.lastSet = currentTime;
193249

194250
player.ChatMessage(
195251
string.Format(
@@ -479,6 +535,14 @@ private static class AutoCodeConfig
479535
// Options.
480536
public static bool DisplayPermissionErrors { private set; get; }
481537

538+
// Spam prevention.
539+
public static bool SpamPreventionEnabled { private set; get; }
540+
public static int SpamAttempts { private set; get; }
541+
public static double SpamLockOutTime { private set; get; }
542+
public static double SpamWindowTime { private set; get; }
543+
public static bool SpamLockOutTimeExponential { private set; get; }
544+
public static double SpamLockOutResetFactor { private set; get; }
545+
482546
// Meta.
483547
private static bool UnsavedChanges = false;
484548

@@ -508,6 +572,14 @@ public static void LoadConfigValues()
508572
// Options.
509573
DisplayPermissionErrors = GetConfigValue(new string[] { "Options", "displayPermissionErrors" }, true);
510574

575+
// Spam prevention.
576+
SpamPreventionEnabled = GetConfigValue(new string[] { "Options", "Spam Prevention", "Enable" }, true);
577+
SpamAttempts = GetConfigValue(new string[] { "Options", "Spam Prevention", "Attempts" }, 5);
578+
SpamLockOutTime = GetConfigValue(new string[] { "Options", "Spam Prevention", "Lock Out Time" }, 5.0);
579+
SpamWindowTime = GetConfigValue(new string[] { "Options", "Spam Prevention", "Window Time" }, 30.0);
580+
SpamLockOutTimeExponential = GetConfigValue(new string[] { "Options", "Spam Prevention", "Exponential Lock Out Time" }, true);
581+
SpamLockOutResetFactor = GetConfigValue(new string[] { "Options", "Spam Prevention", "Lock Out Reset Factor" }, 5.0);
582+
511583
// Commands.
512584
Commands.Use = GetConfigValue(new string[] { "Commands", "Use" }, Commands.Use);
513585

@@ -548,6 +620,13 @@ private static void SetConfigValue<T>(string[] settingPath, T newValue)
548620
UnsavedChanges = true;
549621
}
550622
}
623+
/**
624+
* Utility functions.
625+
*/
626+
private static class Utils
627+
{
628+
public static double CurrentTime() => DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
629+
}
551630

552631
/**
553632
* Everything related to the data the plugin needs to save.
@@ -592,6 +671,11 @@ public class PlayerSettings
592671
{
593672
public string code = null;
594673
public bool enabled = true;
674+
public double lastSet = 0;
675+
public int timesSetInSpamWindow = 0;
676+
public double lockedOutUntil = 0;
677+
public double lastLockedOut = 0;
678+
public int lockedOutTimes = 0;
595679
}
596680
}
597681
}

0 commit comments

Comments
 (0)