summaryrefslogblamecommitdiff
path: root/npc/functions/captcha.txt
blob: 776501c57a2caed6263a60933662280255e1941c (plain) (tree)








































































































































































































































                                                                                                                                                        
// 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;
    }

}