Reddit Multi Code Decoder for obfuscated content

Detects and translates obfuscation code in Reddit comments + Text selection popup for Google search

当前为 2025-06-08 提交的版本,查看 最新版本

// ==UserScript==
// @name         Reddit Multi Code Decoder for obfuscated content
// @namespace    [email protected]
// @version      0.2.0
// @description  Detects and translates obfuscation code in Reddit comments + Text selection popup for Google search
// @author       [email protected]
// @license      MIT
// @match        https://www.reddit.com/*
// @match        https://old.reddit.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Centralized comment selectors for both selection popup and binary decoder
    const COMMENT_SELECTORS = [
        // New Reddit
        '[data-testid="comment"]',
        'shreddit-comment',
        '.Comment',
        '[data-click-id="text"]',
        
        // Old Reddit
        '.usertext-body',
        '.md',
        
        // Generic fallbacks
        '.comment',
        '[class*="comment"]'
    ];

    // ===== TEXT SELECTION POPUP FEATURE =====
    let selectionPopup = null;

    function createSelectionPopup() {
        const popup = document.createElement('div');
        popup.id = 'text-selection-popup';
        popup.style.cssText = `
            position: fixed;
            background: #ffffff;
            border: 1px solid #e0e0e0;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
            padding: 8px;
            z-index: 10000;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            font-size: 14px;
            display: none;
            max-width: 200px;
        `;

        const searchButton = document.createElement('button');
        searchButton.textContent = '🔍 Search on Google';
        searchButton.style.cssText = `
            background: #4285f4;
            color: white;
            border: none;
            padding: 8px 12px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 13px;
            width: 100%;
            transition: background-color 0.2s;
            display: flex;
            align-items: center;
            justify-content: center;
            line-height: 1;
        `;
        
        searchButton.onmouseover = () => searchButton.style.backgroundColor = '#3367d6';
        searchButton.onmouseout = () => searchButton.style.backgroundColor = '#4285f4';

        popup.appendChild(searchButton);
        document.body.appendChild(popup);
        
        return { popup, searchButton };
    }

    function hideSelectionPopup() {
        if (selectionPopup && selectionPopup.popup) {
            selectionPopup.popup.style.display = 'none';
        }
    }

    function showSelectionPopup(x, y, selectedText) {
        if (!selectionPopup) {
            selectionPopup = createSelectionPopup();
        }

        const { popup, searchButton } = selectionPopup;
        
        // Update button click handler with current selected text
        searchButton.onclick = () => {
            const encodedText = encodeURIComponent(selectedText.trim());
            window.open(`https://www.google.com/search?q=${encodedText}`, '_blank');
            hideSelectionPopup();
        };

        // Position popup near mouse cursor (using clientX/clientY with fixed positioning)
        popup.style.left = `${x + 10}px`;
        popup.style.top = `${y + 10}px`;
        popup.style.display = 'block';

        // Adjust position if popup goes off screen
        const rect = popup.getBoundingClientRect();
        const viewportWidth = window.innerWidth;
        const viewportHeight = window.innerHeight;

        if (rect.right > viewportWidth) {
            popup.style.left = `${x - rect.width - 10}px`;
        }
        if (rect.bottom > viewportHeight) {
            popup.style.top = `${y - rect.height - 10}px`;
        }
    }

    // Check if the target element is within a comment
    function isWithinComment(element) {
        return COMMENT_SELECTORS.some(selector => element.closest(selector));
    }

    // Handle text selection events
    document.addEventListener('mouseup', (e) => {
        setTimeout(() => {
            const selection = window.getSelection();
            const selectedText = selection.toString().trim();
            
            if (selectedText.length > 0 && isWithinComment(e.target)) {
                // Use clientX/clientY with fixed positioning for proper viewport positioning
                showSelectionPopup(e.clientX, e.clientY, selectedText);
            } else {
                hideSelectionPopup();
            }
        }, 10);
    });

    // Hide popup when clicking elsewhere or on escape
    document.addEventListener('mousedown', (e) => {
        if (selectionPopup && selectionPopup.popup && !selectionPopup.popup.contains(e.target)) {
            hideSelectionPopup();
        }
    });

    document.addEventListener('keydown', (e) => {
        if (e.key === 'Escape') {
            hideSelectionPopup();
        }
    });

    // ===== BINARY DECODER FUNCTIONALITY =====

    // Binary pattern: sequences of 0s and 1s, typically in groups of 8 (bytes)
    // Matches patterns like: 01001000 01100101 01101100 01101100 01101111
    const binaryPattern = /(?:\b[01]{8}\b[\s]*){2,}/g;

    // Function to convert binary string to text
    function binaryToText(binaryString) {
        try {
            // Remove all whitespace and split into 8-bit chunks
            const cleanBinary = binaryString.replace(/\s/g, '');
            if (cleanBinary.length % 8 !== 0) {
                return null; // Invalid binary string
            }

            let result = '';
            for (let i = 0; i < cleanBinary.length; i += 8) {
                const byte = cleanBinary.substr(i, 8);
                const charCode = parseInt(byte, 2);

                // Only convert printable ASCII characters (32-126) and common whitespace
                if ((charCode >= 32 && charCode <= 126) || charCode === 9 || charCode === 10 || charCode === 13) {
                    result += String.fromCharCode(charCode);
                } else {
                    return null; // Contains non-printable characters, probably not text
                }
            }
            return result;
        } catch (e) {
            return null;
        }
    }

    // Function to create translation component
    function createTranslationComponent(binaryText, translatedText) {
        const container = document.createElement('div');
        container.style.cssText = `
            margin: 8px 0;
            padding: 12px;
            background: #f8f9fa;
            border: 1px solid #e0e0e0;
            border-radius: 8px;
            font-family: monospace;
            font-size: 14px;
            position: relative;
            display: flex;
            align-items: center;
            gap: 12px;
        `;

        const header = document.createElement('div');
        header.style.cssText = `
            font-weight: bold;
            color: #1a73e8;
            font-size: 12px;
            text-transform: uppercase;
            letter-spacing: 0.5px;
            flex-shrink: 0;
        `;
        header.textContent = '🔢 Binary:';

        const translationDiv = document.createElement('div');
        translationDiv.style.cssText = `
            background: #e8f5e8;
            padding: 8px;
            border-radius: 4px;
            font-weight: bold;
            color: #2d5a2d;
            flex-grow: 1;
        `;
        translationDiv.textContent = `${translatedText}`;

        const googleLink = document.createElement('a');
        googleLink.href = `https://www.google.com/search?q=${encodeURIComponent(translatedText)}&tbm=isch`;
        googleLink.target = '_blank';
        googleLink.rel = 'noopener noreferrer';
        googleLink.style.cssText = `
            background: #4285f4;
            color: white;
            padding: 6px 8px;
            border-radius: 4px;
            text-decoration: none;
            display: flex;
            align-items: center;
            gap: 4px;
            font-size: 12px;
            flex-shrink: 0;
            transition: background-color 0.2s;
        `;
        googleLink.onmouseover = () => googleLink.style.backgroundColor = '#3367d6';
        googleLink.onmouseout = () => googleLink.style.backgroundColor = '#4285f4';

        // Google search icon (SVG)
        const googleIcon = document.createElement('span');
        googleIcon.innerHTML = `
            <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
                <path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
            </svg>
        `;

        googleLink.appendChild(googleIcon);
        googleLink.appendChild(document.createTextNode('Search'));

        container.appendChild(header);
        container.appendChild(translationDiv);
        container.appendChild(googleLink);

        return container;
    }

    // Function to process text nodes and detect binary
    function processTextNode(textNode) {
        const text = textNode.textContent;
        const matches = text.match(binaryPattern);

        if (matches) {
            matches.forEach(match => {
                const translated = binaryToText(match);
                if (translated && translated.trim().length > 0) {
                    // Create a wrapper for the original text node
                    const wrapper = document.createElement('span');
                    textNode.parentNode.insertBefore(wrapper, textNode);
                    wrapper.appendChild(textNode);

                    // Add translation component after the text
                    const translationComponent = createTranslationComponent(match, translated);
                    wrapper.parentNode.insertBefore(translationComponent, wrapper.nextSibling);
                }
            });
        }
    }

    // Function to scan for binary in comments
    function scanForBinary() {
        // Create selectors for paragraph elements within comments
        const commentParagraphSelectors = COMMENT_SELECTORS.map(selector => `${selector} p`);

        commentParagraphSelectors.forEach(selector => {
            const elements = document.querySelectorAll(selector);
            elements.forEach(element => {
                // Skip if already processed
                if (element.hasAttribute('data-binary-processed')) return;
                element.setAttribute('data-binary-processed', 'true');

                // Process all text nodes in the element
                const walker = document.createTreeWalker(
                    element,
                    NodeFilter.SHOW_TEXT,
                    null,
                    false
                );

                const textNodes = [];
                let node;
                while (node = walker.nextNode()) {
                    textNodes.push(node);
                }

                textNodes.forEach(processTextNode);
            });
        });
    }

    // Initial scan
    setTimeout(scanForBinary, 1000);

    // Watch for dynamically loaded content
    const observer = new MutationObserver((mutations) => {
        let shouldScan = false;
        mutations.forEach((mutation) => {
            mutation.addedNodes.forEach((node) => {
                if (node.nodeType === Node.ELEMENT_NODE) {
                    // Check if the added node contains comments using centralized selectors
                    if (node.querySelector && COMMENT_SELECTORS.some(selector => 
                        node.querySelector(selector) || node.matches(selector)
                    )) {
                        shouldScan = true;
                    }
                }
            });
        });

        if (shouldScan) {
            setTimeout(scanForBinary, 500);
        }
    });

    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

    // Scan when comments are expanded or loaded
    document.addEventListener('click', (e) => {
        // Check for comment expansion buttons
        if (e.target.matches('[aria-expanded]') ||
            e.target.closest('[aria-expanded]') ||
            e.target.textContent.includes('more replies') ||
            e.target.textContent.includes('comments')) {
            setTimeout(scanForBinary, 1000);
        }
    });

    console.log('Reddit Binary Decoder v0.2.0 loaded - Binary translation + Text selection Google search');
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址