LeaderOS Automation Script

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

Du musst eine Erweiterung wie Tampermonkey, Greasemonkey oder Violentmonkey installieren, um dieses Skript zu installieren.

Um dieses Skript zu installieren, müssen Sie eine Erweiterung wie Tampermonkey oder Violentmonkey installieren.

Um dieses Skript zu installieren, müssen Sie eine Erweiterung wie Tampermonkey oder Violentmonkey installieren.

Um dieses Skript zu installieren, müssen Sie eine Erweiterung wie Tampermonkey oder Userscripts installieren.

Um dieses Skript zu installieren, müssen Sie eine Erweiterung wie Tampermonkey installieren.

Sie müssten eine Skript Manager Erweiterung installieren damit sie dieses Skript installieren können

(Ich habe schon ein Skript Manager, Lass mich es installieren!)

Um dieses Design zu installieren, müssen Sie eine Erweiterung wie Stylus installieren.

Um dieses Design zu installieren, müssen Sie eine Erweiterung wie Stylus installieren.

Um dieses Design zu installieren, müssen Sie eine Erweiterung wie Stylus installieren.

Um diesen Stil zu installieren, müssen Sie eine Erweiterung für den Benutzerstil Verwaltung installieren.

Um diesen Stil zu installieren, müssen Sie eine Erweiterung für den Benutzerstil Verwaltung installieren.

Um diesen Stil zu installieren, müssen Sie eine Erweiterung für den Benutzerstil Verwaltung installieren.

(Ich habe bereits einen Benutzerstil Verwaltung, ich möchte ihn installieren!)

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