RoSniperX

frontend launcher hijack mobile stream sniper

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @name         RoSniperX
// @namespace    http://tampermonkey.net/
// @version      6.1
// @description  frontend launcher hijack mobile stream sniper
// @author       Lukas Dobbles (fixed by Grok-Raw)
// @match        https://www.roblox.com/*
// @grant        none
// ==/UserScript==

(function () {
    "use strict";

    const sleep = (ms) => new Promise(r => setTimeout(r, ms));

    const getJSON = async (url, args = {}) => {
        args.headers = args.headers || {};
        args.credentials = "include";

        let retries = 5;

        while (retries--) {
            try {
                const res = await fetch(url, args);

                if (res.status === 429) {
                    await sleep(2000);
                    continue;
                }

                return await res.json();
            } catch (e) {
                if (retries <= 0) throw e;
                await sleep(1500);
            }
        }
    };

    const getUserId = (name) =>
        getJSON("https://users.roblox.com/v1/usernames/users", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({
                usernames: [name],
                excludeBannedUsers: true
            })
        }).then(d => d?.data?.[0]?.id);

    const getThumb = (id) =>
        getJSON(`https://thumbnails.roblox.com/v1/users/avatar-headshot?userIds=${id}&format=Png&size=150x150`)
            .then(d => d.data[0].imageUrl);

    const getServer = (placeId, cursor) => {
        let url = `https://games.roblox.com/v1/games/${placeId}/servers/Public?limit=100`;
        if (cursor) url += "&cursor=" + cursor;
        return getJSON(url);
    };

    const fetchThumbs = (tokens) => {
        const body = tokens.map(token => ({
            requestId: `0:${token}:AvatarHeadshot:150x150:png:regular`,
            type: "AvatarHeadShot",
            token,
            format: "png",
            size: "150x150"
        }));

        return getJSON("https://thumbnails.roblox.com/v1/batch", {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                Accept: "application/json"
            },
            body: JSON.stringify(body)
        });
    };

    const search = async (placeId, name, setStatus, cb, setThumb) => {
        try {
            const userId = await getUserId(name);

            if (!userId) {
                setStatus("User not found.");
                cb({ found: false });
                return;
            }

            const thumbUrl = await getThumb(userId);
            setThumb(thumbUrl);
            setStatus("Target avatar loaded...");

            let cursor = null;
            let serverQueue = [];
            let serversScraped = 0;
            let playersScraped = 0;
            const startTime = Date.now();

            do {
                const servers = await getServer(placeId, cursor);

                if (!servers || !servers.data) break;

                for (const place of servers.data) {
                    serversScraped++;

                    if (place.playerTokens && place.playerTokens.length) {
                        playersScraped += place.playerTokens.length;

                        for (const token of place.playerTokens) {
                            serverQueue.push({ token, place });
                        }
                    }
                }

                cursor = servers.nextPageCursor;

                setStatus(`Scanned ${serversScraped} servers / ${playersScraped} players / queued ${serverQueue.length}`);

                await sleep(700);

            } while (cursor);

            setStatus(`Finished full scrape. Comparing ${serverQueue.length} player thumbnails...`);

            let checked = 0;

            while (serverQueue.length > 0) {
                const batch = serverQueue.splice(0, 100);

                try {
                    const thumbs = await fetchThumbs(batch.map(x => x.token));

                    if (!thumbs || !thumbs.data) {
                        await sleep(1000);
                        continue;
                    }

                    checked += batch.length;
                    setStatus(`Compared \( {checked}/ \){playersScraped} thumbnails...`);

                    for (const thumb of thumbs.data) {
                        if (!thumb || !thumb.imageUrl) continue;

                        if (thumb.imageUrl === thumbUrl) {
                            const thumbToken = thumb.requestId.split(":")[1];
                            const matchedPlace = batch.find(x => x.token === thumbToken)?.place;

                            if (matchedPlace) {
                                setStatus(`FOUND TARGET after ${(Date.now() - startTime) / 1000}s`);
                                cb({ found: true, place: matchedPlace });
                                return;
                            }
                        }
                    }

                    await sleep(500);

                } catch {
                    await sleep(1200);
                }
            }

            cb({ found: false });

        } catch (e) {
            setStatus("Search failed: " + e.message);
            cb({ found: false });
        }
    };

    // FIXED & AGGRESSIVE LAUNCH HIJACK
    const hijackLaunchAndJoin = async (targetGameId, setStatus) => {
        try {
            setStatus("Hijacking Roblox frontend launcher...");

            let captured = null;

            const tryCapture = (url) => {
                if (typeof url === "string" && url.includes("robloxmobile://")) {
                    captured = url;
                    return true;
                }
                return false;
            };

            // Backup originals
            const oldOpen = window.open;
            const oldAssign = window.location.assign;
            const oldReplace = window.location.replace;

            // Override window.open
            window.open = function (url, ...args) {
                if (tryCapture(url)) return null;
                return oldOpen.apply(this, [url, ...args]);
            };

            // Aggressive location overrides
            let originalHref = window.location.href;

            try {
                Object.defineProperty(window.location, "href", {
                    configurable: true,
                    enumerable: true,
                    get() { return originalHref; },
                    set(v) {
                        if (tryCapture(v)) return;
                        originalHref = v;
                        oldAssign.call(window.location, v);
                    }
                });
            } catch (e) {}

            window.location.assign = function (url) {
                if (tryCapture(url)) return;
                return oldAssign.call(window.location, url);
            };

            window.location.replace = function (url) {
                if (tryCapture(url)) return;
                return oldReplace.call(window.location, url);
            };

            // Find play button
            const playBtn = 
                document.querySelector('[data-testid="play-button"]') ||
                document.querySelector('.btn-common-play-game-lg') ||
                document.querySelector('.game-play-button') ||
                document.querySelector('#game-detail-play-button') ||
                document.querySelector('button[aria-label*="Play"]');

            if (!playBtn) {
                setStatus("Could not locate Roblox Play button.");
                restore();
                return;
            }

            setStatus("Clicking Play button...");
            playBtn.click();

            // Wait for capture
            let waited = 0;
            while (!captured && waited < 15000) {
                await sleep(400);
                waited += 400;
            }

            restore();

            if (!captured) {
                setStatus("Failed to capture robloxmobile:// URI.");
                return;
            }

            setStatus("Captured launcher. Rewriting target server...");

            let finalUrl = captured;

            if (finalUrl.includes("gameInstanceId=")) {
                finalUrl = finalUrl.replace(/gameInstanceId=[^&]+/, `gameInstanceId=${targetGameId}`);
            } else {
                finalUrl += (finalUrl.includes("?") ? "&" : "?") + `gameInstanceId=${targetGameId}`;
            }

            setStatus("Launching exact target server...");

            // Final launch methods (multiple fallbacks)
            setTimeout(() => {
                window.location.href = finalUrl;
            }, 50);

            setTimeout(() => {
                if (document.hidden) return;
                window.open(finalUrl, "_self");
            }, 300);

        } catch (e) {
            setStatus("Launch hijack failed: " + e.message);
            restore();
        }

        function restore() {
            try {
                window.open = oldOpen;
                window.location.assign = oldAssign;
                window.location.replace = oldReplace;
            } catch {}
        }
    };

    // UI Injection
    const instancesContainer = document.getElementById("running-game-instances-container");

    if (instancesContainer) {
        const containerHeader = document.createElement("div");
        containerHeader.id = "rosniperx";
        containerHeader.style.marginBottom = "15px";

        const headerText = document.createElement("h2");
        headerText.innerText = "RoSniperX v6.1";
        containerHeader.appendChild(headerText);

        const form = document.createElement("form");

        const thumbImage = document.createElement("img");
        thumbImage.height = "50";
        thumbImage.style.display = "none";
        thumbImage.style.borderRadius = "8px";
        thumbImage.style.marginBottom = "8px";
        containerHeader.appendChild(thumbImage);

        const usernameInput = document.createElement("input");
        usernameInput.classList = "input-field";
        usernameInput.placeholder = "Target Username";
        usernameInput.style.width = "100%";
        usernameInput.style.marginBottom = "8px";
        form.appendChild(usernameInput);

        const submitButton = document.createElement("button");
        submitButton.classList = "btn-primary-md";
        submitButton.innerText = "Search & Snipe";
        submitButton.disabled = true;
        form.appendChild(submitButton);

        usernameInput.addEventListener("keyup", e => {
            submitButton.disabled = e.target.value.trim().length === 0;
        });

        const statusText = document.createElement("p");
        statusText.style.margin = "8px 0";
        statusText.style.fontSize = "14px";
        form.appendChild(statusText);

        const joinBtn = document.createElement("button");
        joinBtn.style.display = "none";
        joinBtn.innerText = "🚀 Join Exact Server";
        joinBtn.classList = "btn-control-xs btn-primary-md";
        joinBtn.style.width = "100%";
        joinBtn.style.marginTop = "8px";

        containerHeader.appendChild(form);
        containerHeader.appendChild(joinBtn);

        instancesContainer.insertBefore(containerHeader, instancesContainer.firstChild);

        form.addEventListener("submit", evt => {
            evt.preventDefault();

            joinBtn.style.display = "none";

            const placeId = location.href.match(/games\/(\d+)/)?.[1];

            if (!placeId) {
                statusText.innerText = "Not on a game page.";
                return;
            }

            search(
                placeId,
                usernameInput.value.trim(),
                txt => statusText.innerText = txt,
                result => {
                    if (!result.found) {
                        statusText.innerText = "Target not found after full scan.";
                        return;
                    }

                    joinBtn.style.display = "block";

                    joinBtn.onclick = () => {
                        hijackLaunchAndJoin(result.place.id, txt => statusText.innerText = txt);
                    };
                },
                src => {
                    thumbImage.src = src;
                    thumbImage.style.display = "block";
                }
            );
        });
    }
})();