speedtest隐藏IP脚本

ez9l 2026-07-06 12:20 1

效果





开关



// ==UserScript==
// @name Speedtest Hide IP
// @namespace https://www.speedtest.net/
// @version 1.1.0
// @description Hide the public IP address shown on Speedtest web pages, with a quick toggle.
// @author Codex
// @match https://www.speedtest.net/*
// @match https://speedtest.net/*
// @run-at document-start
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @grant GM_notification
// ==/UserScript==

(function () {
'use strict';

const MASK_TEXT = 'IP hidden';
const STORAGE_KEY = 'speedtest-hide-ip-enabled';
const ROOT_CLASS = 'speedtest-hide-ip-enabled';
const TOGGLE_SHORTCUT = 'Alt+Shift+I';
const originalText = new WeakMap();
let enabled = typeof GM_getValue === 'function' ? GM_getValue(STORAGE_KEY, true) : true;
let observer = null;

const ipPatterns = [
/\b(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|1?\d?\d)\b/g,
/\b(?:[a-f0-9]{1,4}:){2,7}[a-f0-9]{1,4}\b/gi
];

const css = `
.${ROOT_CLASS} .js-data-ip,
.${ROOT_CLASS} [data-result-id="ip"],
.${ROOT_CLASS} [data-testid*="ip" i],
.${ROOT_CLASS} [class*="ip-address" i],
.${ROOT_CLASS} [class*="ipAddress" i],
.${ROOT_CLASS} [class*="isp-ip" i] {
color: transparent !important;
text-shadow: none !important;
position: relative !important;
user-select: none !important;
}

.${ROOT_CLASS} .js-data-ip::after,
.${ROOT_CLASS} [data-result-id="ip"]::after,
.${ROOT_CLASS} [data-testid*="ip" i]::after,
.${ROOT_CLASS} [class*="ip-address" i]::after,
.${ROOT_CLASS} [class*="ipAddress" i]::after,
.${ROOT_CLASS} [class*="isp-ip" i]::after {
content: "${MASK_TEXT}" !important;
color: #8f98b8 !important;
position: absolute !important;
inset-inline-start: 0 !important;
top: 0 !important;
}
`;

function addStyle() {
if (typeof GM_addStyle === 'function') {
GM_addStyle(css);
return;
}

const style = document.createElement('style');
style.textContent = css;
document.documentElement.appendChild(style);
}

function containsIp(text) {
return ipPatterns.some((pattern) => {
pattern.lastIndex = 0;
return pattern.test(text);
});
}

function maskTextNode(node) {
if (!enabled) return;
if (!node.nodeValue || !containsIp(node.nodeValue)) return;

if (!originalText.has(node)) {
originalText.set(node, node.nodeValue);
}

let nextText = node.nodeValue;
for (const pattern of ipPatterns) {
pattern.lastIndex = 0;
nextText = nextText.replace(pattern, MASK_TEXT);
}
node.nodeValue = nextText;
}

function shouldSkipElement(element) {
const tag = element.tagName;
return tag === 'SCRIPT' || tag === 'STYLE' || tag === 'NOSCRIPT' || tag === 'TEXTAREA' || tag === 'INPUT';
}

function walkTextNodes(root, callback) {
if (!root) return;

if (root.nodeType === Node.TEXT_NODE) {
callback(root);
return;
}

if (root.nodeType !== Node.ELEMENT_NODE || shouldSkipElement(root)) return;

const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
acceptNode(node) {
const parent = node.parentElement;
if (!parent || shouldSkipElement(parent)) return NodeFilter.FILTER_REJECT;
return NodeFilter.FILTER_ACCEPT;
}
});

while (walker.nextNode()) callback(walker.currentNode);
}

function maskIpText(root) {
if (!root) return;

walkTextNodes(root, (node) => {
if (containsIp(node.nodeValue || '')) {
maskTextNode(node);
}
});
}

function restoreIpText(root) {
walkTextNodes(root, (node) => {
if (originalText.has(node)) {
node.nodeValue = originalText.get(node);
}
});
}

function applyState() {
document.documentElement.classList.toggle(ROOT_CLASS, enabled);

if (!document.body) return;

if (enabled) {
maskIpText(document.body);
} else {
restoreIpText(document.body);
}
}

function notify(message) {
if (typeof GM_notification === 'function') {
GM_notification({ title: 'Speedtest Hide IP', text: message, timeout: 1500 });
return;
}
console.info(`[Speedtest Hide IP] ${message}`);
}

function toggleEnabled() {
enabled = !enabled;
if (typeof GM_setValue === 'function') GM_setValue(STORAGE_KEY, enabled);
applyState();
notify(`IP hiding ${enabled ? 'enabled' : 'disabled'} (${TOGGLE_SHORTCUT})`);
}

function registerControls() {
if (typeof GM_registerMenuCommand === 'function') {
GM_registerMenuCommand(`Toggle IP hiding (${TOGGLE_SHORTCUT})`, toggleEnabled);
}

document.addEventListener('keydown', (event) => {
if (event.altKey && event.shiftKey && event.code === 'KeyI') {
event.preventDefault();
toggleEnabled();
}
});
}

function startObserver() {
applyState();

observer = new MutationObserver((mutations) => {
if (!enabled) return;

for (const mutation of mutations) {
if (mutation.type === 'characterData') {
maskTextNode(mutation.target);
}

for (const node of mutation.addedNodes) {
maskIpText(node);
}
}
});

observer.observe(document.body, {
childList: true,
characterData: true,
subtree: true
});
}

addStyle();
registerControls();
document.documentElement.classList.toggle(ROOT_CLASS, enabled);

if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', startObserver, { once: true });
} else {
startObserver();
}
})();

最新回复 (9)
  • 电报 07-06 12:28
    1

    牛叉!!

  • cshaizhihao 07-06 12:29
    2

    66666666666666666666666666666

  • kshy233 07-06 12:32
    3

    ^-^ 好强

  • z318969887 07-06 12:38
    4

    牛逼,已用上

  • BUGMojang 07-06 12:38
    5

    建议增加对如下域名的支持


    https://single.speedtest.net/zh-Hans

  • TomSoup 07-06 12:45
    6

    ublock origin 两行规则搞定


    www.speedtest.net##.css-12ij8qt.\!text-md.break-words.MuiTypography-body2.MuiTypography-root
    www.speedtest.net##.css-12ij8qt.\!text-sm.break-words.MuiTypography-body2.MuiTypography-root
  • wzdq 07-06 13:04
    7

    打个码更快

  • 杜会主乂 07-06 13:05
    8

    F12 直接删除那行代码

  • ayu31 07-06 13:21
    9

    牛 已用上

* 帖子来源NodeSeek
返回