// 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 // $CAPTCHA (bitmask) // 0 - Captcha Disabled // 1 - Captcha Enabled // 2 - Display warnings (@captcha_lastwarning/@captcha_lastwarningt) // 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_OK=CAPTCHA_TIME; 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: @captcha ##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: // Script disabled by admins if (!$CAPTCHA) { initnpctimer; end; } if ($@BOTCHECK_TARGET) captchaProbe(); // 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]); // TODO: What about jailed players? // 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 to the captcha in time and were arrested for AFK Botting. You can use @jailtime to keep track of time left."); CAPTCHA_OK=CAPTCHA_TIME; detachrid(); continue; } // 2.1 Player is AFK for more than 30 seconds if (checkidle() > 30) { 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) && CAPTCHA_OK <= CAPTCHA_TIME) { atcommand("@jailfor 30mn "+strcharinfo(0)); dispbottom l("You failed to reply to the captcha in time and were 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 if ($CAPTCHA & 2) { if (!@captcha_lastwarningt) @captcha_lastwarningt=3; if (!@captcha_lastwarning) @captcha_lastwarning=gettimetick(2); if (@captcha_lastwarning < gettimetick(2)) { dispbottom l("CAPTCHA: You have %s minute(s) remaining", CaptchName(@captcha_lastwarningt)); @captcha_lastwarningt-=1; @captcha_lastwarning+=60; } } return; } }