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와 같은 확장 프로그램이 필요합니다.

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

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

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

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

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

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

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

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

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

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

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

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