您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
移除页面aff参数,增加代码复制按钮,并深度净化页面内容,移除各类推广元素。
// ==UserScript== // @name 没有朋友 // @namespace https://linux.do/u/amna/ // @version 3.0 // @description 移除页面aff参数,增加代码复制按钮,并深度净化页面内容,移除各类推广元素。 // @license MIT // @author https://linux.do/u/amna/ (Refactored by Senior Software Engineer) // @match *://ygpy.net/* // @icon https://ygpy.net/images/logo-light.webp // @run-at document-start // @grant GM_setClipboard // @grant GM_addStyle // ==/UserScript== (function() { 'use strict'; // --- 全局常量和配置 --- const SCRIPT_BUTTON_CLASS = 'amna-copy-button'; const DEBOUNCE_DELAY = 250; //ms const IGNORED_CODE_TEXT = new Set([ "流媒体解锁", "多设备在线", "支持 BT 下载", "国内中转线路", "IEPL 专线路", "设备不限制", "晚高峰稳定", "国际专线", "IPLC 线路", "50+ 节点", "不限设备", "超大带宽", "赠送 Emby", "IEPL 专线", "家宽 IP", "厦门专线", "开始阅读", "仪表盘", "主页", "兑换礼品卡" ]); // --- 工具函数 --- function debounce(func, wait) { let timeout; return function(...args) { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); }; } /** * @description [架构优化] 注入CSS样式。现在包含更多规则,以在JS执行前消除闪屏。 */ const injectStyles = (() => { let injected = false; return () => { if (injected) return; GM_addStyle(` /* 复制按钮样式 */ .${SCRIPT_BUTTON_CLASS} { margin-left:8px; padding:3px 6px; border:1px solid #ccc; border-radius:4px; background-color:#f0f0f0; color:#333; cursor:pointer; font-size:12px; font-family:sans-serif; user-select:none; display:inline-block; vertical-align:middle; opacity:0.7; transition:opacity .2s, background-color .2s; } /* === 抗闪屏 CSS 规则 === */ /* 隐藏侧边栏最后一个组 */ nav#VPSidebarNav > div.group:last-of-type { display: none !important; } /* 隐藏右侧辅助栏的广告容器 */ div.VPDocAside > div.doc-aside-ads { display: none !important; } /* [新增] 使用:has()选择器隐藏目录中的广告,现代浏览器支持良好 */ ul.VPDocOutlineItem li:has(a[title*="[AD]"]) { display: none !important; } div.vp-doc > div > div.vp-raw { display: none !important; } `); injected = true; }; })(); // --- 核心功能模块 --- function attachProactiveMenuListeners(container) { const menuButtons = container.querySelectorAll('.VPNavBarMenuGroup button:not([data-amna-menu-listener])'); menuButtons.forEach(button => { button.setAttribute('data-amna-menu-listener', 'true'); button.addEventListener('mouseover', () => { hideSponsoredMenuLinks(document.body); }, { passive: true }); }); } function hideSiblingsUntil(startElement, stopTagName) { let currentElement = startElement; while (currentElement) { currentElement.style.display = 'none'; const nextElement = currentElement.nextElementSibling; if (!nextElement || nextElement.tagName === stopTagName) { break; } currentElement = nextElement; } } // [优化] 此函数不再需要,其功能已被CSS :has() 替代,但保留函数定义以防未来需要更复杂的JS逻辑 function hideAdOutlineItems(container) { // Obsolete: Handled by CSS for anti-flicker. } function hideSponsoredSidebarItems(container) { const sidebarItems = container.querySelectorAll('section.VPSidebarItem > div.items > div'); sidebarItems.forEach(itemDiv => { const pTag = itemDiv.querySelector('div > a > p'); if (pTag && pTag.innerText.trim() === '付费机场') { itemDiv.remove(); } }); } function hideSponsoredContent(container) { const contentArea = container.querySelector('main.main > div.vp-doc > div'); if (!contentArea) return; const h1 = contentArea.querySelector('h1'); if (h1) { hideSiblingsUntil(h1, 'H2'); } const adHeaders = contentArea.querySelectorAll('h2[id*="ad"]'); adHeaders.forEach(h2 => { hideSiblingsUntil(h2, 'H2'); }); const children = Array.from(contentArea.children); const firstVisibleChild = children.find(el => el.style.display !== 'none'); if (firstVisibleChild && firstVisibleChild.tagName === 'H2') { firstVisibleChild.style.marginTop = '0'; } } function hideSponsoredHomeActions(container) { const actionDivs = container.querySelectorAll('div.VPHome div.main > div.actions > div.action'); actionDivs.forEach(div => { const link = div.querySelector('a'); if (link && link.innerText.trim() === '付费专栏') { div.remove(); } }); } function hideSponsoredMenuLinks(container) { const menuLinks = container.querySelectorAll('div.VPNavBarMenuGroup > div.menu div.items > div.VPMenuLink'); menuLinks.forEach(div => { const span = div.querySelector('a > span'); if (span && span.innerText.trim() === '付费机场') { div.style.display = 'none'; } }); } function cleanLinks(container) { const links = container.querySelectorAll('a[href*="?"]'); links.forEach(link => { if (!link.href) return; try { const url = new URL(link.href); const hasSearchParams = url.search; const hasHashParams = url.hash && url.hash.includes('?'); if (hasSearchParams || hasHashParams) { let newHref = url.origin + url.pathname; if (hasHashParams) { newHref += url.hash.split('?')[0]; } link.href = newHref; } } catch (error) { if (link.href.includes('?')) { link.href = link.href.split('?')[0]; } } }); } function addCopyButtons(container) { const codeBlocks = container.querySelectorAll('code:not([data-amna-processed])'); codeBlocks.forEach(code => { code.setAttribute('data-amna-processed', 'true'); const textContent = code.textContent?.trim(); if (!textContent || IGNORED_CODE_TEXT.has(textContent)) return; if (code.nextElementSibling?.classList.contains(SCRIPT_BUTTON_CLASS)) return; const copyButton = document.createElement('button'); copyButton.textContent = '复制'; copyButton.className = SCRIPT_BUTTON_CLASS; copyButton.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); GM_setClipboard(textContent, 'text'); copyButton.textContent = '已复制!'; copyButton.disabled = true; setTimeout(() => { copyButton.textContent = '复制'; copyButton.disabled = false; }, 2000); }); code.after(copyButton); }); } // --- 主执行逻辑与DOM监控 --- function processNode(node) { if (!(node instanceof Element)) return; const currentPath = window.location.pathname; if (currentPath.startsWith('/vpn/') && currentPath !== '/vpn/free.html') { hideSponsoredContent(node); // hideAdOutlineItems(node); // 已被CSS替代 } attachProactiveMenuListeners(node); hideSponsoredSidebarItems(node); hideSponsoredHomeActions(node); hideSponsoredMenuLinks(node); cleanLinks(node); addCopyButtons(node); } const debouncedProcessAll = debounce(() => { console.log('[没有朋友] DOM发生变化,重新扫描页面...'); processNode(document.body); }, DEBOUNCE_DELAY); // [架构优化] 主函数,现在它会在DOM加载后执行 function main() { console.log('[没有朋友] 脚本核心逻辑启动 (v6.0)。'); processNode(document.body); const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { debouncedProcessAll(); return; } } }); observer.observe(document.body, { childList: true, subtree: true }); } // [架构优化] 脚本入口点 // 1. 立即(在 document-start 时)注入CSS,这是消除闪屏的关键 injectStyles(); // 2. 等待DOM构建完成后,再执行依赖DOM的JS代码 if (document.readyState === 'loading') { window.addEventListener('DOMContentLoaded', main); } else { main(); // 如果脚本被延迟注入,则直接执行 } })();