Greasy Fork

Greasy Fork is available in English.

anti-rickroll

修复了一些已知问题

当前为 2026-02-07 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         anti-rickroll
// @namespace    http://tampermonkey.net/
// @version      11.4
// @description  修复了一些已知问题
// @author       dext
// @match        *://*/*
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @connect      anti-rickroll.dext.top
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    const API_ENDPOINT = "https://anti-rickroll.dext.top";
    const cache = new Map();
    const queue = [];
    let activeRequests = 0;
    const MAX_CONCURRENT = 3;

    if (window.location.hash.includes('force-pass')) return;

    GM_addStyle(`
        #rick-breaker-overlay { position: fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.95); z-index:2147483647; display:flex; justify-content:center; align-items:center; backdrop-filter:blur(20px); }
        .rick-card { background:#fff; padding:40px; border-radius:24px; width:360px; text-align:center; border:6px solid #ff4444; box-shadow: 0 20px 60px rgba(0,0,0,0.6); font-family: sans-serif; }
        .rick-btn { padding:15px; border-radius:12px; cursor:pointer; border:none; font-weight:bold; width:100%; margin-top:15px; font-size:16px; transition: transform 0.1s; }
        .rick-btn:active { transform: scale(0.98); }
        .btn-safe { background:#eee; color:#333; }
        .btn-danger { background:#ff4444; color:#fff; font-size:11px; opacity:0.6; }
        a.rickroll-danger { outline: 2px dashed #ff4444 !important; outline-offset: 2px; }
    `);

    function recallToOrigin() {
        const referrer = document.referrer;
        if (referrer && !referrer.includes(window.location.hostname)) {
            window.location.replace(referrer);
        } else if (window.history.length > 1) {
            window.history.go(-window.history.length + 1);
            setTimeout(() => { if(!document.hidden) window.close(); }, 500);
        } else {
            window.close();
        }
    }

    function silencePage() {
        document.querySelectorAll('video, audio').forEach(m => { m.pause(); m.muted = true; });
    }

    function renderOverlay(title, url, isIncoming = false) {
        if (document.getElementById('rick-breaker-overlay')) return;
        const silencer = setInterval(silencePage, 200);

        const overlay = document.createElement('div');
        overlay.id = 'rick-breaker-overlay';
        overlay.innerHTML = `
            <div class="rick-card">
                <div style="font-size:60px">🛑</div>
                <h2 style="color:#ff4444; margin:15px 0;">${title}</h2>
                <p style="color:#444; font-size:15px; line-height:1.5;"><br>此路径通往 <b>Rickroll</b> 陷阱。</p>
                <button class="rick-btn btn-safe" id="r-back">安全溯源回退</button>
                <button class="rick-btn btn-danger" id="r-go">我已知晓风险,非要看</button>
            </div>`;

        (document.body || document.documentElement).appendChild(overlay);

        document.getElementById('r-back').onclick = () => {
            clearInterval(silencer);
            recallToOrigin();
        };

        document.getElementById('r-go').onclick = () => {
            clearInterval(silencer);
            overlay.remove();
            // 加上强制通行证并重新加载
            window.location.replace(url + (url.includes('#') ? '&' : '#') + 'force-pass');
        };
    }

    function checkIncoming() {
        const url = window.location.href;
        if (url.length < 25 || /google|baidu|bing/.test(window.location.hostname)) return;

        GM_xmlhttpRequest({
            method: "POST",
            url: API_ENDPOINT,
            data: JSON.stringify({ url }),
            headers: { "Content-Type": "application/json" },
            onload: (res) => {
                try {
                    const data = JSON.parse(res.responseText);
                    if (data.isRickroll) {
                        window.stop();
                        silencePage();
                        renderOverlay("入境链路拦截", url, true);
                    }
                } catch (e) {}
            }
        });
    }

    function isInternalLink(url) {
        try {
            const target = new URL(url);
            return target.protocol === window.location.protocol && target.hostname === window.location.hostname;
        } catch (e) { return true; }
    }

    function getRealUrl(url) {
        try {
            const u = new URL(url);
            if (u.hostname.includes('google.com') && u.searchParams.has('q')) return u.searchParams.get('q');
            if (u.hostname.includes('baidu.com') && u.searchParams.has('url')) return u.searchParams.get('url');
        } catch (e) {}
        return url;
    }

    function processQueue() {
        if (queue.length === 0 || activeRequests >= MAX_CONCURRENT) return;
        const url = queue.shift();
        activeRequests++;
        GM_xmlhttpRequest({
            method: "POST",
            url: API_ENDPOINT,
            data: JSON.stringify({ url }),
            headers: { "Content-Type": "application/json" },
            onload: (res) => {
                try {
                    const data = JSON.parse(res.responseText);
                    cache.set(url, data.isRickroll);
                    if (data.isRickroll) {
                        document.querySelectorAll('a').forEach(a => { if(getRealUrl(a.href) === url) a.classList.add('rickroll-danger'); });
                    }
                } catch (e) {}
                activeRequests--; processQueue();
            },
            onerror: () => { activeRequests--; processQueue(); }
        });
    }

    function scanPage() {
        const EXCLUDE = /\.(jpg|jpeg|png|gif|css|js|woff|zip|pdf|mp4)($|\?)/i;
        document.querySelectorAll('a[href]').forEach(a => {
            const realUrl = getRealUrl(a.href);
            if (!realUrl.startsWith('http') || isInternalLink(realUrl) || EXCLUDE.test(realUrl) || cache.has(realUrl)) return;
            cache.set(realUrl, 'pending');
            queue.push(realUrl);
        });
        processQueue();
    }

    window.addEventListener('click', function(e) {
        const a = e.target.closest('a');
        if (!a || !a.href || !a.href.startsWith('http')) return;
        const realUrl = getRealUrl(a.href);
        if (isInternalLink(realUrl) || realUrl.includes('force-pass') || a.dataset.rickSafe === '1') return;

        e.preventDefault(); e.stopPropagation();
        GM_xmlhttpRequest({
            method: "POST",
            url: API_ENDPOINT,
            data: JSON.stringify({ url: realUrl }),
            headers: { "Content-Type": "application/json" },
            onload: (res) => {
                try {
                    const data = JSON.parse(res.responseText);
                    if (data.isRickroll) {
                        renderOverlay("链路风险拦截", realUrl, false);
                    } else {
                        a.dataset.rickSafe = '1';
                        if (a.target === '_blank' || e.ctrlKey || e.metaKey) window.open(realUrl, '_blank');
                        else window.location.href = realUrl;
                    }
                } catch (e) { window.location.href = realUrl; }
            }
        });
    }, true);

    checkIncoming();
    window.addEventListener('DOMContentLoaded', scanPage);
    setInterval(scanPage, 5000);
})();