Greasy Fork is available in English.
Отправляет команды !дис, !тг, !ютуб в чат Twitch по нажатию горячей клавиши!
// ==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();
}
})();