Twitch HeLLSocials

Отправляет команды !дис, !тг, !ютуб в чат Twitch по нажатию горячей клавиши!

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Twitch HeLLSocials
// @namespace    http://tampermonkey.net/
// @version      0.7
// @description  Отправляет команды !дис, !тг, !ютуб в чат Twitch по нажатию горячей клавиши!
// @author       dear_lesberk
// @match        https://www.twitch.tv/dear_hellgirl
// @icon         https://www.google.com/s2/favicons?sz=64&domain=twitch.tv
// @grant        none
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    //Конфиг
    const BIND_KEY = 'F2'; // бинд XD
    const COMMANDS = ['!дис', '!тг', '!ютуб']; // команды, если что можешь дополнить! Или меня попроси, хз
    const BASE_DELAY = 800; // базовая задержка в мс (увеличено для обхода медленного режима)

    // Ты общаешься в чате?
    function isTyping() {
        const activeElement = document.activeElement;
        if (!activeElement) return false;

        // проверка на определение поля чата
        const tagName = activeElement.tagName.toLowerCase();
        if(tagName === 'input' || tagName === 'textarea') return true;

        // проверка на изменяемость
        if (activeElement.isContentEditable) return true;

        // проверка доступности поля чата
        if (activeElement.closest('[data-a-target="chat-input"]') ||
            activeElement.closest('.chat-input') ||
            activeElement.closest('.text-area')) {
            return true;
        }

        return false;
    }

    // Доказываем твичу, что ты не андроид POCO X3 PRO 13 мегапикселей в квадрате
    function simulateTyping(inputElement, text) {
        inputElement.focus();
        inputElement.click();

        // Очистка значения у элемента ввода
        inputElement.value = '';

        // Имитация постепенного ввода (более естественно)
        inputElement.value = text;

        // Создаем целую кучу событий для убедительности
        const events = [
            new Event('focus', { bubbles: true }),
            new Event('click', { bubbles: true }),
            new InputEvent('input', {
                bubbles: true,
                cancelable: true,
                inputType: 'insertText',
                data: text,
            }),
            new Event('change', { bubbles: true }),
            new KeyboardEvent('keydown', { key: 'a', bubbles: true }),
            new KeyboardEvent('keyup', { key: 'a', bubbles: true })
        ];

        events.forEach(event => inputElement.dispatchEvent(event));

        // Дополнительно имитируем вставку текста
        const pasteEvent = new ClipboardEvent('paste', {
            bubbles: true,
            cancelable: true,
            clipboardData: new DataTransfer()
        });
        pasteEvent.clipboardData.setData('text/plain', text);
        inputElement.dispatchEvent(pasteEvent);
    }

    // Функция для поиска поля ввода
    function findChatInput() {
        const selectors = [
            '[data-a-target="chat-input"]',
            '[data-test-selector="chat-input"]',
            'textarea[data-a-target="chat-input"]',
            '.chat-input__textarea',
            'textarea[placeholder*="Отправить"]',
            'textarea[placeholder*="Send"]',
            '.chat-input textarea',
            '.tw-combo-input textarea',
            '.chat-room__content textarea',
            '.chat-input__textarea-container textarea',
            'textarea[autocomplete="off"]' // дополнительный селектор
        ];

        for (const selector of selectors) {
            const element = document.querySelector(selector);
            if (element && element.isConnected) return element;
        }

        // Поиск по всем textarea в области чата
        const chatSection = document.querySelector('.chat-room, .chat-room__content, [data-a-target="chat-pane"], .stream-chat');
        if (chatSection) {
            const textarea = chatSection.querySelector('textarea');
            if (textarea) return textarea;
        }

        return null;
    }



    // Функция для нажатия кнопки отправки
    function clickSendButton() {
        const selectors = [
            '[data-a-target="chat-send-button"]',
            'button[data-test-selector="chat-send-button"]',
            '.chat-input__buttons-container button[data-a-target="chat-send-button"]',
            '.chat-input__buttons-container button:last-child',
            '.tw-combo-input button',
            'button[aria-label="Отправить сообщение"]',
            'button[aria-label="Send message"]',
            'button[aria-label="Chat"]',
            '.chat-input__send-button',
            'button[type="submit"]' // дополнительный селектор
        ];

        for (const selector of selectors) {
            const button = document.querySelector(selector);
            if (button && !button.disabled && button.isConnected) {
                button.click();
                return true;
            }
        }

        // Поиск по иконке отправки (дополнительная проверка)
        const buttons = document.querySelectorAll('.chat-input__buttons-container button, .tw-combo-input button, button');
        for (const button of buttons) {
            const hasSendIcon = button.querySelector('svg, [data-a-target="chat-send-button"], .tw-icon');
            const hasSendText = button.textContent.includes('Отправить') || button.textContent.includes('Send') || button.textContent.includes('Chat');

            if ((hasSendIcon || hasSendText) && !button.disabled) {
                button.click();
                return true;
            }
        }

        return false;
    }

    // Функция получения случайной задержки (для имитации человека)
    function getRandomDelay(baseDelay) {
        // Добавляем случайную задержку ±25% от базовой + немного случайности
        const variation = baseDelay * 0.25;
        const randomAddition = Math.random() * variation * 2 - variation;
        return Math.max(400, Math.floor(baseDelay + randomAddition));
    }

    // Функция отправки одного сообщения
    function sendMessage(text) {
        return new Promise((resolve) => {
            // Ищем поле ввода чата
            const inputElement = findChatInput();

            if (!inputElement) {
                console.error('[Twitch Commands] Не удалось найти поле ввода чата');
                resolve(false);
                return;
            }

            // Проверяем, не заблокирован ли ввод
            if (inputElement.disabled || inputElement.readOnly) {
                console.warn('[Twitch Commands] Поле ввода заблокировано');
                resolve(false);
                return;
            }

            // Доказываем, что ты не андроид уже на практике
            simulateTyping(inputElement, text);



            // Задержка перед отправкой (чтобы Twitch успел обработать ввод)
            setTimeout(() => {
                const sent = clickSendButton();
                if (!sent) {
                    // Если мы кнопку не нашли, жмем большую красную кнопку ENTER
                    const enterEvents = [
                        new KeyboardEvent('keydown', {
                            key: 'Enter',
                            code: 'Enter',
                            keyCode: 13,
                            which: 13,
                            bubbles: true,
                            cancelable: true,
                            composed: true
                        }),
                        new KeyboardEvent('keypress', {
                            key: 'Enter',
                            code: 'Enter',
                            keyCode: 13,
                            which: 13,
                            bubbles: true,
                            cancelable: true,
                            composed: true
                        }),
                        new KeyboardEvent('keyup', {
                            key: 'Enter',
                            code: 'Enter',
                            keyCode: 13,
                            which: 13,
                            bubbles: true,
                            cancelable: true,
                            composed: true
                        })
                    ];

                    enterEvents.forEach(event => inputElement.dispatchEvent(event));

                    console.log(`[Twitch Commands] Отправлено через Enter: ${text}`);
                } else {
                    console.log(`[Twitch Commands] Отправлено: ${text}`);
                }
                resolve(true);
            }, 75); // Увеличена задержка перед отправкой
        });
    }

    // Основная функция для отправки всех команд
    async function sendAllCommands() {
        console.log(`[Twitch Commands] Отправка команд: ${COMMANDS.join(', ')}`);

        for (let i = 0; i < COMMANDS.length; i++) {
            const command = COMMANDS[i];

            // Небольшая случайная пауза перед каждой командой
            if (i > 0) {
                const preDelay = getRandomDelay(150);
                await new Promise(resolve => setTimeout(resolve, preDelay));
            }

            await sendMessage(command);

            // Задержка между сообщениями со случайной вариацией
            if (i < COMMANDS.length - 1) {
                const delay = getRandomDelay(BASE_DELAY);
                console.log(`[Twitch Commands] Ожидание ${delay}мс перед следующей командой...`);
                await new Promise(resolve => setTimeout(resolve, delay));
            }
        }

        console.log('[Twitch Commands] Все команды отправлены');
    }

    // Проверка, видим ли чат на экране
    function isChatVisible() {
        const chatElement = document.querySelector('.chat-room, .stream-chat, [data-a-target="chat-pane"]');
        if (!chatElement) return false;

        const rect = chatElement.getBoundingClientRect();
        return rect.width > 0 && rect.height > 0;
    }

    // Обработка нажатия клавиш
    function onKeyDown(event) {
        // Проверяем, что нажата нужная клавиша и нет модификаторов
        if ((event.key === BIND_KEY || event.code === BIND_KEY) && !event.ctrlKey && !event.altKey && !event.shiftKey && !event.metaKey) {
            // Проверяем, что ты не общаешься в чате сейчас
            if (isTyping()) {
                console.log('[Twitch Commands] Не могу отправить команды - ты находишься в поле чата!');
                return;
            }

            // Проверяем видимость чата
            if (!isChatVisible()) {
                console.log('[Twitch Commands] Чат не виден на экране! Открой чат полностью.');
                return;
            }

            event.preventDefault();
            event.stopPropagation();

            // Отправляем команды
            sendAllCommands();
        }
    }



    // Функция для проверки готовности чата
    function waitForChat() {
        return new Promise((resolve) => {
            const checkInterval = setInterval(() => {
                const input = findChatInput();
                const button = document.querySelector('[data-a-target="chat-send-button"]');
                const chatVisible = isChatVisible();

                if (input && button && chatVisible) {
                    clearInterval(checkInterval);
                    console.log('[Twitch Commands] Чат обнаружен и готов к работе! Les gooo');
                    console.log(`[Twitch Commands] Базовая задержка: ${BASE_DELAY}мс`);
                    resolve(true);
                }
            }, 500);

            // Таймаут на 30 секунд
            setTimeout(() => {
                clearInterval(checkInterval);
                console.warn('[Twitch Commands] Чат не найден за 30 секунд. Возможно, страница не загрузилась полностью.');
                resolve(false);
            }, 30000);
        });
    }

    // INIT
    async function init() {
        // Ждем загрузки чата
        await waitForChat();

        // Добавляем обработчик с высоким приоритетом
        document.addEventListener('keydown', onKeyDown, true);
        console.log(`[Twitch Commands] Скрипт загружен. Тыкай ${BIND_KEY} для отправки команд: ${COMMANDS.join(', ')}`);

        // Дополнительный обработчик для страницы (на случай iframe)
        window.addEventListener('keydown', onKeyDown, true);
    }

    // Ждем полной загрузки страницы!
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

})();