south-plus域名自动替换 (v2.0)

自动替换域名并支持自定义替换规则

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         south-plus域名自动替换 (v2.0)
// @namespace    https://github.com/qgdyyg/automatically-replace-southplus-domain
// @version      2.0
// @description  自动替换域名并支持自定义替换规则
// @author       qgdyyg
// @match        *://*/*
// @grant        GM_openInTab
// @grant        GM_registerMenuCommand
// @grant        GM_webRequest
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT
// @run-at       document-start
// ==/UserScript==

(function () {
    'use strict';

    // 初始化存储的替换规则
    const DEFAULT_RULES = [
        { source: 'south-plus.net', target: 'bbs.imoutolove.me' },
        { source: 'north-plus.net', target: 'bbs.imoutolove.me' },
        { source: 'white-plus.net', target: 'bbs.imoutolove.me' },
        { source: 'blue-plus.net', target: 'bbs.imoutolove.me' },
        { source: 'snow-plus.net', target: 'bbs.imoutolove.me' },
        { source: 'spring-plus.net', target: 'bbs.imoutolove.me' }
    ];

    // 从存储加载用户规则,如果没有则使用默认规则
    let REPLACEMENT_RULES = GM_getValue('replacementRules', DEFAULT_RULES);

    // 配置区域
    const CONFIG = {
        debug: false,
        autoRefresh: true,
        refreshDelay: 1500
    };

    // 保存规则到存储
    const saveRules = () => {
        GM_setValue('replacementRules', REPLACEMENT_RULES);
        if (CONFIG.debug) {
            console.log('[规则保存] 替换规则已保存', REPLACEMENT_RULES);
        }
    };

    // 重置为默认规则
    const resetToDefault = () => {
        REPLACEMENT_RULES = [...DEFAULT_RULES];
        saveRules();
        alert('已重置为默认替换规则');
    };

    // 添加新规则
    const addRule = () => {
        const source = prompt('请输入要替换的源域名 (例如: example.com)', '');
        if (!source) return;
        
        const target = prompt('请输入替换后的目标域名 (例如: newdomain.com)', 'bbs.imoutolove.me');
        if (!target) return;
        
        // 确保域名格式正确
        const cleanSource = source.replace(/^https?:\/\//, '').replace(/\/.*$/, '').trim();
        const cleanTarget = target.replace(/^https?:\/\//, '').replace(/\/.*$/, '').trim();
        
        if (!cleanSource || !cleanTarget) {
            alert('域名格式不正确,请重新输入');
            return;
        }
        
        // 检查是否已存在相同源域名的规则
        const exists = REPLACEMENT_RULES.some(rule => rule.source === cleanSource);
        if (exists) {
            if (!confirm(`已存在源域名为 "${cleanSource}" 的规则,是否覆盖?`)) {
                return;
            }
            REPLACEMENT_RULES = REPLACEMENT_RULES.filter(rule => rule.source !== cleanSource);
        }
        
        REPLACEMENT_RULES.push({ source: cleanSource, target: cleanTarget });
        saveRules();
        alert(`已添加新规则:\n${cleanSource} → ${cleanTarget}`);
    };

    // 编辑现有规则
    const editRule = () => {
        if (REPLACEMENT_RULES.length === 0) {
            alert('当前没有可用的替换规则');
            return;
        }
        
        const ruleList = REPLACEMENT_RULES.map((rule, i) => 
            `${i + 1}. ${rule.source} → ${rule.target}`
        ).join('\n');
        
        const index = prompt(
            '请输入要编辑的规则编号:\n\n' + ruleList, 
            '1'
        );
        
        if (!index) return;
        
        const ruleIndex = parseInt(index) - 1;
        if (isNaN(ruleIndex) || ruleIndex < 0 || ruleIndex >= REPLACEMENT_RULES.length) {
            alert('无效的规则编号');
            return;
        }
        
        const currentRule = REPLACEMENT_RULES[ruleIndex];
        const newSource = prompt('修改源域名:', currentRule.source);
        if (newSource === null) return; // 用户点击了取消
        
        const newTarget = prompt('修改目标域名:', currentRule.target);
        if (newTarget === null) return; // 用户点击了取消
        
        // 确保域名格式正确
        const cleanSource = newSource.replace(/^https?:\/\//, '').replace(/\/.*$/, '').trim();
        const cleanTarget = newTarget.replace(/^https?:\/\//, '').replace(/\/.*$/, '').trim();
        
        if (!cleanSource || !cleanTarget) {
            alert('域名格式不正确,请重新输入');
            return;
        }
        
        // 检查是否已存在相同源域名的规则(除了当前规则)
        const exists = REPLACEMENT_RULES.some((rule, i) => 
            i !== ruleIndex && rule.source === cleanSource
        );
        
        if (exists) {
            if (!confirm(`已存在源域名为 "${cleanSource}" 的规则,是否覆盖?`)) {
                return;
            }
            REPLACEMENT_RULES = REPLACEMENT_RULES.filter((_, i) => i !== ruleIndex);
            REPLACEMENT_RULES.push({ source: cleanSource, target: cleanTarget });
        } else {
            REPLACEMENT_RULES[ruleIndex] = { 
                source: cleanSource, 
                target: cleanTarget 
            };
        }
        
        saveRules();
        alert(`规则已更新:\n${cleanSource} → ${cleanTarget}`);
    };

    // 删除规则
    const deleteRule = () => {
        if (REPLACEMENT_RULES.length === 0) {
            alert('当前没有可用的替换规则');
            return;
        }
        
        const ruleList = REPLACEMENT_RULES.map((rule, i) => 
            `${i + 1}. ${rule.source} → ${rule.target}`
        ).join('\n');
        
        const index = prompt(
            '请输入要删除的规则编号:\n\n' + ruleList, 
            '1'
        );
        
        if (!index) return;
        
        const ruleIndex = parseInt(index) - 1;
        if (isNaN(ruleIndex) || ruleIndex < 0 || ruleIndex >= REPLACEMENT_RULES.length) {
            alert('无效的规则编号');
            return;
        }
        
        const rule = REPLACEMENT_RULES[ruleIndex];
        if (confirm(`确定要删除规则吗?\n${rule.source} → ${rule.target}`)) {
            REPLACEMENT_RULES = REPLACEMENT_RULES.filter((_, i) => i !== ruleIndex);
            saveRules();
            alert('规则已删除');
        }
    };

    // 显示当前规则
    const showCurrentRules = () => {
        if (REPLACEMENT_RULES.length === 0) {
            alert('当前没有配置替换规则');
            return;
        }
        
        const ruleList = REPLACEMENT_RULES.map(rule => 
            `• ${rule.source} → ${rule.target}`
        ).join('\n');
        
        alert(
            `当前替换规则 (${REPLACEMENT_RULES.length}条):\n\n` +
            ruleList + 
            '\n\n点击 plus.net 域名链接将自动替换为目标域名'
        );
    };

    // 注册右键菜单
    GM_registerMenuCommand('🛠️ 配置自动替换规则', () => {
        const options = [
            '1. 查看当前规则',
            '2. 添加新规则',
            '3. 编辑现有规则',
            '4. 删除规则',
            '5. 重置为默认规则',
            '6. 退出'
        ].join('\n');
        
        const choice = prompt(
            `请选择操作:\n\n${options}`, 
            '1'
        );
        
        switch (choice) {
            case '1':
                showCurrentRules();
                break;
            case '2':
                addRule();
                break;
            case '3':
                editRule();
                break;
            case '4':
                deleteRule();
                break;
            case '5':
                if (confirm('确定要重置为默认规则吗?当前自定义规则将丢失')) {
                    resetToDefault();
                }
                break;
            default:
                // 任何其他选择或取消都视为退出
                break;
        }
    });

    // 1. 点击链接时替换域名
    document.addEventListener('click', function (e) {
        if (e.target.tagName.toLowerCase() === 'a') {
            try {
                const url = new URL(e.target.href);
                const matchedRule = REPLACEMENT_RULES.find(rule => 
                    url.hostname.endsWith(rule.source)
                );

                if (matchedRule) {
                    const oldUrl = e.target.href;
                    url.hostname = matchedRule.target;
                    const newUrl = url.toString();
                    
                    if (CONFIG.debug) {
                        console.log(`[自动替换] ${oldUrl} → ${newUrl}`);
                    }
                    
                    e.preventDefault();
                    GM_openInTab(newUrl, {
                        active: true,
                        insert: true,
                        setParent: true
                    });
                }
            } catch (error) {
                console.warn('[链接解析失败]', e.target.href, error);
            }
        }
    });

    // 2. 使用 webRequest API 重定向请求
    const rules = REPLACEMENT_RULES.map(rule => ({
        selector: `*://*.${rule.source}/*`,
        action: {
            type: "redirect",
            redirectUrl: (details) => {
                const url = new URL(details.url);
                url.hostname = rule.target;
                const newUrl = url.toString();
                
                if (CONFIG.debug) {
                    console.log(`[请求重定向] ${details.url} → ${newUrl}`);
                }
                
                return newUrl;
            }
        }
    }));

    GM_webRequest(rules);

    // 3. 自动刷新失败页面功能
    if (CONFIG.autoRefresh) {
        const checkPageStatus = () => {
            const isEmpty = !document.body || 
                           (document.body.textContent || '').trim() === '' ||
                           document.body.innerHTML === '';
            
            const hasError = document.body && (
                document.body.textContent.includes('404') ||
                document.body.textContent.includes('Not Found') ||
                document.body.textContent.includes('Connection refused') ||
                document.body.textContent.includes('Unable to connect') ||
                document.body.textContent.includes('This site can’t be reached')
            );
            
            const currentDomain = window.location.hostname;
            const isTargetDomain = REPLACEMENT_RULES.some(rule => 
                currentDomain.endsWith(rule.source)
            );
            
            if (isTargetDomain && (isEmpty || hasError)) {
                if (CONFIG.debug) {
                    console.log('[自动刷新] 检测到加载失败页面,尝试刷新...');
                }
                
                try {
                    const url = new URL(window.location.href);
                    const matchedRule = REPLACEMENT_RULES.find(rule => 
                        currentDomain.endsWith(rule.source)
                    );
                    
                    if (matchedRule) {
                        url.hostname = matchedRule.target;
                        const newUrl = url.toString();
                        
                        if (CONFIG.debug) {
                            console.log(`[自动刷新] 尝试重定向: ${window.location.href} → ${newUrl}`);
                        }
                        
                        setTimeout(() => {
                            window.location.replace(newUrl);
                        }, CONFIG.refreshDelay);
                    }
                } catch (error) {
                    console.error('[自动刷新] 重定向失败:', error);
                }
            }
        };
        
        window.addEventListener('load', checkPageStatus);
        setInterval(checkPageStatus, 3000);
    }
    
    // 4. 添加视觉指示器(脚本运行状态提示)
    GM_addStyle(`
        .vm-plusnet-indicator {
            position: fixed;
            bottom: 20px;
            right: 20px;
            background: #4CAF50;
            color: white;
            padding: 8px 12px;
            border-radius: 4px;
            font-size: 12px;
            z-index: 9999;
            box-shadow: 0 2px 10px rgba(0,0,0,0.2);
            cursor: pointer;
            animation: vm-pulse 2s infinite;
            user-select: none;
        }
        @keyframes vm-pulse {
            0% { opacity: 0.7; }
            50% { opacity: 1; }
            100% { opacity: 0.7; }
        }
    `);
    
    const indicator = document.createElement('div');
    indicator.className = 'vm-plusnet-indicator';
    indicator.textContent = `自动替换已启用 (${REPLACEMENT_RULES.length}条规则) ✅`;
    indicator.title = '点击显示当前规则';
    indicator.addEventListener('click', showCurrentRules);
    document.body.appendChild(indicator);
})();