Slack Conversation Scraper & Custom Buttons

Combines conversation scraper and custom message buttons with smooth UI and arrow key toggle animation.

// ==UserScript==
// @name         Slack Conversation Scraper & Custom Buttons
// @version      2.7.1
// @description  Combines conversation scraper and custom message buttons with smooth UI and arrow key toggle animation.
// @author       Mahmudul Hasan Shawon
// @icon         https://www.slack.com/favicon.ico
// @match        https://app.slack.com/client/*
// @license      MIT
// @grant        none
// @namespace https://gf.qytechs.cn/users/1392874
// ==/UserScript==

(function () {
    'use strict';

    const buttons = [
        { id: 'goodMorningButton', text: 'GM!', message: 'Good morning!' },
        { id: 'okButton', text: 'Ok', message: 'Ok' },
        { id: 'scrapeConversationButton', text: '📋 Scrape All', message: '' },
        { id: 'copyConversationButton', text: '✨ Guess Reply', message: '' },
    ];

    function waitForSlackInterface() {
        const checkInterval = setInterval(() => {
            const textBox = document.querySelector('[data-message-input="true"] .ql-editor');
            if (textBox) {
                clearInterval(checkInterval);
                addControlBox();
            }
        }, 1000);
    }

    function addControlBox() {
        const controlBox = document.createElement('div');
        Object.assign(controlBox.style, {
            position: 'fixed',
            bottom: '35px',
            right: '100px',
            width: '200px',
            padding: '15px',
            backgroundColor: '#ffffff',
            borderRadius: '12px',
            boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)',
            fontFamily: 'Arial, sans-serif',
            zIndex: '9999',
            transition: 'transform 0.3s ease-in-out, opacity 0.3s ease-in-out',
            transform: 'translateY(0)',
            opacity: '1',
        });
        controlBox.id = 'slackControlBox';

        const toggleButton = document.createElement('div');
        Object.assign(toggleButton.style, {
            position: 'fixed',
            bottom: '46px',
            right: '120px',
            width: '36px',
            height: '36px',
            backgroundColor: '#C3FF93',
            borderRadius: '50%',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            cursor: 'pointer',
            zIndex: '10000',
        });
        toggleButton.innerHTML = `<svg width="24" height="24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3 12h18M12 3v18" stroke="#333" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
        toggleButton.title = 'Hide/Show Control Box';
        toggleButton.addEventListener('click', () => toggleControlBox(controlBox));

        document.body.appendChild(toggleButton);

        const sideBySideContainer = document.createElement('div');
        Object.assign(sideBySideContainer.style, {
            display: 'flex',
            justifyContent: 'space-between',
            gap: '10px',
            marginBottom: '10px',
        });

        const goodMorningButton = createButton(buttons[0]);
        const okButton = createButton(buttons[1]);
        goodMorningButton.style.flex = '1';
        okButton.style.flex = '1';

        sideBySideContainer.appendChild(goodMorningButton);
        sideBySideContainer.appendChild(okButton);
        controlBox.appendChild(sideBySideContainer);

        buttons.slice(2).forEach((btn) => {
            const button = createButton(btn);
            controlBox.appendChild(button);
        });

        const inputField = createInputField();
        controlBox.appendChild(inputField);

        document.body.appendChild(controlBox);

        // Add keyboard toggle functionality
        document.addEventListener('keydown', (e) => handleKeyPress(e, controlBox));
    }

    function toggleControlBox(controlBox) {
        const isHidden = controlBox.style.opacity === '0';
        controlBox.style.opacity = isHidden ? '1' : '0';
        controlBox.style.transform = isHidden ? 'translateY(0)' : 'translateY(20px)';
    }

    function handleKeyPress(event, controlBox) {
        if (event.key === 'ArrowDown') {
            controlBox.style.opacity = '0';
            controlBox.style.transform = 'translateY(20px)';
        } else if (event.key === 'ArrowUp') {
            controlBox.style.opacity = '1';
            controlBox.style.transform = 'translateY(0)';
        }
    }

    function createButton({ id, text, message }) {
        const button = document.createElement('button');
        Object.assign(button.style, {
            display: 'block',
            width: '100%',
            marginBottom: '10px',
            padding: '8px 0',
            backgroundColor: '#f3f3f3',
            color: '#333',
            fontSize: '14px',
            border: 'none',
            borderRadius: '8px',
            cursor: 'pointer',
            transition: 'background-color 0.2s ease-in-out',
        });
        button.id = id;
        button.textContent = text;
        button.addEventListener('mouseenter', () => {
            button.style.backgroundColor = '#e0e0e0';
        });
        button.addEventListener('mouseleave', () => {
            button.style.backgroundColor = '#f3f3f3';
        });

        button.addEventListener('click', () => {
            if (id === 'copyConversationButton') copyMessages();
            else if (id === 'scrapeConversationButton') scrapeConversation();
            else sendMessage(message);
        });
        return button;
    }

    function createInputField() {
        const input = document.createElement('input');
        Object.assign(input.style, {
            width: '65%',
            padding: '8px',
            fontSize: '12px',
            border: '1px solid #ccc',
            borderRadius: '8px',
            textAlign: 'center',
            boxSizing: 'border-box',
        });
        input.id = 'messageCountInputField';
        input.type = 'number';
        input.placeholder = 'Number of messages';
        return input;
    }

    function scrapeConversation() {
        const messageBlocks = Array.from(document.querySelectorAll('.c-message_kit__background'));
        let conversation = '', lastSender = null;

        messageBlocks.forEach((block) => {
            const sender = block.querySelector('.c-message__sender_button')?.textContent.trim() || lastSender;
            if (sender) lastSender = sender;

            const messageText = Array.from(block.querySelectorAll('.p-rich_text_section'))
                .map((el) => el.textContent.trim())
                .join(' ').trim();

            if (messageText) {
                conversation += `${lastSender}: ${messageText}\n\n`;
            }
        });

        if (conversation.trim()) {
            copyToClipboard(conversation, 'All conversations copied!');
        } else {
            showPopUp('No conversation found.');
        }
    }




   function copyMessages() {
    const numberOfMessages = document.getElementById('messageCountInputField').value || 2;
    const messageBlocks = Array.from(document.querySelectorAll('.c-message_kit__background')).slice(-numberOfMessages);
    let conversation = '', lastSender = null;

    messageBlocks.forEach((block) => {
        const sender = block.querySelector('.c-message__sender_button')?.textContent.trim() || lastSender;
        if (sender) lastSender = sender;

        const messageText = Array.from(block.querySelectorAll('.p-rich_text_section'))
            .map((el) => el.textContent.trim())
            .join(' ').trim();

        if (messageText) {
            conversation += `${lastSender}: ${messageText}\n\n`;
        }
    });

    if (conversation.trim()) {
        const formatted = `${conversation}Guess reply:`;
        copyToClipboard(formatted, `Last ${numberOfMessages} messages copied!`);
    } else {
        showPopUp(`Unable to copy ${numberOfMessages} messages.`);
    }
}






    function copyToClipboard(text, message) {
        navigator.clipboard.writeText(text)
            .then(() => showPopUp(message))
            .catch(() => showPopUp('Failed to copy.'));
    }

    function sendMessage(message) {
        const textBox = document.querySelector('[data-message-input="true"] .ql-editor');
        const sendButton = document.querySelector('[data-qa="texty_send_button"]');

        if (textBox) {
            textBox.focus();
            document.execCommand('insertText', false, message);
            setTimeout(() => sendButton?.click(), 500);
        } else {
            showPopUp('Message box not found.');
        }
    }

    function showPopUp(message) {
        const popUp = document.createElement('div');
        Object.assign(popUp.style, {
            position: 'fixed',
            bottom: '245px',
            right: '100px',
            backgroundColor: '#A294F9',
            color: '#FFFFFF',
            padding: '10px 15px',
            borderRadius: '16px 16px 0px 16px',
            fontSize: '14px',
            boxShadow: '0px 4px 8px rgba(0, 0, 0, 0.3)',
            animation: 'fadeInOut 3s ease-in-out',
            zIndex: '9999',
        });
        popUp.textContent = message;
        document.body.appendChild(popUp);

        setTimeout(() => popUp.remove(), 3000);
    }

    const styleSheet = document.createElement('style');
    styleSheet.type = 'text/css';
    styleSheet.innerText = `
        @keyframes fadeInOut {
            0% {
                opacity: 0;
                transform: translateY(20px);
            }
            10% {
                opacity: 1;
                transform: translateY(0);
            }
            90% {
                opacity: 1;
                transform: translateY(0);
            }
            100% {
                opacity: 0;
                transform: translateY(20px);
            }
        }
    `;
    document.head.appendChild(styleSheet);

    waitForSlackInterface();
})();

QingJ © 2025

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