LeaderOS Automation Script

Автоматизация для форума Black Russia: автолайкер, статистика, авто-UP/DOWN и постер в профили.

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey, Greasemonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

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

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

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

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

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

(I already have a user script manager, let me install it!)

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.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         LeaderOS Automation Script
// @namespace    https://vk.com/club237051164
// @version      1.0.6
// @description  Автоматизация для форума Black Russia: автолайкер, статистика, авто-UP/DOWN и постер в профили.
// @author       Akzholch1k
// @match        https://forum.blackrussia.online/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=blackrussia.online
// @grant        none
// @license      MIT
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    // ==================== КОНФИГУРАЦИЯ ====================
    const CONFIG = {
        STORAGE_KEY: 'leaderos_stats_likes',
        STATE_KEY: 'leaderos_state_active',
        
        // Безопасные задержки (мс) для обхода флуд-контроля
        LIKE_DELAY_MIN: 2000,
        LIKE_DELAY_MAX: 4500,
        
        MESSAGES_PROFILE: [
            "привет, как дела",
            "🤩🤩",
            "💥💥"
        ],
        
        MAX_LOG_ENTRIES: 12
    };

    // ==================== СОСТОЯНИЕ ====================
    const state = {
        autoLikeActive: localStorage.getItem(CONFIG.STATE_KEY) === 'true', // берем сохраненное состояние напрямую
        isLoopRunning: false,
        likeCount: parseInt(localStorage.getItem(CONFIG.STORAGE_KEY) || '0'),
        sessionLikes: 0,
        sessionPosts: 0,
        logs: []
    };

    // ==================== УТИЛИТЫ ====================
    const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
    const randomDelay = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
    
    const log = (message, type = 'info') => {
        const time = new Date().toLocaleTimeString('ru-RU', {hour: '2-digit', minute: '2-digit', second: '2-digit'});
        state.logs.unshift({ time, message, type });
        if (state.logs.length > CONFIG.MAX_LOG_ENTRIES) state.logs.pop();
        updateLogPanel();
        console.log(`[LeaderOS] [${time}] [${type}] ${message}`);
    };

    const showNotification = (message, type = 'info') => {
        const notif = document.createElement('div');
        notif.className = `leaderos-notification leaderos-notification--${type}`;
        notif.textContent = message;
        document.body.appendChild(notif);
        
        setTimeout(() => notif.classList.add('show'), 10);
        setTimeout(() => {
            notif.classList.remove('show');
            setTimeout(() => notif.remove(), 300);
        }, 2500);
    };

    // 100% фикс детекта авторизации (работает через ядро XenForo + фоллбэк на селекторы для ПК/мобилок)
    const isAuthorized = () => {
        if (window.XF && window.XF.config && window.XF.config.userId) {
            return parseInt(window.XF.config.userId) > 0;
        }
        return !!document.querySelector('.p-navgroup--user, .p-nav-user, .p-account, .avatar, [data-xf-init="visitor-menu"]');
    };

    const isCaptchaPresent = () => {
        return !!document.querySelector('.h-captcha, .g-recaptcha, iframe[src*="captcha"], iframe[src*="recaptcha"]');
    };

    // ==================== СТИЛИ И ИНТЕРФЕЙС ====================
    const injectStyles = () => {
        if (document.getElementById('leaderos-styles')) return;
        const style = document.createElement('style');
        style.id = 'leaderos-styles';
        style.textContent = `
            #leaderos-panel {
                position: fixed;
                bottom: 15px;
                right: 15px;
                background: linear-gradient(135deg, rgba(15, 15, 15, 0.98), rgba(30, 30, 30, 0.98));
                border: 2px solid #e01111;
                border-radius: 12px;
                padding: 12px;
                color: #fff;
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
                font-size: 13px;
                z-index: 999999;
                box-shadow: 0 8px 25px rgba(224, 17, 17, 0.25);
                max-width: 280px;
                min-width: 240px;
                backdrop-filter: blur(8px);
                user-select: none;
            }
            #leaderos-panel h3 {
                margin: 0 0 8px 0; color: #e01111; font-size: 14px; text-align: center;
                text-transform: uppercase; letter-spacing: 1px; font-weight: bold;
            }
            .leaderos-stats { background: rgba(0, 0, 0, 0.4); border-radius: 6px; padding: 6px 10px; margin-bottom: 8px; }
            .leaderos-stat { margin-bottom: 4px; display: flex; justify-content: space-between; font-weight: 500; }
            .leaderos-stat:last-child { margin-bottom: 0; }
            .leaderos-stat-value { color: #e01111; font-weight: bold; }
            .leaderos-btn {
                display: block; width: 100%; background: linear-gradient(135deg, #e01111, #b00e0e);
                color: white; border: none; padding: 7px; margin: 4px 0; border-radius: 6px;
                cursor: pointer; font-weight: bold; text-align: center; font-size: 11px;
                text-transform: uppercase; letter-spacing: 0.5px; transition: all 0.2s;
            }
            .leaderos-btn:hover:not(:disabled) { background: linear-gradient(135deg, #ff2222, #d01111); }
            .leaderos-btn:disabled { opacity: 0.5; cursor: not-allowed; }
            .leaderos-btn.active {
                background: linear-gradient(135deg, #28a745, #1e7e34);
                box-shadow: 0 0 10px rgba(40, 167, 69, 0.4);
            }
            .leaderos-log {
                background: rgba(0, 0, 0, 0.5); border-radius: 6px; padding: 6px;
                margin-top: 8px; max-height: 90px; overflow-y: auto; font-size: 10px;
                border: 1px solid rgba(224, 17, 17, 0.15); font-family: monospace;
            }
            .leaderos-log::-webkit-scrollbar { width: 3px; }
            .leaderos-log::-webkit-scrollbar-thumb { background: #e01111; border-radius: 2px; }
            .leaderos-log-entry { padding: 1px 0; border-bottom: 1px solid rgba(255,255,255,0.03); }
            .leaderos-log-time { color: #888; margin-right: 4px; }
            .leaderos-log--info { color: #fff; }
            .leaderos-log--success { color: #28a745; }
            .leaderos-log--error { color: #dc3545; }
            .leaderos-log--warning { color: #ffc107; }
            .leaderos-notification {
                position: fixed; top: 15px; right: 15px; background: rgba(15, 15, 15, 0.95);
                border-left: 4px solid #e01111; border-radius: 6px; padding: 10px 15px;
                color: white; font-family: sans-serif; font-size: 13px; z-index: 9999999;
                transform: translateX(350px); transition: transform 0.3s ease; box-shadow: 0 4px 15px rgba(0,0,0,0.4);
            }
            .leaderos-notification.show { transform: translateX(0); }
            .leaderos-notification--success { border-left-color: #28a745; }
            .leaderos-notification--error { border-left-color: #dc3545; }
            .leaderos-notification--warning { border-left-color: #ffc107; }
            @media (max-width: 768px) {
                #leaderos-panel { bottom: 8px; right: 8px; max-width: 190px; min-width: 170px; padding: 8px; font-size: 11px; }
                #leaderos-panel h3 { font-size: 11px; }
                .leaderos-btn { padding: 5px; font-size: 9px; }
                .leaderos-log { max-height: 65px; font-size: 8px; }
            }
        `;
        document.head.appendChild(style);
    };

    const createPanel = () => {
        injectStyles();
        if (document.getElementById('leaderos-panel')) return;

        const panel = document.createElement('div');
        panel.id = 'leaderos-panel';
        panel.innerHTML = `
            <h3>⚡ LeaderOS v11.0.6</h3>
            <div class="leaderos-stats">
                <div class="leaderos-stat"><span>Всего лайков:</span><span class="leaderos-stat-value" id="like-total">${state.likeCount}</span></div>
                <div class="leaderos-stat"><span>За сессию:</span><span class="leaderos-stat-value" id="like-session">${state.sessionLikes}</span></div>
                <div class="leaderos-stat"><span>Сообщения:</span><span class="leaderos-stat-value" id="post-count">${state.sessionPosts}</span></div>
            </div>
            <button id="btn-autolike" class="leaderos-btn">Автолайкер: Загрузка</button>
            <button id="btn-autoup" class="leaderos-btn" style="display:none;">⬆️ Отправить UP</button>
            <button id="btn-autodown" class="leaderos-btn" style="display:none;">⬇️ Отправить DOWN</button>
            <button id="btn-profile-spam" class="leaderos-btn" style="display:none;">💬 Написать приветствие</button>
            <div class="leaderos-log" id="leaderos-log"></div>
        `;
        document.body.appendChild(panel);
        updateButtonUI();
    };

    const updateButtonUI = () => {
        const btn = document.getElementById('btn-autolike');
        if (!btn) return;
        if (state.autoLikeActive) {
            btn.textContent = '🔄 Автолайкер: ВКЛ';
            btn.classList.add('active');
        } else {
            btn.textContent = '🔄 Автолайкер: ВЫКЛ';
            btn.classList.remove('active');
        }
    };

    const updateLogPanel = () => {
        const logEl = document.getElementById('leaderos-log');
        if (!logEl) return;
        logEl.innerHTML = state.logs.map(e => `
            <div class="leaderos-log-entry leaderos-log--${e.type}">
                <span class="leaderos-log-time">${e.time}</span>${e.message}
            </div>
        `).join('');
    };

    const updateStatsUI = () => {
        const totalEl = document.getElementById('like-total');
        const sessionEl = document.getElementById('like-session');
        const postEl = document.getElementById('post-count');
        if (totalEl) totalEl.textContent = state.likeCount;
        if (sessionEl) sessionEl.textContent = state.sessionLikes;
        if (postEl) postEl.textContent = state.sessionPosts;
    };

    // ==================== ДВИЖОК АВТОЛАЙКЕРА ====================
    const findLikeButtons = () => {
        const selectors = [
            'a.reaction:not(.reaction--reacted)',
            'a.reaction-text:not(.reaction--reacted)',
            '[data-xf-click="reaction"]:not(.reaction--reacted)'
        ];
        for (const selector of selectors) {
            const buttons = document.querySelectorAll(selector);
            if (buttons.length > 0) return Array.from(buttons);
        }
        return [];
    };

    const startLikeEngine = async () => {
        if (state.isLoopRunning) return; // Защита от дублирования циклов
        state.isLoopRunning = true;

        log('🤖 Движок автолайков активен', 'info');

        while (state.autoLikeActive) {
            // Проверка валидности страницы для лайков
            if (!window.location.href.includes('/whats-new/posts/') && 
                !window.location.href.includes('/forums/') && 
                !window.location.href.includes('/threads/')) {
                break; 
            }

            if (!isAuthorized()) {
                log('❌ Ошибка: Вы не авторизованы', 'error');
                state.autoLikeActive = false;
                localStorage.setItem(CONFIG.STATE_KEY, 'false');
                updateButtonUI();
                break;
            }

            if (isCaptchaPresent()) {
                log('⚠️ Найдена капча! Стоп.', 'warning');
                showNotification('Решите капчу вручную!', 'warning');
                state.autoLikeActive = false;
                localStorage.setItem(CONFIG.STATE_KEY, 'false');
                updateButtonUI();
                break;
            }

            const buttons = findLikeButtons();

            if (buttons.length === 0) {
                // Если лайкать нечего — плавно листаем вниз для автоподгрузки XenForo
                window.scrollBy({ top: 350, behavior: 'smooth' });
                await sleep(2000);
                continue;
            }

            // Берем самую верхнюю доступную кнопку
            const target = buttons[0];
            target.scrollIntoView({ behavior: 'smooth', block: 'center' });
            
            try {
                target.click();
                state.likeCount++;
                state.sessionLikes++;
                localStorage.setItem(CONFIG.STORAGE_KEY, state.likeCount.toString());
                updateStatsUI();
                log(`❤️ Лайк отправлен! (Сессия: ${state.sessionLikes})`, 'success');
            } catch (e) {
                log('❌ Сбой при клике по реакции', 'error');
            }

            // Рандомный анти-чит интервал
            await sleep(randomDelay(CONFIG.LIKE_DELAY_MIN, CONFIG.LIKE_DELAY_MAX));
        }

        state.isLoopRunning = false;
        log('⏹️ Движок автолайков остановлен', 'info');
    };

    // ==================== МОДУЛЬ: АВТООТВЕТЫ (UP/DOWN) ====================
    const handleUpDownModule = () => {
        const isTargetPage = window.location.href.includes('.656/') || window.location.href.includes('/threads/');
        const upBtn = document.getElementById('btn-autoup');
        const downBtn = document.getElementById('btn-autodown');
        
        if (!upBtn || !downBtn) return;

        if (!isTargetPage) {
            upBtn.style.display = 'none';
            downBtn.style.display = 'none';
            return;
        }

        upBtn.style.display = 'block';
        downBtn.style.display = 'block';
    };

    const sendComment = async (text) => {
        try {
            if (!isAuthorized()) return showNotification('Вы не авторизованы на форуме!', 'error');
            
            const editor = document.querySelector('.fr-element.fr-view') || document.querySelector('textarea[name="message"]');
            if (!editor) throw new Error('Форма ответа не найдена. Прокрутите страницу вниз.');

            if (editor.tagName === 'TEXTAREA') {
                editor.value = text;
                editor.dispatchEvent(new Event('input', { bubbles: true }));
            } else {
                editor.focus();
                editor.innerHTML = `<p>${text}</p>`;
                editor.dispatchEvent(new Event('input', { bubbles: true }));
            }

            await sleep(400);

            const submitBtn = document.querySelector('.button--icon--reply, .button--primary[type="submit"], .js-quickReply .button--primary');
            if (!submitBtn) throw new Error('Кнопка отправки не найдена.');

            submitBtn.click();
            state.sessionPosts++;
            updateStatsUI();
            log(`✉️ Добавлено сообщение: ${text}`, 'success');
            showNotification(`Отправлено: ${text}`, 'success');
        } catch (err) {
            log(`❌ Ошибка ответа: ${err.message}`, 'error');
            showNotification(err.message, 'error');
        }
    };

    // ==================== МОДУЛЬ: СТЕНЫ ПРОФИЛЕЙ ====================
    const handleProfileModule = () => {
        const spamBtn = document.getElementById('btn-profile-spam');
        if (!spamBtn) return;
        
        if (window.location.href.includes('/members/')) {
            spamBtn.style.display = 'block';
        } else {
            spamBtn.style.display = 'none';
        }
    };

    const sendProfileSpam = async () => {
        try {
            if (!isAuthorized()) return showNotification('Вы не авторизованы!', 'error');

            const input = document.querySelector('textarea[name="message"]') || document.querySelector('.js-profileComment textarea');
            const submit = document.querySelector('.js-quickReply .button--primary') || document.querySelector('.profileComment button[type="submit"]');

            if (!input || !submit) throw new Error('Поле ответа на стене не обнаружено.');

            const randomMsg = CONFIG.MESSAGES_PROFILE[Math.floor(Math.random() * CONFIG.MESSAGES_PROFILE.length)];
            
            input.focus();
            input.value = '';
            
            // Имитируем человеческий ввод побуквенно
            for (const char of randomMsg) {
                input.value += char;
                input.dispatchEvent(new Event('input', { bubbles: true }));
                await sleep(randomDelay(40, 110));
            }

            await sleep(500);
            submit.click();

            state.sessionPosts++;
            updateStatsUI();
            log(`💬 На стену улетело: "${randomMsg}"`, 'success');
            showNotification('Приветствие оставлено!', 'success');
        } catch (e) {
            log(`❌ Ошибка стены: ${e.message}`, 'error');
            showNotification(e.message, 'error');
        }
    };

    // ==================== ИНИЦИАЛИЗАЦИЯ И ОБРАБОТКА AJAX ====================
    const setupEvents = () => {
        // Переключатель рубильника автолайкера
        document.getElementById('btn-autolike').addEventListener('click', () => {
            state.autoLikeActive = !state.autoLikeActive;
            localStorage.setItem(CONFIG.STATE_KEY, state.autoLikeActive.toString());
            updateButtonUI();
            
            if (state.autoLikeActive) {
                showNotification('Автолайкер полностью запущен!', 'success');
                startLikeEngine();
            } else {
                log('⏸️ Локальный стоп триггернут пользователем', 'warning');
            }
        });

        // Кнопки UP / DOWN
        document.getElementById('btn-autoup').addEventListener('click', () => sendComment('UP'));
        document.getElementById('btn-autodown').addEventListener('click', () => sendComment('DOWN'));

        // Кнопка флуда в профиль
        document.getElementById('btn-profile-spam').addEventListener('click', sendProfileSpam);
    };

    const syncModulesWithCurrentURL = () => {
        handleUpDownModule();
        handleProfileModule();
        
        // Полная автоматизация: если переключатель активен — запускаем цикл без кликов по UI
        if (state.autoLikeActive) {
            startLikeEngine();
        }
    };

    const init = () => {
        if (!window.location.hostname.includes('blackrussia.online')) return;

        createPanel();
        setupEvents();
        
        log('✅ LeaderOS успешно интегрирован', 'success');
        syncModulesWithCurrentURL();

        // Следим за бесшовными AJAX переходами XenForo без перезагрузки страниц
        let currentUrl = location.href;
        const navObserver = new MutationObserver(() => {
            if (location.href !== currentUrl) {
                currentUrl = location.href;
                log(`跳转 Динамический переход: ${currentUrl}`, 'info');
                setTimeout(syncModulesWithCurrentURL, 1200); // даем движку форума отрендерить новый DOM
            }
        });
        navObserver.observe(document.body, { childList: true, subtree: true });
    };

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();