Twitch HeLLSocials

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

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

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

})();