Автоматизация для форума Black Russia: автолайкер, статистика, авто-UP/DOWN и постер в профили.
// ==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();
}
})();