// ==UserScript==
// @name Deeper Tools
// @description Набор инструментов для Deeper: автоавторизация, белый список, сканер доменов.
// @author https://github.com/lReDragol
// @namespace http://tampermonkey.net/
// @version 3.0
// @icon https://avatars.mds.yandex.net/get-socsnippets/10235467/2a0000019509580bc84108597cea65bc46ee/square_83
// @match http://34.34.34.34/*
// @match *://*/*
// @license MIT
// @run-at document-start
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @grant GM_getValue
// @grant GM_setValue
// ==/UserScript==
(function() {
'use strict';
function getScannerEnabled() {
return GM_getValue('domainScannerEnabled', false);
}
function setScannerEnabled(val) {
GM_setValue('domainScannerEnabled', val);
updateScannerMenuCommand();
if (!val) {
const container = document.getElementById('domain-scanner-container');
if (container) container.remove();
} else {
ensureScannerContainer();
}
console.log('[Deeper Tools] Domain Scanner: ' + (val ? 'ON' : 'OFF'));
}
const nativeOpen = XMLHttpRequest.prototype.open;
const nativeSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function(method, url) {
this._method = method;
this._url = url;
if (getScannerEnabled()) {
try {
const urlObj = new URL(url);
addDomain(urlObj.hostname);
} catch(e) {}
}
return nativeOpen.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function(body) {
if (
this._url &&
this._url.includes('/api/admin/login') &&
this._method &&
this._method.toUpperCase() === 'POST'
) {
try {
const parsed = JSON.parse(body);
if (parsed && parsed.password) {
if (!localStorage.getItem('adminPassword')) {
localStorage.setItem('adminPassword', parsed.password);
console.log('[Deeper Tools] Пароль сохранён из XHR.');
}
}
} catch (err) {
console.error('[Deeper Tools] Ошибка парсинга XHR при авторизации:', err);
}
}
return nativeSend.apply(this, arguments);
};
if (window.location.href.includes('/login/')) {
const storedPassword = localStorage.getItem('adminPassword');
if (storedPassword) {
window.addEventListener('load', () => {
fetch('http://34.34.34.34/api/admin/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
"username": "admin",
"password": storedPassword
})
})
.then(response => {
if (response.status === 200) {
window.location.href = 'http://34.34.34.34/admin/dashboard';
}
return response.json();
})
.then(data => console.log('[Deeper Tools] Авторизация прошла успешно:', data))
.catch(error => console.error('[Deeper Tools] Ошибка при авторизации:', error));
});
} else {
console.log('[Deeper Tools] Пароль не найден. Выполните ручную авторизацию.');
}
}
if (window.location.href.startsWith('http://34.34.34.34/')) {
const iconButton = document.createElement('div');
iconButton.style.position = 'fixed';
iconButton.style.width = '25px';
iconButton.style.height = '25px';
iconButton.style.top = '10px';
iconButton.style.right = '10px';
iconButton.style.zIndex = '9999';
iconButton.style.backgroundColor = 'rgb(240, 240, 252)';
iconButton.style.borderRadius = '4px';
iconButton.style.boxShadow = '0 2px 5px rgba(0,0,0,0.3)';
iconButton.style.cursor = 'pointer';
iconButton.style.display = 'flex';
iconButton.style.alignItems = 'center';
iconButton.style.justifyContent = 'center';
const img = document.createElement('img');
img.src = 'https://avatars.mds.yandex.net/get-socsnippets/10235467/2a0000019509580bc84108597cea65bc46ee/square_83';
img.style.maxWidth = '80%';
img.style.maxHeight = '80%';
iconButton.appendChild(img);
const menuContainer = document.createElement('div');
menuContainer.style.position = 'fixed';
menuContainer.style.top = '45px';
menuContainer.style.right = '10px';
menuContainer.style.zIndex = '10000';
menuContainer.style.padding = '10px';
menuContainer.style.border = '1px solid #ccc';
menuContainer.style.borderRadius = '5px';
menuContainer.style.boxShadow = '0 2px 5px rgba(0,0,0,0.3)';
menuContainer.style.backgroundColor = '#fff';
menuContainer.style.display = 'none';
menuContainer.style.flexDirection = 'column';
function toggleMenu() {
menuContainer.style.display = (menuContainer.style.display === 'none') ? 'flex' : 'none';
}
iconButton.addEventListener('click', toggleMenu);
const buttonStyle = {
margin: '5px 0',
padding: '6px 10px',
backgroundColor: '#f8f8f8',
border: '1px solid #ccc',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '14px'
};
const downloadBtn = document.createElement('button');
downloadBtn.textContent = 'Скачать';
Object.assign(downloadBtn.style, buttonStyle);
const uploadBtn = document.createElement('button');
uploadBtn.textContent = 'Загрузить';
Object.assign(uploadBtn.style, buttonStyle);
const disableRebootBtn = document.createElement('button');
disableRebootBtn.textContent = 'Отключить перезагрузку';
Object.assign(disableRebootBtn.style, buttonStyle);
const forgetBtn = document.createElement('button');
forgetBtn.textContent = 'Забыть пароль';
Object.assign(forgetBtn.style, buttonStyle);
menuContainer.appendChild(downloadBtn);
menuContainer.appendChild(uploadBtn);
menuContainer.appendChild(disableRebootBtn);
menuContainer.appendChild(forgetBtn);
function ensureMenu() {
if (!document.body.contains(iconButton)) {
document.body.appendChild(iconButton);
}
if (!document.body.contains(menuContainer)) {
document.body.appendChild(menuContainer);
}
}
document.addEventListener('DOMContentLoaded', ensureMenu);
new MutationObserver(ensureMenu).observe(document.documentElement, { childList: true, subtree: true });
async function getExistingWhitelist() {
const pageSize = 100;
let pageNo = 1;
let total = 0;
let allItems = [];
let firstIteration = true;
do {
const url = `http://34.34.34.34/api/smartRoute/getRoutingWhitelist/domain?pageNo=${pageNo}&pageSize=${pageSize}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error('Ошибка при запросе списка на странице ' + pageNo);
}
const data = await response.json();
if (firstIteration) {
total = data.total;
firstIteration = false;
}
if (data.list && data.list.length > 0) {
allItems = allItems.concat(data.list);
}
pageNo++;
} while (allItems.length < total);
return allItems;
}
downloadBtn.addEventListener('click', async () => {
downloadBtn.disabled = true;
downloadBtn.textContent = 'Скачивание...';
try {
const allItems = await getExistingWhitelist();
const finalData = { total: allItems.length, list: allItems };
const blob = new Blob([JSON.stringify(finalData, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'data.json';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
} catch (error) {
console.error('[Deeper Tools] Ошибка при скачивании:', error);
alert('Ошибка при скачивании данных. Проверьте консоль.');
}
downloadBtn.textContent = 'Скачать';
downloadBtn.disabled = false;
});
uploadBtn.addEventListener('click', () => {
const input = document.createElement('input');
input.type = 'file';
input.accept = 'application/json';
input.style.display = 'none';
input.addEventListener('change', async function() {
if (input.files.length === 0) return;
const file = input.files[0];
const reader = new FileReader();
reader.onload = async function(e) {
try {
const jsonData = JSON.parse(e.target.result);
if (!jsonData.list || !Array.isArray(jsonData.list)) {
throw new Error('Неверный формат файла: ожидалось поле list[].');
}
const fileDomainNames = jsonData.list.map(item => item.domainName);
const existing = await getExistingWhitelist();
const existingDomainNames = existing.map(item => item.domainName);
const duplicates = fileDomainNames.filter(d => existingDomainNames.includes(d));
if (duplicates.length > 0) {
console.log('[Deeper Tools] Удаляем дубликаты:', duplicates);
const delRes = await fetch('http://34.34.34.34/api/smartRoute/deleteFromWhitelist/domain', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(duplicates)
});
if (!delRes.ok) {
console.error('[Deeper Tools] Ошибка при удалении дубликатов:', duplicates);
}
}
// Добавляем все из файла
for (let item of jsonData.list) {
const payload = { domainName: item.domainName, tunnelCode: item.tunnelCode };
const res = await fetch('http://34.34.34.34/api/smartRoute/addToWhitelist/domain', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!res.ok) {
console.error('[Deeper Tools] Ошибка добавления домена:', item.domainName);
}
}
alert('[Deeper Tools] Данные успешно загружены!');
} catch(err) {
console.error('[Deeper Tools] Ошибка загрузки:', err);
alert('Ошибка загрузки. Смотрите консоль.');
}
};
reader.readAsText(file);
});
document.body.appendChild(input);
input.click();
document.body.removeChild(input);
});
disableRebootBtn.addEventListener('click', async () => {
disableRebootBtn.disabled = true;
disableRebootBtn.textContent = 'Отключение...';
try {
const queryParams = '?on=false&hour=0&minute=0&day=0';
const response = await fetch(`http://34.34.34.34/api/autoReboot/config${queryParams}`, {
method: 'GET',
headers: { 'Content-Type': 'application/json' }
});
if (!response.ok) {
throw new Error('Ошибка при отключении перезагрузки');
}
alert('[Deeper Tools] Перезагрузка отключена!');
} catch (error) {
console.error('[Deeper Tools] Ошибка отключения перезагрузки:', error);
alert('Ошибка отключения перезагрузки. Смотрите консоль.');
}
disableRebootBtn.textContent = 'Отключить перезагрузку';
disableRebootBtn.disabled = false;
});
forgetBtn.addEventListener('click', () => {
if (confirm('Внимание! Логин и пароль будут очищены. Продолжить?')) {
localStorage.removeItem('adminPassword');
alert('[Deeper Tools] Пароль очищен. Авторизуйтесь вручную.');
}
});
}
const domainSet = new Set();
const originalFetch = window.fetch;
window.fetch = function(input, init) {
if (getScannerEnabled()) {
try {
const url = (typeof input === 'string') ? input : input.url;
const urlObj = new URL(url);
addDomain(urlObj.hostname);
} catch(e) {}
}
return originalFetch.apply(this, arguments);
};
const observer = new MutationObserver(mutations => {
if (!getScannerEnabled()) return;
mutations.forEach(m => {
if (m.addedNodes) {
m.addedNodes.forEach(node => {
if (node.tagName) {
const src = node.src || node.href;
if (src) {
try {
const urlObj = new URL(src);
addDomain(urlObj.hostname);
} catch(e) {}
}
}
});
}
});
});
observer.observe(document.documentElement, { childList: true, subtree: true });
setInterval(() => {
if (!getScannerEnabled()) return;
const entries = performance.getEntriesByType('resource');
entries.forEach(entry => {
try {
const urlObj = new URL(entry.name);
addDomain(urlObj.hostname);
} catch(e) {}
});
}, 1000);
function addDomain(domain) {
if (!domainSet.has(domain)) {
domainSet.add(domain);
const container = document.getElementById('domain-scanner-container');
if (container) {
const listEl = container.querySelector('#domain-list');
const sortedArr = Array.from(domainSet).sort();
listEl.textContent = sortedArr.join('\n');
}
}
}
function ensureScannerContainer() {
if (!getScannerEnabled()) return;
if (document.getElementById('domain-scanner-container')) return;
const container = document.createElement('div');
container.id = 'domain-scanner-container';
container.style.position = 'fixed';
container.style.top = '10px';
container.style.right = '10px';
container.style.width = '300px';
container.style.height = '400px';
container.style.overflowY = 'scroll';
container.style.backgroundColor = 'white';
container.style.border = '1px solid black';
container.style.zIndex = '10000';
container.style.padding = '10px';
container.style.fontSize = '12px';
container.style.fontFamily = 'monospace';
container.style.color = 'black';
container.style.whiteSpace = 'pre-wrap';
const domainList = document.createElement('div');
domainList.id = 'domain-list';
container.appendChild(domainList);
const buttonWrapper = document.createElement('div');
buttonWrapper.style.marginTop = '10px';
const addBtn = document.createElement('button');
addBtn.textContent = 'Добавить в deeper';
Object.assign(addBtn.style, {
padding: '6px 10px',
backgroundColor: '#f8f8f8',
border: '1px solid #ccc',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '14px'
});
addBtn.addEventListener('click', addToDeeper);
buttonWrapper.appendChild(addBtn);
container.appendChild(buttonWrapper);
document.body.appendChild(container);
}
async function addToDeeper() {
try {
const resp = await fetch('http://34.34.34.34/api/smartRoute/getRoutingWhitelist/domain?pageNo=1&pageSize=100');
if (!resp.ok) {
alert('[Deeper Tools] Ошибка при получении белого списка');
return;
}
const data = await resp.json();
const existingDomains = new Set();
const tunnelCodes = [];
if (Array.isArray(data.list)) {
data.list.forEach(item => {
if (item.domainName) existingDomains.add(item.domainName);
if (item.tunnelCode) tunnelCodes.push(item.tunnelCode);
});
}
if (tunnelCodes.length === 0) {
tunnelCodes.push('defaultCode');
}
const newItems = [];
domainSet.forEach(d => {
if (!existingDomains.has(d)) {
const randomIndex = Math.floor(Math.random() * tunnelCodes.length);
newItems.push({
domainName: d,
tunnelCode: tunnelCodes[randomIndex]
});
}
});
if (newItems.length === 0) {
alert('[Deeper Tools] Нет новых доменов для добавления.');
return;
}
for (let item of newItems) {
const r = await fetch('http://34.34.34.34/api/smartRoute/addToWhitelist/domain', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(item)
});
if (!r.ok) {
console.error('[Deeper Tools] Ошибка при добавлении домена:', item);
}
}
alert('[Deeper Tools] Новые домены добавлены в deeper!');
} catch (err) {
console.error('[Deeper Tools] Ошибка при добавлении в deeper:', err);
alert('Ошибка при добавлении. Смотрите консоль.');
}
}
let scannerMenuCommandId = null;
function updateScannerMenuCommand() {
if (scannerMenuCommandId && typeof GM_unregisterMenuCommand === 'function') {
GM_unregisterMenuCommand(scannerMenuCommandId);
}
if (typeof GM_registerMenuCommand === 'function') {
const currentState = getScannerEnabled();
const label = 'Domain Scanner: ' + (currentState ? '🟢' : '🔴');
scannerMenuCommandId = GM_registerMenuCommand(label, () => {
setScannerEnabled(!getScannerEnabled());
});
}
}
if (GM_getValue('domainScannerEnabled') === undefined) {
GM_setValue('domainScannerEnabled', false);
}
updateScannerMenuCommand();
if (getScannerEnabled()) {
if (document.readyState === 'complete' || document.readyState === 'interactive') {
ensureScannerContainer();
} else {
document.addEventListener('DOMContentLoaded', ensureScannerContainer);
}
}
})();