Greasy Fork is available in English.

RoSniperX

stream sniper script without using exploits

// ==UserScript==
// @name         RoSniperX
// @namespace    http://tampermonkey.net/
// @version      3.1
// @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 serversScraped = 0;
    const startTime = Date.now();

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

    let serverQueue = [];
    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;
      }
    }

    let found = false;

    while (serverQueue.length > 0 && !found) {
      const tokens = serverQueue.splice(0, 100); // Process tokens in batches of 100

      try {
        const serverThumbs = (await fetchThumbs(tokens.map(({ token }) => token))).data;

        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;
            }
          }
        }
      } 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 tokens
    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 = "";
        }
      );
    });
  }
})();