torn-crack

Simple Cracking Helper

// ==UserScript==
// @name         torn-crack
// @namespace    torn-crack
// @version      0.0.6
// @description  Simple Cracking Helper
// @author       SirAua [3785905]
// @match        *://www.torn.com/page.php?sid=crimes*
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @license      mit
// ==/UserScript==

(function () {
    'use strict';

    if (window.CRACK_INJECTED) return;
    window.CRACK_INJECTED = true;

    const debug = true;
    function crackLog(...args) {
        if (!debug) return;
        console.log('[Crack]', ...args);
    }

    const isPda = window.GM_info?.scriptHandler?.toLowerCase().includes('tornpda');
    const UPDATE_INTERVAL = 800;
    const MAX_SUG = isPda ? 7 : 15;

    let dict = [];
    let dictLoaded = false;

    const wordlistUrl = 'https://raw.githubusercontent.com/danielmiessler/SecLists/refs/heads/master/Passwords/Common-Credentials/xato-net-10-million-passwords-1000000.txt';

    async function loadDict() {
        if (dictLoaded) return;
        crackLog('Loading wordlist...');
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'get',
                url: wordlistUrl,
                timeout: 30000,
                ontimeout: reject,
                onerror: reject,
                onload: (res) => {
                    const lines = res.responseText.split(/\r?\n/).map(w => w.trim().toUpperCase());
                    for (const word of lines) {
                        if (!/^[A-Z0-9_.]+$/.test(word)) continue;
                        const len = word.length;
                        if (!dict[len]) dict[len] = [];
                        dict[len].push(word);
                    }
                    dictLoaded = true;
                    crackLog('Loaded dictionary');
                    resolve();
                },
            });
        });
    }

    async function suggest(pat) {
        const len = pat.length;
        if (!dictLoaded || !dict[len]) return [];

        const worker = new Worker(URL.createObjectURL(new Blob([`
            self.onmessage = function(e) {
                const { dictChunk, pattern, max } = e.data;
                const regex = new RegExp('^' + pattern.replace(/[*]/g, '.') + '$');
                const out = [];
                for (const word of dictChunk) {
                    if (regex.test(word)) out.push(word);
                    if (out.length >= max) break;
                }
                self.postMessage(out);
            };
        `], { type: 'application/javascript' })));

        return new Promise((resolve) => {
            worker.onmessage = (e) => {
                worker.terminate();
                resolve([...new Set(e.data)]);
            };
            worker.postMessage({ dictChunk: dict[len], pattern: pat.toUpperCase(), max: MAX_SUG });
        });
    }

    function prependPanelToRow(row, pat) {
        const existing = row.querySelector('.__crackhelp_panel');
        if (existing && existing.dataset.pattern === pat) return;
        if (existing) existing.remove();

        const panel = document.createElement('div');
        panel.className = '__crackhelp_panel';
        panel.dataset.pattern = pat;
        panel.style.cssText = `
            background: #000; font-size: 10px; text-align: center; position: absolute; z-index: 9999;
        `;

        const listDiv = document.createElement('div');
        listDiv.style.cssText = 'margin-top: 2px;';
        panel.appendChild(listDiv);
        row.prepend(panel);

        async function updateSuggestions() {
            const sugs = await suggest(pat);
            listDiv.innerHTML = '';
            sugs.forEach(word => {
                const sp = document.createElement('span');
                sp.style.cssText = 'padding:2px; color: #00ff00;';
                sp.textContent = word;
                listDiv.appendChild(sp);
            });
            if (sugs.length === 0) {
                const none = document.createElement('span');
                none.textContent = '(no matches)';
                none.style.color = '#a00';
                listDiv.appendChild(none);
            }
        }

        loadDict().then(updateSuggestions);
    }

    function scanCrimePage() {
        if (!location.href.endsWith('cracking')) return;

        const currentCrime = document.querySelector('[class^="currentCrime"');
        if (!currentCrime) return;

        const container = currentCrime.querySelector('[class^="virtualList"');
        if (!container) return;

        const crimeOptions = container.querySelectorAll('[class^="crimeOptionWrapper"');
        crackLog('Scanning crime options:', crimeOptions.length);

        for (const crimeOption of crimeOptions) {
            let patText = '';
            const charSlots = crimeOption.querySelectorAll('[class^="charSlot"');
            for (const charSlot of charSlots) {
                if (!charSlot) continue;
                let char = charSlot.textContent.trim();
                patText += char ? char.toUpperCase() : '*';
            }

            if (!/^[*]+$/.test(patText)) prependPanelToRow(crimeOption, patText);
        }
    }

    // Show a helper message
    setInterval(() => {
        const el = document.getElementById('crackInfo');
        if (el) el.remove();

        if (!location.href.endsWith('cracking')) return;

        const appHeader = document.querySelector('[class^="appHeaderDelimiter"');
        if (!appHeader) return;

        const sp = document.createElement('span');
        sp.id = 'crackInfo';
        sp.style.cssText = 'background: #000; color: #0f0; font-size: 10px; text-align: left; z-index: 9999;';
        sp.textContent = 'Bruteforce characters to show suggestions!';
        appHeader.append(sp);
    }, UPDATE_INTERVAL);

    // Delay initial scan/setup slightly to support PDA DOM timing
        scanCrimePage();
    setInterval(scanCrimePage, UPDATE_INTERVAL);
})();