RoSniperX

stream sniper script without using exploits

目前为 2025-01-03 提交的版本。查看 最新版本

// ==UserScript==
// @name         RoSniperX
// @namespace    http://tampermonkey.net/
// @version      3.0
// @description  stream sniper script without using exploits
// @author       Lukas Dobbles
// @match        https://www.roblox.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=tampermonkey.net
// @grant        none
// ==/UserScript==

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

    return fetch(url, args).then((r) => r.json());
  };

  const search = async (placeId, name, setStatus, cb, setThumb) => {
    const userId = await getUserId(name);
    const thumbUrl = await getThumb(userId);
    setStatus("thumb url: " + thumbUrl);
    setThumb(thumbUrl);
    let cursor = null;
    let searching = true;
    let allPlayerTokens = [];
    let serversScraped = 0;
    const startTime = Date.now();

    const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

    let serverQueue = [];
    const chunkArray = (array, chunkSize) => {
      const result = [];
      for (let i = 0; i < array.length; i += chunkSize) {
        result.push(array.slice(i, i + chunkSize));
      }
      return result;
    };

    let retryCount = 0;
    const maxRetries = 5;

    while (searching) {
      try {
        // Fetch servers data
        const servers = await getServer(placeId, cursor);

        // Reset retry count on success
        retryCount = 0;

        // Update the count of scraped servers
        serversScraped += servers.data.length;

        // Update the cursor for the next page
        cursor = servers.nextPageCursor;

        // Process each server's player tokens
        for (const place of servers.data) {
          const tokens = place.playerTokens.map((token) => ({ token, place }));
          serverQueue = serverQueue.concat(tokens);
        }

        // If there's no next page, exit the loop
        if (!cursor) break;

        // Update the status with the current progress
        setStatus(
          `Scraped ${serverQueue.length} player tokens from ${serversScraped} servers`
        );
      } catch (err) {
        if (retryCount >= maxRetries) {
          setStatus(
            `Error fetching servers after ${maxRetries} retries. Please try again later. ${err}`
          );
          break;
        }

        // Apply exponential backoff
        const backoffTime = Math.pow(2, retryCount) * 1000; // Exponential backoff in milliseconds
        setStatus(
          `Error fetching servers. Retrying in ${backoffTime / 1000} seconds... (${err})`
        );
        await delay(backoffTime);
        retryCount++;
        continue;
      }
    }

    const chunkSize = 500; // Larger chunk size for parallel processing

    let found = false;

    while (serverQueue.length > 0 && !found) {
      const tokens = serverQueue.splice(0, chunkSize); // Dequeue a chunk of tokens

      try {
        const matchedServer = await Promise.any(
          chunkArray(tokens, 100).map(async (chunk) => {
            let retry = 0;
            while (retry <= maxRetries) {
              try {
                const serverThumbs = (await fetchThumbs(chunk.map(({ token }) => token))).data;
                console.log(serverThumbs)
                if (!serverThumbs) {
                  setStatus(
                    "Error fetching server thumbnails. Try again or get help at the Discord server: https://discord.gg/7zTCxVj9JD."
                  );
                  throw new Error("No thumbnails received");
                }

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

                    if (matchedPlace) {
                      found = true;
                      setStatus(
                        `Found them! Completed in ${
                          (Date.now() - startTime) / 1000
                        } seconds`
                      );

                      cb({
                        found: true,
                        place: matchedPlace,
                      });
                      return matchedPlace;
                    }
                  }
                }

                return null; // If no match found in this chunk
              } catch (err) {
                if (retry >= maxRetries) {
                  throw err;
                }
                const backoffTime = Math.pow(2, retry) * 1000;
                setStatus(
                  `Error processing chunk. Retrying in ${backoffTime / 1000} seconds... (${err})`
                );
                await delay(backoffTime);
                retry++;
              }
            }
          })
        ).catch(() => null); // If all promises reject, resolve with null

        if (matchedServer) {
          break; // Exit loop if a match is found
        }
      } catch (err) {
        setStatus(
          "There was an error when fetching user thumbnails. Try again or get help at the Discord server: https://discord.gg/7zTCxVj9JD. " +
            err
        );
      }
    }

    // If not found after all chunks
    if (!found) {
      cb({ found: false });
    }
  };

  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) => {
    let body = [];

    tokens.forEach((token) => {
      body.push({
        requestId: `0:${token}:AvatarHeadshot:150x150:png:regular`,
        type: "AvatarHeadShot",
        targetId: 0,
        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 instancesContainer = document.getElementById(
    "running-game-instances-container"
  );
  if (instancesContainer) {
    const containerHeader = document.createElement("div");
    containerHeader.id = "rosniperx";

    const headerText = document.createElement("h2");
    headerText.innerText = "RoSniperX";
    headerText.id = "rosniperx-header";
    containerHeader.appendChild(headerText);

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

    const thumbImage = document.createElement("img");
    thumbImage.height = "40";
    thumbImage.style.display = "none";
    containerHeader.appendChild(thumbImage);

    const usernameInput = document.createElement("input");
    usernameInput.classList = "input-field";
    usernameInput.placeholder = "Username";
    form.appendChild(usernameInput);

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

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

    const statusText = document.createElement("p");
    form.appendChild(statusText);

    const joinBtn = document.createElement("button");
    joinBtn.style.display = "none";
    joinBtn.innerText = "Join";
    joinBtn.classList =
      "btn-control-xs rbx-game-server-join game-server-join-btn btn-primary-md btn-min-width";

    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(/\d+/)[0];

      search(
        placeId,
        usernameInput.value,
        (txt) => {
          console.log(txt);
          statusText.innerText = txt;
        },
        (place) => {
          if (!place.found) {
            statusText.innerText = "couldn't find them";
            joinBtn.style.display = "none";
            return;
          }

          joinBtn.style.display = "";

          joinBtn.onclick = () => {
            window.Roblox.GameLauncher.joinGameInstance(placeId, place.place.id);
          };
        },
        (src) => {
          thumbImage.src = src;
          thumbImage.style.display = "";
        }
      );
    });
  }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址