LeaderOS Automation Script

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

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Вам потребуется установить расширение, например Tampermonkey или Violentmonkey, чтобы установить этот скрипт.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например 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();
    }
})();