Greasy Fork

Greasy Fork is available in English.

福利吧百家姓暗号转换器

自动识别页面中的福利吧百家姓暗号并转换为磁力链接

// ==UserScript==
// @name         福利吧百家姓暗号转换器
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  自动识别页面中的福利吧百家姓暗号并转换为磁力链接
// @author       MR.Z
// @match        https://fuliba2023.net/*
// @grant        GM_setClipboard
// ==/UserScript==

// ==UserScript==
// @name        百家姓暗号
// @namespace   http://tampermonkey.net/
// @version     1.0
// @description 识别和转换网页中的"百家姓暗号"
// @license    MIT
// ==/UserScript==

(function() {
    'use strict';

    // 百家姓映射表
    const surnameMap = {
        "赵":"0", "钱":"1", "孙":"2", "李":"3", "周":"4", "吴":"5", "郑":"6", "王":"7", "冯":"8", "陈":"9",
        "褚":"a", "卫":"b", "蒋":"c", "沈":"d", "韩":"e", "杨":"f", "朱":"g", "秦":"h", "尤":"i", "许":"j",
        "何":"k", "吕":"l", "施":"m", "张":"n", "孔":"o", "曹":"p", "严":"q", "华":"r", "金":"s", "魏":"t",
        "陶":"u", "姜":"v", "戚":"w", "谢":"x", "邹":"y", "喻":"z", "福":"A", "水":"B", "窦":"C", "章":"D",
        "云":"E", "苏":"F", "潘":"G", "葛":"H", "奚":"I", "范":"J", "彭":"K", "郎":"L", "鲁":"M", "韦":"N",
        "昌":"O", "马":"P", "苗":"Q", "凤":"R", "花":"S", "方":"T", "俞":"U", "任":"V", "袁":"W", "柳":"X",
        "唐":"Y", "罗":"Z", "薛":".", "伍":"-", "余":"_", "米":"+", "贝":"=", "姚":"/", "孟":"?", "顾":"#",
        "尹":"%", "江":"&", "钟":"*"
    };

    // 获取评论区元素
    function findCommentContainer() {
        // 常见评论区选择器
        const commentSelectors = [
            '#comments',
            '#comment',
            '.comments',
            '.comment',
            '.comment-list',
            '.comment-section',
            '.comment-container',
            '.comment-area',
            '.article-comments',
            '.post-comments'
        ];
        
        for (const selector of commentSelectors) {
            const element = document.querySelector(selector);
            if (element) {
                return element;
            }
        }
        return document.body; // 如果找不到则返回整个body
    }

    // 自动扫描页面文本节点
    function scanTextNodes() {
        console.log('开始扫描页面文本节点...');
        const commentContainer = findCommentContainer();
        const walker = document.createTreeWalker(
            commentContainer,
            NodeFilter.SHOW_TEXT,
            {
                acceptNode: function(node) {
                    // 跳过script、style、textarea等元素
                    if (node.parentNode.nodeName === 'SCRIPT' || 
                        node.parentNode.nodeName === 'STYLE' ||
                        node.parentNode.nodeName === 'TEXTAREA' ||
                        node.parentNode.nodeName === 'INPUT' ||
                        node.parentNode.hasAttribute('data-surname-ignore')) {
                        return NodeFilter.FILTER_REJECT;
                    }
                    // 检查是否已处理过
                    if (node.parentNode.hasAttribute('data-surname-processed')) {
                        return NodeFilter.FILTER_REJECT;
                    }
                    return NodeFilter.FILTER_ACCEPT;
                }
            },
            false
        );

        let node;
        let foundCount = 0;
        while (node = walker.nextNode()) {
            const text = node.nodeValue.trim();
            if (text.length === 0) continue;

            console.log('扫描到文本:', text.substring(0, 50) + (text.length > 50 ? '...' : ''));
            
            if (isSurnameCode(text)) {
                console.log('发现百家姓暗号:', text);
                foundCount++;
                processSurnameCode(node, text);
            }
        }
        console.log(`扫描完成,共发现${foundCount}处百家姓暗号`);
    }

    // 检查是否是百家姓暗号
    function isSurnameCode(text) {
        // 检查是否包含至少3个百家姓字符,提高准确性
        let surnameCount = 0;
        const surnames = Object.keys(surnameMap);
        
        for (const surname of surnames) {
            if (text.includes(surname)) {
                surnameCount++;
                if (surnameCount >= 3) {
                    return true;
                }
            }
        }
        return false;
    }

    // 处理找到的百家姓暗号
    function processSurnameCode(textNode, text) {
        // 双重检查是否已经处理过
        if (textNode.parentNode.hasAttribute('data-surname-processed') ||
            textNode.parentNode.querySelector('button[data-surname-button]')) {
            return;
        }
        
        const span = document.createElement('span');
        span.textContent = text;
        
        const button = document.createElement('button');
        button.textContent = '识别暗号';
        button.setAttribute('data-surname-button', 'true');
        button.style.cssText = `
            margin-left: 8px;
            padding: 4px 12px;
            font-size: 13px;
            font-weight: 500;
            color: #fff;
            background-color: #4a6cf7;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            transition: all 0.2s ease;
            box-shadow: 0 1px 2px rgba(0,0,0,0.1);
            min-width: 80px;
            min-height: 32px;
        `;

        // 移动端样式调整
        const mobileStyle = document.createElement('style');
        mobileStyle.textContent = `
            @media (max-width: 768px) {
                button[data-surname-button] {
                    padding: 8px 16px;
                    font-size: 15px;
                    min-width: 90px;
                    min-height: 36px;
                }
                
                div[data-surname-toast] {
                    font-size: 16px;
                    padding: 16px 28px;
                    bottom: 30px;
                    max-width: 80%;
                }
            }
        `;
        document.head.appendChild(mobileStyle);
        button.addEventListener('mouseenter', () => {
            button.style.backgroundColor = '#3a5ce9';
            button.style.transform = 'translateY(-1px)';
        });
        button.addEventListener('mouseleave', () => {
            button.style.backgroundColor = '#4a6cf7';
            button.style.transform = 'none';
        });
        
        // 创建美观的提示函数
        function showToast(message) {
            const toast = document.createElement('div');
            toast.textContent = message;
            toast.setAttribute('data-surname-toast', 'true');
            toast.style.cssText = `
                position: fixed;
                bottom: 20px;
                left: 50%;
                transform: translateX(-50%);
                background-color: #4a6cf7;
                color: white;
                padding: 12px 24px;
                border-radius: 4px;
                font-size: 14px;
                box-shadow: 0 4px 12px rgba(0,0,0,0.15);
                z-index: 9999;
                opacity: 0;
                transition: opacity 0.3s ease;
                white-space: nowrap;
            `;
            document.body.appendChild(toast);
            
            // 显示动画
            setTimeout(() => {
                toast.style.opacity = '1';
            }, 10);
            
            // 3秒后自动消失
            setTimeout(() => {
                toast.style.opacity = '0';
                setTimeout(() => {
                    document.body.removeChild(toast);
                }, 300);
            }, 3000);
        }

        button.addEventListener('click', function() {
            const magnetLink = 'magnet:?xt=urn:btih:' + encodeToCipher(text);
            GM_setClipboard(magnetLink);
            showToast('磁力链接已复制');
        });

        const wrapper = document.createElement('span');
        wrapper.setAttribute('data-surname-processed', 'true');
        wrapper.appendChild(span);
        wrapper.appendChild(button);
        
        textNode.parentNode.replaceChild(wrapper, textNode);
    }

    // 转换函数
    function encodeToCipher(text) {
        const chars = text.split('');
        let result = '';
        for(let i = 0; i < chars.length; i++) {
            const cipher = getValueByKey(surnameMap, chars[i]);
            result += cipher || '';
        }
        return result;
    }

    function getValueByKey(obj, key) {
        for(let k in obj) {
            if(k === key) {
                return obj[k];
            }
        }
        return '';
    }

    // 初始化扫描 - 优化性能版本
    let isScanning = false;
    let scanQueue = [];
    let debounceTimer;

    function throttledScan() {
        if (isScanning) {
            return;
        }

        isScanning = true;
        const startTime = performance.now();
        
        // 每次最多处理50个节点
        const nodesToProcess = scanQueue.splice(0, 50);
        for (const node of nodesToProcess) {
            // 跳过已处理的节点
            if (node.parentNode && node.parentNode.hasAttribute('data-surname-processed')) {
                continue;
            }
            
            const text = node.nodeValue.trim();
            if (text.length > 0 && isSurnameCode(text)) {
                processSurnameCode(node, text);
            }
        }

        isScanning = false;
        console.log(`扫描完成,耗时${(performance.now() - startTime).toFixed(2)}ms`);

        if (scanQueue.length > 0) {
            setTimeout(throttledScan, 100);
        }
    }

    function handleMutations(mutations) {
        // 暂停期间不处理
        if (!observer) return;
        
        mutations.forEach(mutation => {
            if (mutation.addedNodes.length) {
                mutation.addedNodes.forEach(node => {
                    if (node.nodeType === Node.ELEMENT_NODE) {
                        const walker = document.createTreeWalker(
                            node,
                            NodeFilter.SHOW_TEXT,
                            {
                                acceptNode: function(n) {
                                    // 跳过已处理节点
                                    if (processedNodes.has(n)) {
                                        return NodeFilter.FILTER_REJECT;
                                    }
                                    if (n.parentNode.nodeName === 'SCRIPT' || 
                                        n.parentNode.nodeName === 'STYLE' ||
                                        n.parentNode.nodeName === 'TEXTAREA' ||
                                        n.parentNode.nodeName === 'INPUT') {
                                        return NodeFilter.FILTER_REJECT;
                                    }
                                    return NodeFilter.FILTER_ACCEPT;
                                }
                            },
                            false
                        );

                        let textNode;
                        while (textNode = walker.nextNode()) {
                            // 检查是否已存在相同内容的按钮
                            if (!textNode.parentNode.querySelector('button[data-surname-button]')) {
                                scanQueue.push(textNode);
                            }
                        }
                    }
                });
            }
        });

        // 防抖处理
        clearTimeout(debounceTimer);
        debounceTimer = setTimeout(throttledScan, 300); // 缩短防抖时间
    }

    // 延迟初始化扫描
    setTimeout(() => {
        try {
            console.log('开始初始扫描...');
            const initialWalker = document.createTreeWalker(
                document.body,
                NodeFilter.SHOW_TEXT,
                {
                    acceptNode: function(node) {
                        if (node.parentNode.nodeName === 'SCRIPT' || 
                            node.parentNode.nodeName === 'STYLE' ||
                            node.parentNode.nodeName === 'TEXTAREA' ||
                            node.parentNode.nodeName === 'INPUT') {
                            return NodeFilter.FILTER_REJECT;
                        }
                        return NodeFilter.FILTER_ACCEPT;
                    }
                },
                false
            );

            let initialNode;
            while (initialNode = initialWalker.nextNode()) {
                scanQueue.push(initialNode);
            }
            
            throttledScan();

            // 监听DOM变化,使用防抖
            const observer = new MutationObserver(handleMutations);
            observer.observe(document.body, {
                childList: true,
                subtree: true,
                attributes: false,
                characterData: false
            });
        } catch (e) {
            console.error('初始化扫描出错:', e);
        }
    }, 2000); // 延长初始延迟
})();