您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Check if a user is banned and update their status
// ==UserScript== // @name NitroType Ban Check (NTL / ntcomps) // @namespace http://tampermonkey.net/ // @version 2.3 // @description Check if a user is banned and update their status // @match https://www.nitrotype.com/* // @grant GM_xmlhttpRequest // @icon https://www.google.com/s2/favicons?sz=64&domain=fontawesome.com/icons/ban // @connect ntleaderboards.onrender.com // @connect ntcomps.com // @license MIT // ==/UserScript== (function() { 'use strict'; let NT_COMPS_TOKEN; const NT_TOKEN = `Bearer ${localStorage.getItem("player_token")}`; const VALID_PAGE_PATTERNS = [{ pattern: "https://www.nitrotype.com/leagues", handler: global_handleLeaguesPage }, { pattern: "https://www.nitrotype.com/racer/", handler: global_handleUserPage }, { pattern: "https://www.nitrotype.com/team/", handler: global_handleTeamPage }, ]; function init() { console.log("[BAN_CHECK_LOG] - Init. Checking page type."); const location = window.location.href; const validPage = VALID_PAGE_PATTERNS.find(({ pattern }) => location.startsWith(pattern)); if (validPage) { console.log("[BAN_CHECK_LOG]] - Valid page, getting NTComps Token."); get_NTCOMPS_TOKEN().then(token => { NT_COMPS_TOKEN = token.replace(/\n/g, ''); main(validPage.handler); }).catch(error => { console.error(error); }); } else { console.log("[BAN_CHECK_LOG]] - Invalid page, skipping token retrieval."); } } function main(handler) { console.log("[BAN_CHECK_LOG]] - Token updated, handling page."); handler(); } function global_handleUserPage() { console.log("[BAN_CHECK_LOG]] - Handling User Page."); const username = getUsernameFromUrl(); if (username) { getStatusAndColor(username) .then(({ finalStatus, color }) => { console.log(`[BAN_CHECK_LOG] - Status for user ${username} determined as ${finalStatus}, updating.`); updateProfileStatus(finalStatus, color); }) .catch(error => { console.error("Error processing user:", error); }); } function updateProfileStatus(finalStatus, color) { const playerNameContainer = document.querySelector('.profile-title'); const statusLabel = document.createElement('span'); statusLabel.textContent = finalStatus; statusLabel.style.color = color; statusLabel.style.marginLeft = "10px"; if (playerNameContainer) { playerNameContainer.appendChild(statusLabel); } } function getUsernameFromUrl() { const pathParts = window.location.pathname.split('/'); return pathParts[pathParts.length - 1]; } } function global_handleTeamPage() { console.log(`[BAN_CHECK_LOG] - Handling Team page`); let countdown = 1; const countdownInterval = setInterval(() => { countdown--; if (countdown < 0) { clearInterval(countdownInterval); checkUserBansTeam(); } }, 1000); async function updateUsersTeamApplications(userMap) { for (const [displayName, username] of Object.entries(userMap)) { try { const { finalStatus, color } = await getStatusAndColor(username); console.log(`[BAN_CHECK_LOG] - Status for user ${username} determined as ${finalStatus}, updating.`); await updateUserStatusTeamApplications(finalStatus, color, username, displayName); } catch (error) { console.error(`Error processing user ${username}:`, error); } } } async function updateUsersTeam(userMap) { for (const [displayName, username] of Object.entries(userMap)) { try { const { finalStatus, color } = await getStatusAndColor(username); console.log(`[BAN_CHECK_LOG] - Status for user ${username} determined as ${finalStatus}, updating.`); await updateUserStatusTeam(finalStatus, color, username, displayName); } catch (error) { console.error(`Error processing user ${username}:`, error); } } } async function checkUserBansTeam() { const applicationsMap = await fetchTeamApplications(); const userMap = await fetchTeamActivity(); document.querySelector('.table-cell.table-cell--races.table-filter').click(); if (applicationsMap){ console.log(`[BAN_CHECK_LOG] - Checking Team Applications`); updateUsersTeamApplications(applicationsMap); } if (userMap) { console.log(`[BAN_CHECK_LOG] - User Activity Retrieved, updating users`); updateUsersTeam(userMap); } else { console.error("Failed to retrieve user map."); } } function updateUserStatusTeamApplications(finalStatus, color, username, displayName) { //console.log("Checking team applications", finalStatus, color, username, displayName); const team_table = document.querySelector(".table.table--a.table--striped.well.well--m.well--b"); const playerNameContainers = team_table.querySelectorAll('.player-name--container[title]'); const playerNameContainer = Array.from(playerNameContainers).find(container => { const nameSpan = container.querySelector('.type-ellip'); const isNameMatch = nameSpan && nameSpan.textContent.trim() === displayName.trim(); if (isNameMatch) { return container; } return false; }); //console.log(playerNameContainer); const parentCont = playerNameContainer.parentElement; const titleCont = parentCont.nextElementSibling; const statusLabel = document.createElement('span'); statusLabel.textContent = finalStatus; statusLabel.style.color = color; statusLabel.style.fontWeight = "bold"; const existingStatusLabel = playerNameContainer.querySelector('.status-label'); if (existingStatusLabel) { existingStatusLabel.remove(); } if (titleCont) { statusLabel.classList.add('status-label'); titleCont.innerHTML = ''; titleCont.appendChild(statusLabel); } } function updateUserStatusTeam(finalStatus, color, username, displayName) { const team_table = document.querySelector('.table.table--striped.table--selectable.table--team.table--teamOverview'); const playerNameContainers = team_table.querySelectorAll('.player-name--container[title]'); const playerNameContainer = Array.from(playerNameContainers).find(container => { const nameSpan = container.querySelector('.type-ellip'); const isNameMatch = nameSpan && nameSpan.textContent.trim() === displayName.trim(); if (isNameMatch) { return container; } return false; }); //console.log(playerNameContainer); const parentCont = playerNameContainer.parentElement; const titleCont = parentCont.nextElementSibling; const statusLabel = document.createElement('span'); statusLabel.textContent = finalStatus; statusLabel.style.color = color; statusLabel.style.fontWeight = "bold"; const existingStatusLabel = playerNameContainer.querySelector('.status-label'); if (existingStatusLabel) { existingStatusLabel.remove(); } if (titleCont) { statusLabel.classList.add('status-label'); titleCont.innerHTML = ''; titleCont.appendChild(statusLabel); } } async function fetchTeamApplications() { try { const response = await fetch("https://www.nitrotype.com/api/v2/teams/applications", { headers: { accept: "application/json, text/plain, */*", authorization: NT_TOKEN, "sec-fetch-mode": "cors", "sec-fetch-site": "same-origin", }, referrer: "https://www.nitrotype.com/team/FASZ", referrerPolicy: "same-origin", method: "GET", mode: "cors", credentials: "include" }); const data = await response.json(); // Convert the response to JSON const memberMap = {}; // Populate memberMap based on data.results data.results.forEach(member => { const { displayName, username } = member; memberMap[displayName || username] = username; }); console.log(memberMap); // Log memberMap for inspection return memberMap; // Return the populated memberMap } catch (error) { console.error("Error fetching team applications:", error); return null; } } async function fetchTeamActivity() { try { const TEAM = window.location.pathname.split('/').pop(); const response = await fetch(`https://www.nitrotype.com/api/v2/teams/${TEAM}`, { headers: { accept: "application/json, text/plain, */*", authorization: NT_TOKEN, }, referrer: `https://www.nitrotype.com/team/${TEAM}`, referrerPolicy: "same-origin", method: "GET", mode: "cors", credentials: "include" }); const data = await response.json(); if (data.status === "OK") { const members = data.results.members; members.sort((a, b) => b.played - a.played); const memberMap = {}; members.forEach(member => { const { displayName, username } = member; memberMap[displayName || username] = username; }); console.log(memberMap); return memberMap; } else { console.error("Error: ", data.status); return null; } } catch (error) { console.error("Fetch error: ", error); return null; } } } function global_handleLeaguesPage() { console.log(`[BAN_CHECK_LOG] - Handling Leagues page`); let countdown = 1; const countdownInterval = setInterval(() => { countdown--; if (countdown < 0) { clearInterval(countdownInterval); checkUserBans(); } }, 1000); async function fetchUserActivity() { try { const response = await fetch("https://www.nitrotype.com/api/v2/leagues/user/activity", { headers: { accept: "application/json, text/plain, */*", authorization: NT_TOKEN, }, referrer: "https://www.nitrotype.com/leagues", referrerPolicy: "same-origin", method: "GET", mode: "cors", credentials: "include" }); const data = await response.json(); if (data.status === "OK") { const standings = data.results.standings; standings.sort((a, b) => b.experience - a.experience); const userMap = {}; standings.forEach(user => { const { displayName, username } = user; userMap[displayName || username] = username; }); return userMap; } else { console.error("Error: ", data.status); return null; } } catch (error) { console.error("Fetch error: ", error); return null; } } async function updateUsers(userMap) { for (const [displayName, username] of Object.entries(userMap)) { try { const { finalStatus, color } = await getStatusAndColor(username); console.log(`[BAN_CHECK_LOG] - Status for user ${username} determined as ${finalStatus}, updating.`); await updateUserStatus(finalStatus, color, username, displayName); } catch (error) { console.error(`Error processing user ${username}:`, error); } } } async function checkUserBans() { const userMap = await fetchUserActivity(); if (userMap) { console.log(`[BAN_CHECK_LOG] - User Activity Retrieved, updating users`); updateUsers(userMap); } else { console.error("Failed to retrieve user map."); } } function updateUserStatus(finalStatus, color, username, displayName) { const playerNameContainers = document.querySelectorAll('.player-name--container[title]'); const playerNameContainer = Array.from(playerNameContainers).find(container => { const nameSpan = container.querySelector('.type-ellip'); const isNameMatch = nameSpan && nameSpan.textContent.trim() === displayName.trim(); if (isNameMatch) { return container; } return false; }); const parentCont = playerNameContainer.parentElement; const titleCont = parentCont.nextElementSibling; const statusLabel = document.createElement('span'); statusLabel.textContent = finalStatus; statusLabel.style.color = color; statusLabel.style.fontWeight = "bold"; const existingStatusLabel = playerNameContainer.querySelector('.status-label'); if (existingStatusLabel) { existingStatusLabel.remove(); } if (titleCont) { statusLabel.classList.add('status-label'); titleCont.innerHTML = ''; titleCont.appendChild(statusLabel); } } } function get_NTCOMPS_TOKEN() { const targetUrl = 'https://www.ntcomps.com/racers/search'; return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', headers: { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", "Content-Type": "application/x-www-form-urlencoded", }, url: targetUrl, onload: function(response) { if (response.status === 200) { const parser = new DOMParser(); const doc = parser.parseFromString(response.responseText, 'text/html'); const tokenElement = doc.querySelector('input[name="authenticity_token"]'); if (tokenElement) { const tokenValue = tokenElement.value; resolve(tokenValue); } else { reject('authenticity_token element not found.'); } } else { reject(`Failed to fetch page: ${response.status}`); } }, onerror: function(error) { reject('Error fetching the page: ' + error); } }); }); } async function getStatusAndColor(username, retries = 3, delay = 1000) { let attempts = 0; while (attempts < retries) { try { const NTL_status = await ntleaderboards_check(username); const NTC_status = await ntcomps_check(username); //console.log(NTL_status, NTC_status); const { finalStatus, color } = determineFinalStatus(NTL_status, NTC_status); return { finalStatus, color }; } catch (error) { attempts++; console.error(`Attempt ${attempts} failed:`, error); if (attempts < retries) { console.log(`Retrying in ${delay}ms...`); await new Promise(resolve => setTimeout(resolve, delay)); } else { console.error("Max retries reached. Throwing error."); throw error; } } } } // Function to check if a user is banned from ntleaderboards // return "Not Banned" (legit or not found) / Banned" (and flagged) / "Banned, but not bot" (if banned but no flag) function ntleaderboards_check(username) { console.log(`[BAN_CHECK_LOG] - Waiting for ${username} status from ntleaderboards.`); const url = `https://ntleaderboards.onrender.com/is_user_banned/${username}`; return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: url, onload: function(response) { if (response.status === 200) { const data = response.responseText; let banned_status; if (data === "N") { banned_status = "Not Banned"; } else { if (data.includes("flag")) { banned_status = "Banned"; } else { banned_status = "Banned, but not bot"; } } resolve(banned_status); } else { reject(`Request failed with status: ${response.status}`); } }, onerror: function(error) { reject('Error fetching data: ' + error); } }); }); } // Function to check racer status from ntcomps // Return "Flagged" or "Legit" or "Not found" async function ntcomps_check(searchString) { console.log(`[BAN_CHECK_LOG] - Waiting for ${searchString} status from ntcomps.`); return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "POST", url: "https://www.ntcomps.com/racers/search", headers: { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", "Content-Type": "application/x-www-form-urlencoded", "Sec-Fetch-Dest": "document", "Sec-Fetch-Mode": "navigate", "Sec-Fetch-Site": "same-origin", }, data: `authenticity_token=${encodeURIComponent(NT_COMPS_TOKEN)}&racer%5Bsearch_string%5D=${encodeURIComponent(searchString)}&racer%5Bflagged%5D=0&commit=Search+racer`, onload: function(response) { if (response.status === 200) { const parser = new DOMParser(); const doc = parser.parseFromString(response.responseText, "text/html"); const rows = doc.querySelectorAll("tbody tr"); for (const row of rows) { const cells = row.querySelectorAll("td"); if (cells.length >= 6) { const racerName = cells[2].textContent.trim(); if (racerName.toLowerCase() === searchString.toLowerCase()) { const status = cells[5].textContent.trim(); resolve(status); return; } } } resolve("Not found"); } else { console.error('Request failed with status:', response.status); reject("Error"); } }, onerror: function(error) { console.error('Error occurred:', error); reject("Error"); } }); }); } //Possible Final Statuses //"Bot (100%)" if Banned / Flagged at both - RED //"Bot (ntcomps)" if Flagged at ntcomps only - RED //"Bot (NTL)" if Flagged at NTL only - RED //"NTL banned (not bot?)" - When banned at NTL only, but not for botting - ORANGE //"Legit" - Not banned, not Flagged at both - GREEN //GRAY color should mean ERROR function determineFinalStatus(NTL_status, NTC_status) { console.log({"NTL":NTL_status, "NTC": NTC_status}); const statusMap = { "Not Banned": { "Flagged on NTcomps": { finalStatus: "Bot (ntcomps)", color: "rgb(255, 0, 0)" }, // Red "Flagged on both platforms": { finalStatus: "Bot (100%)", color: "rgb(255, 0, 0)" }, // Red "Flagged on NTLeaderboards": { finalStatus: "Bot (NTL)", color: "rgb(255, 0, 0)" }, // Red "Legit": { finalStatus: "Legit", color: "rgb(0, 255, 0)" }, // Green "Not found": { finalStatus: "Unknown player", color: "rgb(255, 255, 0)" } // Yellow }, "Banned": { "Flagged on NTcomps": { finalStatus: "Bot (ntcomps)", color: "rgb(255, 0, 0)" }, // Red "Flagged on both platforms": { finalStatus: "Bot (100%)", color: "rgb(255, 0, 0)" }, // Red "Flagged on NTLeaderboards": { finalStatus: "Bot (NTL)", color: "rgb(255, 0, 0)" }, // Red "Legit": { finalStatus: "Bot (NTL)", color: "rgb(255, 0, 0)" }, // Red "Not found": { finalStatus: "Bot (NTL)", color: "rgb(255, 0, 0)" } // Red }, "Banned, but not bot": { "Flagged on NTcomps": { finalStatus: "Bot (ntcomps)", color: "rgb(255, 0, 0)" }, // Red "Flagged on both platforms": { finalStatus: "Bot (100%)", color: "rgb(255, 0, 0)" }, // Red "Flagged on NTLeaderboards": { finalStatus: "Bot (NTL)", color: "rgb(255, 0, 0)" }, // Red "Legit": { finalStatus: "NTL banned (not bot?)", color: "rgb(255, 165, 0)" }, // Orange "Not found": { finalStatus: "NTL banned (not bot?)", color: "rgb(255, 165, 0)" } // Orange } }; const result = statusMap[NTL_status]?.[NTC_status]; return result || { finalStatus: "Unknown status", color: "rgb(128, 128, 128)" }; // Default to gray color for unknown cases } init(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址