Twitch HeLLSocials

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

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==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();
    }

})();