Greasy Fork

Greasy Fork is available in English.

anti-rickroll

fixed bing

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         anti-rickroll
// @version      12.5
// @description  fixed bing
// @author       dext
// @match        *://*/*
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @connect      anti-rickroll.dext.top
// @run-at       document-start
// @namespace http://tampermonkey.net/
// ==/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;

    // --- 0. 免死金牌判定 ---
    if (window.location.hash.includes('force-pass') || window.location.search.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.98); z-index:2147483647; display:flex; justify-content:center; align-items:center; backdrop-filter:blur(25px); }
        .rick-card { background:#fff; padding:40px; border-radius:24px; width:350px; text-align:center; border:6px solid #ff4444; box-shadow: 0 20px 60px rgba(0,0,0,0.8); 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: 0.2s; }
        .btn-safe { background:#eee; color:#333; }
        .btn-safe:hover { background:#ddd; }
        .btn-danger { background:#ff4444; color:#fff; font-size:11px; opacity:0.5; }
        .btn-danger:hover { opacity:1; }
        #rick-report-btn { position: fixed; bottom: 20px; left: 20px; z-index: 2147483646; background: rgba(0,0,0,0.7); color: white; padding: 8px 15px; border-radius: 20px; cursor: pointer; font-size: 13px; backdrop-filter: blur(5px); border: 1px solid rgba(255,255,255,0.2); display: flex; align-items: center; gap: 8px; }
        #rick-report-btn:hover { background: #ff4444; }
        a.rickroll-danger { outline: 3px dashed #ff4444 !important; outline-offset: 2px; background: rgba(255,0,0,0.1) !important; }
    `);

    // --- 1. 工具函数:脱壳与内链判定 ---
    function getCleanUrl(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');

            // 必应(Bing) Base64 深度脱壳
            if (u.hostname.includes('bing.com') && u.pathname.includes('/ck/a')) {
                let rawU = u.searchParams.get('u');
                if (rawU) {
                    try {
                        // 去掉 a1 前缀并解码
                        return atob(rawU.substring(2).replace(/-/g, '+').replace(/_/g, '/'));
                    } catch (e) { return url; }
                }
            }
            return u.origin + u.pathname + u.search;
        } catch (e) { return url; }
    }

    function isInternal(url) {
        try {
            const t = new URL(url);
            const curr = window.location.hostname;
            // 必应重定向链接不视为内链
            if (curr.includes('bing.com') && t.pathname.includes('/ck/a')) return false;
            return t.hostname === curr;
        } catch (e) { return true; }
    }

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

    // --- 2. 智能退出逻辑 ---
    function smartExit(isIncoming) {
        if (!isIncoming) {
            document.getElementById('rick-breaker-overlay')?.remove();
            return;
        }
        const referrer = document.referrer;
        if (referrer && !referrer.includes(window.location.hostname)) {
            window.location.replace(referrer);
        } else if (window.history.length > 1) {
            window.history.back();
        } else {
            window.close();
            setTimeout(() => { window.location.href = "about:blank"; }, 150);
        }
    }

    // --- 3. UI 渲染逻辑 ---
    function renderOverlay(title, url, isIncoming = false) {
        if (document.getElementById('rick-breaker-overlay')) return;
        const silencer = setInterval(silenceMedia, 200);

        const overlay = document.createElement('div');
        overlay.id = 'rick-breaker-overlay';
        overlay.innerHTML = `<div class="rick-card">
            <div style="font-size:50px">🛑</div>
            <h2 style="color:#ff4444; margin:10px 0;">${title}</h2>
            <p style="color:#444; font-size:14px;">此路径被识别为 <b>Rickroll</b>。</p>
            <button class="rick-btn btn-safe" id="r-back">${isIncoming ? '离开' : '关闭'}</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);
            smartExit(isIncoming);
        };

        document.getElementById('r-go').onclick = () => {
            clearInterval(silencer);
            overlay.remove();
            const separator = url.includes('#') ? '&' : '#';
            const jumpUrl = url + separator + 'force-pass';
            if (isIncoming) {
                window.location.replace(jumpUrl);
            } else {
                const a = document.createElement('a');
                a.href = jumpUrl;
                a.target = "_blank";
                a.click();
            }
        };
    }

    // --- 4. 扫描与并发控制 ---
    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 d = JSON.parse(res.responseText);
                    cache.set(url, { isRickroll: !!d.isRickroll, status: 'done' });
                    if (d.isRickroll) {
                        document.querySelectorAll(`a`).forEach(a => {
                            if(getCleanUrl(a.href) === url) a.classList.add('rickroll-danger');
                        });
                    }
                } catch (e) {}
                activeRequests--; processQueue();
            },
            onerror: () => { activeRequests--; processQueue(); }
        });
    }

    function scan() {
        const EX = /\.(jpg|jpeg|png|gif|css|js|woff|zip|pdf|mp4)($|\?)/i;
        document.querySelectorAll('a[href]').forEach(a => {
            const url = getCleanUrl(a.href);
            if (!url.startsWith('http') || isInternal(url) || EX.test(url) || cache.has(url)) return;
            cache.set(url, { isRickroll: false, status: 'pending' });
            queue.push(url);
        });
        processQueue();
    }

    // --- 5. 事件监听:点击拦截 ---
    window.addEventListener('click', (e) => {
        const a = e.target.closest('a');
        if (!a || !a.href.startsWith('http')) return;
        const url = getCleanUrl(a.href);
        if (isInternal(url) || a.dataset.rickSafe === '1' || url.includes('force-pass')) return;

        const res = cache.get(url);
        if (res && res.status === 'done' && res.isRickroll) {
            e.preventDefault(); e.stopPropagation();
            renderOverlay("链路风险拦截", url, false);
            return;
        }

        e.preventDefault(); e.stopPropagation();
        GM_xmlhttpRequest({
            method: "POST", url: API_ENDPOINT, data: JSON.stringify({ url }), headers: { "Content-Type": "application/json" },
            onload: (res) => {
                try {
                    const d = JSON.parse(res.responseText);
                    cache.set(url, { isRickroll: !!d.isRickroll, status: 'done' });
                    if (d.isRickroll) renderOverlay("实时风险拦截", url, false);
                    else { a.dataset.rickSafe = '1'; a.click(); }
                } catch (e) { a.dataset.rickSafe = '1'; a.click(); }
            }
        });
    }, true);

    // --- 6. 举报按钮逻辑 ---
    function createReportButton() {
        if (document.getElementById('rick-report-btn') || sessionStorage.getItem('rick-btn-hidden') === '1') return;
        const btn = document.createElement('div');
        btn.id = 'rick-report-btn';
        btn.innerHTML = `<span id="rick-do-report">🛡️ 我被骗了!</span><span id="rick-close-report" style="margin-left:8px;opacity:0.5">×</span>`;
        document.body.appendChild(btn);

        document.getElementById('rick-do-report').onclick = () => {
            const b = document.getElementById('rick-report-btn');
            b.innerText = "🔍 AI 正在存入云端...";
            GM_xmlhttpRequest({
                method: "POST", url: API_ENDPOINT, data: JSON.stringify({
                    url: window.location.href,
                    isFeedback: true,
                    title: document.title,
                    snippet: document.body ? document.body.innerText.substring(0, 800) : ""
                }),
                headers: { "Content-Type": "application/json" },
                onload: (res) => {
                    try {
                        const d = JSON.parse(res.responseText);
                        if (d.isRickroll) {
                            b.innerText = "✅ 举报成功!";
                            setTimeout(() => renderOverlay("举报生效拦截", window.location.href, true), 800);
                        } else {
                            b.innerText = "❌ AI 判定安全";
                            setTimeout(() => b.remove(), 2000);
                        }
                    } catch(e) { b.remove(); }
                }
            });
        };
        document.getElementById('rick-close-report').onclick = (e) => { e.stopPropagation(); btn.remove(); sessionStorage.setItem('rick-btn-hidden', '1'); };
    }

    // --- 7. 入境与自启动 ---
    function checkCurrent() {
        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 d = JSON.parse(res.responseText);
                    if (d.isRickroll) { window.stop(); renderOverlay("入境风险拦截", url, true); }
                } catch (e) {}
            }
        });
    }

    checkCurrent();
    window.addEventListener('load', () => {
        createReportButton();
        setInterval(scan, 4000);
    });
})();