Twitter sol/bsc to gmgn

sol/bsc to gmgn

当前为 2025-07-12 提交的版本,查看 最新版本

// ==UserScript==
// @name         Twitter sol/bsc to gmgn
// @namespace    http://tampermonkey.net/
// @version      0.7
// @description  sol/bsc to gmgn
// @author       mqtt
// @match        https://x.com/*
// @match        https://twitter.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=twitter.com
// @grant        GM_addStyle
// ==/UserScript==

(function () {
    'use strict';
    const solAddressRegex = /\b(?![^<]*>|[^<>]*<\/)([1-9A-HJ-NP-Za-km-z]{32,44})(?![a-zA-Z0-9])\b/g;
    const evmAddressRegex = /\b(?![^<]*>|[^<>]*<\/)(0x[a-fA-F0-9]{40})(?![a-zA-Z0-9])\b/g;
    const TWEET_TEXT_SELECTORS = [
        '[data-testid="tweetText"]',
        'div[data-testid="card.wrapper"] div[lang]',
        'div[data-testid="cellInnerDiv"] div[lang]'
    ].join(', ');
    const SOL_LINK_CLASS = 'sol-address-link';
    const EVM_LINK_CLASS = 'evm-address-link';
    const processedNodes = new WeakSet();
    let processQueue = [];
    let processTimer = null;
    GM_addStyle(`
        a.${ SOL_LINK_CLASS } {
            color: red !important;
            text-decoration: underline !important;
            position: relative;
            z-index: 100;
            transition: all 0.2s ease;
        }
        a.${ SOL_LINK_CLASS }:hover {
            filter: brightness(1.2);
            transform: translateY(-1px);
        }
        a.${ EVM_LINK_CLASS } {
            color: red !important;
            text-decoration: underline !important;
            position: relative;
            z-index: 100;
            transition: all 0.2s ease;
        }
        a.${ EVM_LINK_CLASS }:hover {
            filter: brightness(1.2);
            transform: translateY(-1px);
        }
    `);
    function debounce(func, wait) {
        let timeout;
        return function () {
            const context = this;
            const args = arguments;
            clearTimeout(timeout);
            timeout = setTimeout(() => func.apply(context, args), wait);
        };
    }
    function processAddresses() {
        if (document.readyState !== 'complete') {
            return;
        }
        const nodesToProcess = [...processQueue];
        processQueue = [];
        nodesToProcess.forEach(element => {
            if (processedNodes.has(element)) {
                return;
            }
            const textNodes = [];
            const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, {
                acceptNode: element_1 => {
                    if (element_1.parentElement.closest('script, style, noscript, template, a') || processedNodes.has(element_1.parentElement)) {
                        return NodeFilter.FILTER_SKIP;
                    }
                    if (/\b([a-zA-Z0-9]{32,44}|0x[a-fA-F0-9]{40})\b/.test(element_1.textContent)) {
                        return NodeFilter.FILTER_ACCEPT;
                    }
                    return NodeFilter.FILTER_SKIP;
                }
            }, false);
            while (walker.nextNode()) {
                textNodes.push(walker.currentNode);
            }
            textNodes.forEach(element_2 => {
                const parent = element_2.parentElement;
                if (processedNodes.has(parent)) {
                    return;
                }
                const text = element_2.textContent;
                if (!solAddressRegex.test(text) && !evmAddressRegex.test(text)) {
                    return;
                }
                const fragment = document.createDocumentFragment();
                let lastIndex = 0;
                solAddressRegex.lastIndex = 0;
                evmAddressRegex.lastIndex = 0;
                const combinedMatches = [];
                let solMatch;
                while (solMatch = solAddressRegex.exec(text)) {
                    combinedMatches.push({
                        index: solMatch.index,
                        length: solMatch[0].length,
                        type: 'sol',
                        address: solMatch[1]
                    });
                }
                let evmMatch;
                while (evmMatch = evmAddressRegex.exec(text)) {
                    combinedMatches.push({
                        index: evmMatch.index,
                        length: evmMatch[0].length,
                        type: 'evm',
                        address: evmMatch[1]
                    });
                }
                combinedMatches.sort((a, b) => a.index - b.index);
                combinedMatches.forEach(element_3 => {
                    if (element_3.index > lastIndex) {
                        fragment.appendChild(document.createTextNode(text.substring(lastIndex, element_3.index)));
                    }
                    const link = document.createElement('a');
                    if (element_3.type === 'sol') {
                        link.href = `https://gmgn.ai/sol/token/ufeXUTjX_${ element_3.address }?filter=All`;
                        link.className = SOL_LINK_CLASS;
                    } else {
                        link.href = `https://gmgn.ai/bsc/token/${ element_3.address }`;
                        link.className = EVM_LINK_CLASS;
                    }
                    link.target = '_blank';
                    link.rel = 'noopener noreferrer';
                    link.textContent = element_3.address;
                    link.dataset[`${ element_3.type }Address`] = element_3.address;
                    fragment.appendChild(link);
                    lastIndex = element_3.index + element_3.length;
                });
                if (lastIndex < text.length) {
                    fragment.appendChild(document.createTextNode(text.substring(lastIndex)));
                }
                parent.replaceChild(fragment, element_2);
                processedNodes.add(parent);
            });
        });
    }
    function enqueueNodeProcessing(node) {
        if (!processedNodes.has(node) && node.textContent) {
            processQueue.push(node);
            if (!processTimer) {
                processTimer = setTimeout(() => {
                    processAddresses();
                    processTimer = null;
                }, 100);
            }
        }
    }
    function setupObserver() {
        const targetNode = document.querySelector('[data-testid="primaryColumn"]') || document.body;
        const observer = new MutationObserver(element => {
            let hasRelevantChanges = false;
            for (const mutation of element) {
                if (mutation.type === 'childList') {
                    for (const node of mutation.addedNodes) {
                        if (node.nodeType !== 1) {
                            continue;
                        }
                        if (node.matches(TWEET_TEXT_SELECTORS)) {
                            enqueueNodeProcessing(node);
                            hasRelevantChanges = true;
                        } else {
                            if (node.querySelector(TWEET_TEXT_SELECTORS)) {
                                node.querySelectorAll(TWEET_TEXT_SELECTORS).forEach(element => {
                                    enqueueNodeProcessing(element);
                                });
                                hasRelevantChanges = true;
                            }
                        }
                    }
                }
                if (hasRelevantChanges) {
                    break;
                }
            }
        });
        observer.observe(targetNode, {
            childList: true,
            subtree: true,
            attributes: false,
            characterData: false
        });
    }
    function initialize() {
        document.querySelectorAll(TWEET_TEXT_SELECTORS).forEach(element => {
            enqueueNodeProcessing(element);
        });
        setupObserver();
        window.addEventListener('focus', debounce(() => {
            document.querySelectorAll(TWEET_TEXT_SELECTORS).forEach(element => {
                enqueueNodeProcessing(element);
            });
        }, 500));
        window.addEventListener('scroll', debounce(() => {
            const visibleElements = document.querySelectorAll(TWEET_TEXT_SELECTORS);
            visibleElements.forEach(element => {
                const rect = element.getBoundingClientRect();
                if (rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth)) {
                    enqueueNodeProcessing(element);
                }
            });
        }, 500));
    }
    function waitForTwitterLoad() {
        const checkInterval = setInterval(() => {
            if (document.querySelector('[data-testid="primaryColumn"]')) {
                clearInterval(checkInterval);
                initialize();
            }
        }, 500);
    }
    if (document.readyState === 'complete') {
        waitForTwitterLoad();
    } else {
        window.addEventListener('load', waitForTwitterLoad);
    }
}());