Greasy Fork is available in English.

Roblox AutoFill Stable Typing v2

Simule la vraie frappe, vérifie que les champs sont pris en compte, retry si besoin. Ne soumet pas, ne contourne pas le captcha.

// ==UserScript==
// @name         Roblox AutoFill Stable Typing v2
// @namespace    http://tampermonkey.net/
// @version      2.0
// @description  Simule la vraie frappe, vérifie que les champs sont pris en compte, retry si besoin. Ne soumet pas, ne contourne pas le captcha.
// @match        https://www.roblox.com/fr/*
// @grant        none
// ==/UserScript==

(function(){
    'use strict';

    const PSEUDOS = [
        "05nvf","05o7i","05onh","05p6j","05qme","05ry4","05t2o","05tfl","05tn7",
        "05uzw","05wkm","05wl1","05xqj","05yqq","05z6d","05ztt","060af","060ku",
        "066ac","068e0","068mw","068sq","0693f","069a7","06b1v","06bui","06cof",
        "06cuv","06fm5","06gmq","06hb6","06hri","06i3k","06i9j","06ic7","06ifa",
        "06il1","06j9w"
    ];
    const PASSWORD = "GDCRQDGY"; // ton password (8 chars)
    const DOB = { day: '01', month: 'Jan', year: '2000' };
    const GENDER = 'male';

    const log = (...a) => console.log('[RBX-AutoFill-v2]', ...a);

    function waitFor(selector, timeout = 20000) {
        return new Promise((resolve, reject) => {
            const el = document.querySelector(selector);
            if (el) return resolve(el);
            const mo = new MutationObserver(() => {
                const e = document.querySelector(selector);
                if (e) {
                    mo.disconnect();
                    resolve(e);
                }
            });
            mo.observe(document.body, { childList: true, subtree: true });
            if (timeout > 0) setTimeout(() => { mo.disconnect(); reject(new Error('timeout ' + selector)); }, timeout);
        });
    }

    // setter "robuste" - set native value (helps React)
    function setNativeValue(el, value) {
        try {
            const proto = Object.getPrototypeOf(el);
            const desc = Object.getOwnPropertyDescriptor(proto, 'value') || Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value');
            if (desc && desc.set) {
                desc.set.call(el, value);
            } else {
                el.value = value;
            }
            el.dispatchEvent(new Event('input', { bubbles: true }));
            el.dispatchEvent(new Event('change', { bubbles: true }));
            return true;
        } catch (e) {
            try { el.value = value; el.dispatchEvent(new Event('input', { bubbles: true })); el.dispatchEvent(new Event('change', { bubbles: true })); return true; }
            catch (e2) { return false; }
        }
    }

    // simule une vraie frappe, caractère par caractère, renvoie une Promise qui résout quand fini
    function typeLikeHuman(el, text, charDelay = 80) {
        return new Promise((resolve) => {
            if (!el) return resolve(false);
            el.focus();
            // vider d'abord (mais parfois safer de ne pas tout vider si page attend)
            try { setNativeValue(el, ''); } catch(e){ el.value = ''; }

            let i = 0;
            const step = () => {
                if (i >= text.length) {
                    // events finaux
                    el.dispatchEvent(new Event('input', { bubbles: true }));
                    el.dispatchEvent(new Event('change', { bubbles: true }));
                    // blur après court délai
                    setTimeout(()=>{ try{ el.blur(); el.dispatchEvent(new Event('blur', { bubbles: true })); }catch(e){} }, 40);
                    return resolve(true);
                }
                const ch = text[i];
                // Key events (keydown, keypress, input, keyup)
                try {
                    el.dispatchEvent(new KeyboardEvent('keydown', { key: ch, bubbles: true }));
                } catch(e) {}
                // append char via native setter to be safe for React
                setNativeValue(el, el.value + ch);
                try {
                    el.dispatchEvent(new KeyboardEvent('keypress', { key: ch, bubbles: true }));
                } catch(e) {}
                el.dispatchEvent(new Event('input', { bubbles: true }));
                try {
                    el.dispatchEvent(new KeyboardEvent('keyup', { key: ch, bubbles: true }));
                } catch(e) {}
                i++;
                setTimeout(step, charDelay + Math.round(Math.random()*20)); // small jitter
            };
            step();
        });
    }

    // vérifie que l'élément contient exactement la valeur souhaitée
    function verifyValue(el, expected) {
        try {
            if (!el) return false;
            const current = (el.value || '').toString();
            return current === expected;
        } catch(e) { return false; }
    }

    // remplit pseudo/pwd avec retries et vérifications
    async function fillCredentialsWithVerification(usernameEl, passwordEl, username, password) {
        const MAX_ATTEMPTS = 4;
        for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
            log(`Typing username attempt ${attempt}...`);
            await typeLikeHuman(usernameEl, username, 70);
            // petite pause pour que la page réagisse
            await new Promise(r => setTimeout(r, 250));
            if (verifyValue(usernameEl, username)) {
                log('Username verified OK');
                break;
            } else {
                log('Username mismatch after typing, retrying (setNative fallback)');
                setNativeValue(usernameEl, username);
                await new Promise(r => setTimeout(r, 200));
                if (verifyValue(usernameEl, username)) { log('Username OK after fallback'); break; }
            }
            if (attempt === MAX_ATTEMPTS) log('Unable to reliably set username (giving up after retries)');
        }

        // now password
        for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
            log(`Typing password attempt ${attempt}...`);
            await typeLikeHuman(passwordEl, password, 70);
            await new Promise(r => setTimeout(r, 200));
            if (verifyValue(passwordEl, password)) {
                log('Password verified OK');
                break;
            } else {
                log('Password mismatch after typing, retrying (setNative fallback)');
                setNativeValue(passwordEl, password);
                await new Promise(r => setTimeout(r, 120));
                if (verifyValue(passwordEl, password)) { log('Password OK after fallback'); break; }
            }
            if (attempt === MAX_ATTEMPTS) log('Unable to reliably set password (giving up after retries)');
        }

        return {
            userOk: verifyValue(usernameEl, username),
            passOk: verifyValue(passwordEl, password)
        };
    }

    // remplit selects DOB de façon robuste (choisit option existante par value ou text)
    function setSelectByValueOrText(selectEl, want) {
        if (!selectEl || !want) return false;
        // essayer value d'abord
        try {
            const optByValue = Array.from(selectEl.options).find(o => o.value === want);
            if (optByValue) { selectEl.value = optByValue.value; selectEl.dispatchEvent(new Event('change', { bubbles: true })); return true; }
            // sinon comparer le texte (abrégé ou complet)
            const optByText = Array.from(selectEl.options).find(o => (o.text||'').toLowerCase().includes(want.toLowerCase()));
            if (optByText) { selectEl.value = optByText.value; selectEl.dispatchEvent(new Event('change', { bubbles: true })); return true; }
            // fallback : set first non disabled
            const fallback = Array.from(selectEl.options).find(o => !o.disabled && o.value);
            if (fallback) { selectEl.value = fallback.value; selectEl.dispatchEvent(new Event('change', { bubbles: true })); return true; }
        } catch(e) { return false; }
        return false;
    }

    async function performFullFill() {
        try {
            // attendre que les éléments existent
            const usernameEl = await waitFor('#signup-username').catch(()=>document.getElementById('signup-username'));
            const passwordEl = await waitFor('#signup-password').catch(()=>document.querySelector('input[type="password"]'));
            const dayEl = document.querySelector('#DayDropdown') || document.querySelector('select[name="birthdayDay"]');
            const monthEl = document.querySelector('#MonthDropdown') || document.querySelector('select[name="birthdayMonth"]');
            const yearEl = document.querySelector('#YearDropdown') || document.querySelector('select[name="birthdayYear"]');
            const maleBtn = document.getElementById('MaleButton');
            const femaleBtn = document.getElementById('FemaleButton');
            const checkbox = document.getElementById('signup-checkbox');
            const signupBtn = document.getElementById('signup-button') || document.querySelector('button[name="signupSubmit"]');

            const chosenPseudo = PSEUDOS[Math.floor(Math.random() * PSEUDOS.length)];
            log('Chosen pseudo:', chosenPseudo);

            if (!usernameEl || !passwordEl) {
                log('Elements username/password non trouvés, abort.');
                showToast('Champs username/password non trouvés — attends le chargement et réessaie.');
                return;
            }

            // set DOB early to avoid race conditions (some pages revalidate after DOB change)
            if (dayEl) setSelectByValueOrText(dayEl, DOB.day);
            if (monthEl) setSelectByValueOrText(monthEl, DOB.month);
            if (yearEl) setSelectByValueOrText(yearEl, DOB.year);

            // short pause
            await new Promise(r=>setTimeout(r, 220));

            // fill credentials with verification
            const res = await fillCredentialsWithVerification(usernameEl, passwordEl, chosenPseudo, PASSWORD);

            // Re-set DOB and gender again after credentials to avoid page clearing them
            if (dayEl) setSelectByValueOrText(dayEl, DOB.day);
            if (monthEl) setSelectByValueOrText(monthEl, DOB.month);
            if (yearEl) setSelectByValueOrText(yearEl, DOB.year);

            if (GENDER === 'male' && maleBtn) try{ maleBtn.click(); } catch(e){}
            if (GENDER === 'female' && femaleBtn) try{ femaleBtn.click(); } catch(e){}

            if (checkbox && !checkbox.checked) try{ checkbox.click(); } catch(e){ try{ checkbox.checked = true; checkbox.dispatchEvent(new Event('change', { bubbles: true })); } catch(e){} }

            // enable signup button (best effort)
            if (signupBtn) { try { signupBtn.removeAttribute('disabled'); signupBtn.disabled = false; } catch(e){} }

            // final check
            const finalUserOk = verifyValue(usernameEl, chosenPseudo);
            const finalPassOk = verifyValue(passwordEl, PASSWORD);

            showToast(`Remplissage fini — user:${finalUserOk? 'OK':'FAIL'} pass:${finalPassOk? 'OK':'FAIL'}. Résous le captcha puis clique sur S'inscrire.`);
            log('Final status', {finalUserOk, finalPassOk});
        } catch (e) {
            console.error('[RBX-AutoFill-v2] erreur', e);
            showToast('Erreur interne — regarde la console (F12).');
        }
    }

    // UI: gros bouton
    function createButton() {
        if (document.getElementById('rbx-auto-btn-v2')) return;
        const btn = document.createElement('button');
        btn.id = 'rbx-auto-btn-v2';
        btn.innerText = 'Remplir (stable)';
        Object.assign(btn.style, {
            position: 'fixed', right: '16px', bottom: '16px', zIndex: 2147483647,
            padding: '14px 18px', background: '#ff5a5f', color: '#fff', border: 'none',
            fontSize: '15px', fontWeight: '700', borderRadius: '10px', cursor: 'pointer',
            boxShadow: '0 6px 20px rgba(0,0,0,0.3)', fontFamily: 'Segoe UI, Roboto, Arial'
        });
        btn.addEventListener('click', performFullFill);
        document.body.appendChild(btn);
    }

    // toast
    function showToast(msg, timeout = 4500) {
        const id = 'rbx-autofill-toast-v2';
        let el = document.getElementById(id);
        if (!el) {
            el = document.createElement('div');
            el.id = id;
            Object.assign(el.style, {
                position: 'fixed', right: '16px', bottom: '86px', zIndex: 2147483647,
                background: 'rgba(0,0,0,0.78)', color: '#fff', padding: '10px 14px', borderRadius: '10px',
                fontFamily: 'Segoe UI, Roboto, Arial', fontSize: '13px', boxShadow: '0 6px 20px rgba(0,0,0,0.35)'
            });
            document.body.appendChild(el);
        }
        el.textContent = msg;
        el.style.opacity = '1';
        clearTimeout(el._t);
        el._t = setTimeout(()=>{ el.style.transition = 'opacity 400ms'; el.style.opacity = '0'; }, timeout);
    }

    // Wait for page and add button
    (async function bootstrap() {
        try {
            await waitFor('#signup-username').catch(()=>null);
        } catch(e){}
        createButton();
        log('Autofill stable ready.');
    })();

    // expose for console
    window.RBXAutoFillStable = { run: performFullFill };
})();