diff options
-rw-r--r-- | npc/005-7/trainer.txt | 8 | ||||
-rw-r--r-- | npc/functions/captcha.txt | 233 | ||||
-rw-r--r-- | npc/scripts.conf | 1 |
3 files changed, 242 insertions, 0 deletions
diff --git a/npc/005-7/trainer.txt b/npc/005-7/trainer.txt index 3dfa9a999..c1dc37c8b 100644 --- a/npc/005-7/trainer.txt +++ b/npc/005-7/trainer.txt @@ -541,6 +541,14 @@ L_Regras: mes ""; callfunc "GameRules"; next; + mes ".:: "+l("Automatic Botchecks")+" ::."; + mes l("If enabled, automatic captchas may be sent to you every once in a while."); + mes l("To answer them, use: @captcha <num_value>"); + next; + CaptchExample(); + mes l("Example: Give the answer for the following: one+1"); + mes l("Reply: %s", b("@captcha 2")); + next; goto L_PreMenu; L_Menu_gap: diff --git a/npc/functions/captcha.txt b/npc/functions/captcha.txt new file mode 100644 index 000000000..776501c57 --- /dev/null +++ b/npc/functions/captcha.txt @@ -0,0 +1,233 @@ +// TMW2 functions. +// Author: +// Jesusalva +// Description: +// Random CAPTCHA check against AFK robots. +// TODO: Put this in a blackbox for bots which sniff text chat. +// Variables: +// gettimetick(2) variables: CAPTCHA_TIME | CAPTCHA_OK +// CAPTCHA_1 | CAPTCHA_2 | CAPTCHA_ANSWER => The correct captcha reply +// CAPTCHA_OP$ => The operation, defaults to "+" +// $@BOTCHECK_TARGET => The account ID of the char being probed +// @captcha_cooldown => anti-flood + +// CaptchName, names the number +function script CaptchName { + switch (getarg(0)) { + case 0: return l("zero"); + case 1: return l("one"); + case 2: return l("two"); + case 3: return l("three"); + case 4: return l("four"); + case 5: return l("five"); + case 6: return l("six"); + case 7: return l("seven"); + case 8: return l("eight"); + case 9: return l("nine"); + case 10: return l("ten"); + case 11: return l("eleven"); + case 12: return l("twelve"); + case 13: return l("thirteen"); + case 14: return l("fourteen"); + case 15: return l("fifteen"); + case 16: return l("sixteen"); + case 17: return l("seventeen"); + case 18: return l("eighteen"); + case 19: return l("nineteen"); + case 20: return l("twenty"); + } + return getarg(0); +} + +// MakeCaptch, makes a captcha and saves it +function script MakeCaptch { + CAPTCHA_TIME=gettimetick(2); + CAPTCHA_1=rand2(21); + CAPTCHA_2=rand2(20); + // select a operation + switch (rand2(1)) { + case 0: + CAPTCHA_OP$="-"; + CAPTCHA_ANSWER=CAPTCHA_1-CAPTCHA_2; + break; + default: + CAPTCHA_OP$="+"; + CAPTCHA_ANSWER=CAPTCHA_1+CAPTCHA_2; + break; + } + return; +} + +function script CaptchVal { + // Make it nice + .@c$=any( + CAPTCHA_1+CAPTCHA_OP$+CAPTCHA_2, + CaptchName(CAPTCHA_1)+CAPTCHA_OP$+CAPTCHA_2, + CAPTCHA_1+CAPTCHA_OP$+CaptchName(CAPTCHA_2), + CaptchName(CAPTCHA_1)+CAPTCHA_OP$+CaptchName(CAPTCHA_2), + CAPTCHA_1+" "+CAPTCHA_OP$+" "+CAPTCHA_2, + CaptchName(CAPTCHA_1)+" "+CAPTCHA_OP$+" "+CAPTCHA_2, + CAPTCHA_1+" "+CAPTCHA_OP$+" "+CaptchName(CAPTCHA_2), + CaptchName(CAPTCHA_1)+" "+CAPTCHA_OP$+" "+CaptchName(CAPTCHA_2)); + return .@c$; +} + +function script CaptchExample { + if (!CAPTCHA_TIME || getarg(0, false)) { + dispbottom("##1TO REPLY TO CAPTCHAS: @capcha <numeric answer>##1"); + dispbottom l("Example: Give the answer for the following: one+1"); + dispbottom l("Reply: %s", b("@captcha 2")); + dispbottom b(l("This example will not be shown again.")); + } + return; +} + +- script @captcha 32767,{ + function captchaProbe; + end; + +OnCall: + // Not you, ignore + if (getcharid(3) != $@BOTCHECK_TARGET) + end; + // Attempt cooldown + if (@captcha_cooldown > gettimetick(2)) { + dispbottom l("CAPTCHA: Cooldown in effect."); end;} + // Process answer + @captcha_cooldown=gettimetick(2)+.antiflood; + // Lets be reasonable + if (gettimetick(2)+2 > CAPTCHA_TIME) { + dispbottom l("CAPTCHA: An error happened, try again."); end;} + + // Verify answer + .@ans$ = implode(.@atcmd_parameters$, " "); + .@ans=atoi(.@ans$); + if (.@ans == CAPTCHA_ANSWER) { + CAPTCHA_OK=gettimetick(2)+.cooldown; + $@BOTCHECK_TARGET=0; + dispbottom any( + l("captcha successful"), + l("captcha ok"), + l("correct"), + l("understood"), + l("not bad"), + l("hmpf. That'll do."), + l("a bit longer and I would have jailed you %%\\ "), + l("%%\\ that'll do."), + l("%%N")); + dispbottom l("Remember: Players can also help enforcing no-AFK-bot rule!"); + //dispbottom l("Remember: Never lend your toothbrush to a slime!"); + } else { + dispbottom l("CAPTCHA: Incorrect answer. Wait %ds and try again.", .antiflood); // Max 10 attempts total + } + end; + +OnInit: + .thr=180; // Seconds to reply + .cooldown=3600; // Captcha Immunity + .antiflood=18; // Seconds between captcha failed attempts + bindatcmd "captcha", "@captcha::OnCall", 0, 0, 0; + initnpctimer; + end; + +// Pick a random target for captcha checks +OnTimer5000: + if ($@BOTCHECK_TARGET) captchaProbe(); + + // Script disabled by admins + if (!$CAPTCHA) { + initnpctimer; + end; + } + + // Maybe we will conduct a captcha + if (rand2(10) < 3) { + // This can be slow, beware + .@c = getunits(BL_PC, .@players, MAX_CYCLE_PC); + for (.@i = 0; .@i < .@c; .@i++) { + // Too lazy + if (rand2(100) > 5) + continue; + + // Okay, lets do it + attachrid(.@players[.@i]); + + // 1. Player in immunity, who is next one + if (CAPTCHA_OK > gettimetick(2)) { + detachrid(); + continue; + } + + // 2. Player must be jailed, and we continue + if (CAPTCHA_TIME < CAPTCHA_OK) { + atcommand("@jailfor 40mn "+strcharinfo(0)); + dispbottom l("You failed to reply captcha in time and was arrested for AFK Botting. You can use @jailtime to keep track of time left."); + CAPTCHA_OK=CAPTCHA_TIME; + detachrid(); + continue; + } + + // 3. This is a good target, lets do this + .@g$=""; + CaptchExample(); + MakeCaptch(); + + .@g$=any( + "CAPTCHA: Please reply the following: "+CaptchVal(), + "BOTCHECK: Please reply the following: "+CaptchVal(), + "CAPTCHA: You must answer this: "+CaptchVal(), + "BOTCHECK: You must answer this: "+CaptchVal()); + + // 4. Find a random method + switch (rand2(2)) { + case 0: + message(getcharid(3), .@g$); + message(getcharid(3), "Example for one + one: @captcha 2"); + break; + case 1: + announce(.@g$, bc_self|bc_pc); + announce("Example for one + one: @captcha 2", bc_self|bc_pc); + break; + default: + dispbottom(.@g$); + dispbottom("Example for one + one: @captcha 2"); + break; + } + + // 5. Detach rid, target is set + $@BOTCHECK_TARGET=getcharid(3); + detachrid(); + break; + } + } + + // Continue this timer forever + initnpctimer; + end; + +function captchaProbe { + // Attach rid + .@online=attachrid($@BOTCHECK_TARGET); + + // User disconnected, next captcha they'll be arrested because timer will expire + if (!.@online) { + $@BOTCHECK_TARGET=false; + CAPTCHA_OK=false; + } + + // Timer expired? Ban hammer + if (CAPTCHA_TIME+.thr > gettimetick(2)) { + atcommand("@jailfor 30mn "+strcharinfo(0)); + dispbottom l("You failed to reply captcha in time and was arrested for AFK Botting. You can use @jailtime to keep track of time left."); + CaptchExample(true); + $@BOTCHECK_TARGET=false; + CAPTCHA_OK=CAPTCHA_TIME; + } + + // Nothing happened, lets wait + return; + } + +} + + diff --git a/npc/scripts.conf b/npc/scripts.conf index bc1cbb246..193e9160b 100644 --- a/npc/scripts.conf +++ b/npc/scripts.conf @@ -143,6 +143,7 @@ "npc/functions/mkbot.txt", "npc/functions/gmbot.txt", "npc/functions/instances.txt", +"npc/functions/captcha.txt", // Maps specific scripts @include "npc/_import.txt" |