Deeper Tools

Набор инструментов для Deeper: автоавторизация, белый список, сканер доменов.

目前为 2025-04-14 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Deeper Tools
  3. // @description Набор инструментов для Deeper: автоавторизация, белый список, сканер доменов.
  4. // @author https://github.com/lReDragol
  5. // @namespace http://tampermonkey.net/
  6. // @version 3.0
  7. // @icon https://avatars.mds.yandex.net/get-socsnippets/10235467/2a0000019509580bc84108597cea65bc46ee/square_83
  8. // @match http://34.34.34.34/*
  9. // @match *://*/*
  10. // @license MIT
  11. // @run-at document-start
  12. // @grant GM_registerMenuCommand
  13. // @grant GM_unregisterMenuCommand
  14. // @grant GM_getValue
  15. // @grant GM_setValue
  16. // ==/UserScript==
  17.  
  18. (function() {
  19. 'use strict';
  20.  
  21. function getScannerEnabled() {
  22. return GM_getValue('domainScannerEnabled', false);
  23. }
  24. function setScannerEnabled(val) {
  25. GM_setValue('domainScannerEnabled', val);
  26. updateScannerMenuCommand();
  27. if (!val) {
  28. const container = document.getElementById('domain-scanner-container');
  29. if (container) container.remove();
  30. } else {
  31. ensureScannerContainer();
  32. }
  33. console.log('[Deeper Tools] Domain Scanner: ' + (val ? 'ON' : 'OFF'));
  34. }
  35.  
  36.  
  37. const nativeOpen = XMLHttpRequest.prototype.open;
  38. const nativeSend = XMLHttpRequest.prototype.send;
  39.  
  40. XMLHttpRequest.prototype.open = function(method, url) {
  41. this._method = method;
  42. this._url = url;
  43. if (getScannerEnabled()) {
  44. try {
  45. const urlObj = new URL(url);
  46. addDomain(urlObj.hostname);
  47. } catch(e) {}
  48. }
  49. return nativeOpen.apply(this, arguments);
  50. };
  51.  
  52. XMLHttpRequest.prototype.send = function(body) {
  53. if (
  54. this._url &&
  55. this._url.includes('/api/admin/login') &&
  56. this._method &&
  57. this._method.toUpperCase() === 'POST'
  58. ) {
  59. try {
  60. const parsed = JSON.parse(body);
  61. if (parsed && parsed.password) {
  62. if (!localStorage.getItem('adminPassword')) {
  63. localStorage.setItem('adminPassword', parsed.password);
  64. console.log('[Deeper Tools] Пароль сохранён из XHR.');
  65. }
  66. }
  67. } catch (err) {
  68. console.error('[Deeper Tools] Ошибка парсинга XHR при авторизации:', err);
  69. }
  70. }
  71. return nativeSend.apply(this, arguments);
  72. };
  73.  
  74. if (window.location.href.includes('/login/')) {
  75. const storedPassword = localStorage.getItem('adminPassword');
  76. if (storedPassword) {
  77. window.addEventListener('load', () => {
  78. fetch('http://34.34.34.34/api/admin/login', {
  79. method: 'POST',
  80. headers: { 'Content-Type': 'application/json' },
  81. body: JSON.stringify({
  82. "username": "admin",
  83. "password": storedPassword
  84. })
  85. })
  86. .then(response => {
  87. if (response.status === 200) {
  88. window.location.href = 'http://34.34.34.34/admin/dashboard';
  89. }
  90. return response.json();
  91. })
  92. .then(data => console.log('[Deeper Tools] Авторизация прошла успешно:', data))
  93. .catch(error => console.error('[Deeper Tools] Ошибка при авторизации:', error));
  94. });
  95. } else {
  96. console.log('[Deeper Tools] Пароль не найден. Выполните ручную авторизацию.');
  97. }
  98. }
  99.  
  100.  
  101. if (window.location.href.startsWith('http://34.34.34.34/')) {
  102. const iconButton = document.createElement('div');
  103. iconButton.style.position = 'fixed';
  104. iconButton.style.width = '25px';
  105. iconButton.style.height = '25px';
  106. iconButton.style.top = '10px';
  107. iconButton.style.right = '10px';
  108. iconButton.style.zIndex = '9999';
  109. iconButton.style.backgroundColor = 'rgb(240, 240, 252)';
  110. iconButton.style.borderRadius = '4px';
  111. iconButton.style.boxShadow = '0 2px 5px rgba(0,0,0,0.3)';
  112. iconButton.style.cursor = 'pointer';
  113. iconButton.style.display = 'flex';
  114. iconButton.style.alignItems = 'center';
  115. iconButton.style.justifyContent = 'center';
  116.  
  117. const img = document.createElement('img');
  118. img.src = 'https://avatars.mds.yandex.net/get-socsnippets/10235467/2a0000019509580bc84108597cea65bc46ee/square_83';
  119. img.style.maxWidth = '80%';
  120. img.style.maxHeight = '80%';
  121. iconButton.appendChild(img);
  122.  
  123. const menuContainer = document.createElement('div');
  124. menuContainer.style.position = 'fixed';
  125. menuContainer.style.top = '45px';
  126. menuContainer.style.right = '10px';
  127. menuContainer.style.zIndex = '10000';
  128. menuContainer.style.padding = '10px';
  129. menuContainer.style.border = '1px solid #ccc';
  130. menuContainer.style.borderRadius = '5px';
  131. menuContainer.style.boxShadow = '0 2px 5px rgba(0,0,0,0.3)';
  132. menuContainer.style.backgroundColor = '#fff';
  133. menuContainer.style.display = 'none';
  134. menuContainer.style.flexDirection = 'column';
  135.  
  136. function toggleMenu() {
  137. menuContainer.style.display = (menuContainer.style.display === 'none') ? 'flex' : 'none';
  138. }
  139. iconButton.addEventListener('click', toggleMenu);
  140.  
  141. const buttonStyle = {
  142. margin: '5px 0',
  143. padding: '6px 10px',
  144. backgroundColor: '#f8f8f8',
  145. border: '1px solid #ccc',
  146. borderRadius: '4px',
  147. cursor: 'pointer',
  148. fontSize: '14px'
  149. };
  150.  
  151. const downloadBtn = document.createElement('button');
  152. downloadBtn.textContent = 'Скачать';
  153. Object.assign(downloadBtn.style, buttonStyle);
  154.  
  155. const uploadBtn = document.createElement('button');
  156. uploadBtn.textContent = 'Загрузить';
  157. Object.assign(uploadBtn.style, buttonStyle);
  158.  
  159. const disableRebootBtn = document.createElement('button');
  160. disableRebootBtn.textContent = 'Отключить перезагрузку';
  161. Object.assign(disableRebootBtn.style, buttonStyle);
  162.  
  163. const forgetBtn = document.createElement('button');
  164. forgetBtn.textContent = 'Забыть пароль';
  165. Object.assign(forgetBtn.style, buttonStyle);
  166.  
  167. menuContainer.appendChild(downloadBtn);
  168. menuContainer.appendChild(uploadBtn);
  169. menuContainer.appendChild(disableRebootBtn);
  170. menuContainer.appendChild(forgetBtn);
  171.  
  172. function ensureMenu() {
  173. if (!document.body.contains(iconButton)) {
  174. document.body.appendChild(iconButton);
  175. }
  176. if (!document.body.contains(menuContainer)) {
  177. document.body.appendChild(menuContainer);
  178. }
  179. }
  180. document.addEventListener('DOMContentLoaded', ensureMenu);
  181. new MutationObserver(ensureMenu).observe(document.documentElement, { childList: true, subtree: true });
  182.  
  183. async function getExistingWhitelist() {
  184. const pageSize = 100;
  185. let pageNo = 1;
  186. let total = 0;
  187. let allItems = [];
  188. let firstIteration = true;
  189. do {
  190. const url = `http://34.34.34.34/api/smartRoute/getRoutingWhitelist/domain?pageNo=${pageNo}&pageSize=${pageSize}`;
  191. const response = await fetch(url);
  192. if (!response.ok) {
  193. throw new Error('Ошибка при запросе списка на странице ' + pageNo);
  194. }
  195. const data = await response.json();
  196. if (firstIteration) {
  197. total = data.total;
  198. firstIteration = false;
  199. }
  200. if (data.list && data.list.length > 0) {
  201. allItems = allItems.concat(data.list);
  202. }
  203. pageNo++;
  204. } while (allItems.length < total);
  205. return allItems;
  206. }
  207.  
  208. downloadBtn.addEventListener('click', async () => {
  209. downloadBtn.disabled = true;
  210. downloadBtn.textContent = 'Скачивание...';
  211. try {
  212. const allItems = await getExistingWhitelist();
  213. const finalData = { total: allItems.length, list: allItems };
  214. const blob = new Blob([JSON.stringify(finalData, null, 2)], { type: 'application/json' });
  215. const url = URL.createObjectURL(blob);
  216. const a = document.createElement('a');
  217. a.href = url;
  218. a.download = 'data.json';
  219. document.body.appendChild(a);
  220. a.click();
  221. document.body.removeChild(a);
  222. URL.revokeObjectURL(url);
  223. } catch (error) {
  224. console.error('[Deeper Tools] Ошибка при скачивании:', error);
  225. alert('Ошибка при скачивании данных. Проверьте консоль.');
  226. }
  227. downloadBtn.textContent = 'Скачать';
  228. downloadBtn.disabled = false;
  229. });
  230.  
  231. uploadBtn.addEventListener('click', () => {
  232. const input = document.createElement('input');
  233. input.type = 'file';
  234. input.accept = 'application/json';
  235. input.style.display = 'none';
  236. input.addEventListener('change', async function() {
  237. if (input.files.length === 0) return;
  238. const file = input.files[0];
  239. const reader = new FileReader();
  240. reader.onload = async function(e) {
  241. try {
  242. const jsonData = JSON.parse(e.target.result);
  243. if (!jsonData.list || !Array.isArray(jsonData.list)) {
  244. throw new Error('Неверный формат файла: ожидалось поле list[].');
  245. }
  246. const fileDomainNames = jsonData.list.map(item => item.domainName);
  247. const existing = await getExistingWhitelist();
  248. const existingDomainNames = existing.map(item => item.domainName);
  249. const duplicates = fileDomainNames.filter(d => existingDomainNames.includes(d));
  250. if (duplicates.length > 0) {
  251. console.log('[Deeper Tools] Удаляем дубликаты:', duplicates);
  252. const delRes = await fetch('http://34.34.34.34/api/smartRoute/deleteFromWhitelist/domain', {
  253. method: 'POST',
  254. headers: { 'Content-Type': 'application/json' },
  255. body: JSON.stringify(duplicates)
  256. });
  257. if (!delRes.ok) {
  258. console.error('[Deeper Tools] Ошибка при удалении дубликатов:', duplicates);
  259. }
  260. }
  261. // Добавляем все из файла
  262. for (let item of jsonData.list) {
  263. const payload = { domainName: item.domainName, tunnelCode: item.tunnelCode };
  264. const res = await fetch('http://34.34.34.34/api/smartRoute/addToWhitelist/domain', {
  265. method: 'POST',
  266. headers: { 'Content-Type': 'application/json' },
  267. body: JSON.stringify(payload)
  268. });
  269. if (!res.ok) {
  270. console.error('[Deeper Tools] Ошибка добавления домена:', item.domainName);
  271. }
  272. }
  273. alert('[Deeper Tools] Данные успешно загружены!');
  274. } catch(err) {
  275. console.error('[Deeper Tools] Ошибка загрузки:', err);
  276. alert('Ошибка загрузки. Смотрите консоль.');
  277. }
  278. };
  279. reader.readAsText(file);
  280. });
  281. document.body.appendChild(input);
  282. input.click();
  283. document.body.removeChild(input);
  284. });
  285.  
  286. disableRebootBtn.addEventListener('click', async () => {
  287. disableRebootBtn.disabled = true;
  288. disableRebootBtn.textContent = 'Отключение...';
  289. try {
  290. const queryParams = '?on=false&hour=0&minute=0&day=0';
  291. const response = await fetch(`http://34.34.34.34/api/autoReboot/config${queryParams}`, {
  292. method: 'GET',
  293. headers: { 'Content-Type': 'application/json' }
  294. });
  295. if (!response.ok) {
  296. throw new Error('Ошибка при отключении перезагрузки');
  297. }
  298. alert('[Deeper Tools] Перезагрузка отключена!');
  299. } catch (error) {
  300. console.error('[Deeper Tools] Ошибка отключения перезагрузки:', error);
  301. alert('Ошибка отключения перезагрузки. Смотрите консоль.');
  302. }
  303. disableRebootBtn.textContent = 'Отключить перезагрузку';
  304. disableRebootBtn.disabled = false;
  305. });
  306.  
  307. forgetBtn.addEventListener('click', () => {
  308. if (confirm('Внимание! Логин и пароль будут очищены. Продолжить?')) {
  309. localStorage.removeItem('adminPassword');
  310. alert('[Deeper Tools] Пароль очищен. Авторизуйтесь вручную.');
  311. }
  312. });
  313. }
  314.  
  315.  
  316. const domainSet = new Set();
  317. const originalFetch = window.fetch;
  318. window.fetch = function(input, init) {
  319. if (getScannerEnabled()) {
  320. try {
  321. const url = (typeof input === 'string') ? input : input.url;
  322. const urlObj = new URL(url);
  323. addDomain(urlObj.hostname);
  324. } catch(e) {}
  325. }
  326. return originalFetch.apply(this, arguments);
  327. };
  328.  
  329. const observer = new MutationObserver(mutations => {
  330. if (!getScannerEnabled()) return;
  331. mutations.forEach(m => {
  332. if (m.addedNodes) {
  333. m.addedNodes.forEach(node => {
  334. if (node.tagName) {
  335. const src = node.src || node.href;
  336. if (src) {
  337. try {
  338. const urlObj = new URL(src);
  339. addDomain(urlObj.hostname);
  340. } catch(e) {}
  341. }
  342. }
  343. });
  344. }
  345. });
  346. });
  347. observer.observe(document.documentElement, { childList: true, subtree: true });
  348.  
  349. setInterval(() => {
  350. if (!getScannerEnabled()) return;
  351. const entries = performance.getEntriesByType('resource');
  352. entries.forEach(entry => {
  353. try {
  354. const urlObj = new URL(entry.name);
  355. addDomain(urlObj.hostname);
  356. } catch(e) {}
  357. });
  358. }, 1000);
  359.  
  360. function addDomain(domain) {
  361. if (!domainSet.has(domain)) {
  362. domainSet.add(domain);
  363. const container = document.getElementById('domain-scanner-container');
  364. if (container) {
  365. const listEl = container.querySelector('#domain-list');
  366. const sortedArr = Array.from(domainSet).sort();
  367. listEl.textContent = sortedArr.join('\n');
  368. }
  369. }
  370. }
  371.  
  372. function ensureScannerContainer() {
  373. if (!getScannerEnabled()) return;
  374.  
  375. if (document.getElementById('domain-scanner-container')) return;
  376.  
  377. const container = document.createElement('div');
  378. container.id = 'domain-scanner-container';
  379. container.style.position = 'fixed';
  380. container.style.top = '10px';
  381. container.style.right = '10px';
  382. container.style.width = '300px';
  383. container.style.height = '400px';
  384. container.style.overflowY = 'scroll';
  385. container.style.backgroundColor = 'white';
  386. container.style.border = '1px solid black';
  387. container.style.zIndex = '10000';
  388. container.style.padding = '10px';
  389. container.style.fontSize = '12px';
  390. container.style.fontFamily = 'monospace';
  391. container.style.color = 'black';
  392. container.style.whiteSpace = 'pre-wrap';
  393.  
  394. const domainList = document.createElement('div');
  395. domainList.id = 'domain-list';
  396. container.appendChild(domainList);
  397.  
  398. const buttonWrapper = document.createElement('div');
  399. buttonWrapper.style.marginTop = '10px';
  400.  
  401. const addBtn = document.createElement('button');
  402. addBtn.textContent = 'Добавить в deeper';
  403. Object.assign(addBtn.style, {
  404. padding: '6px 10px',
  405. backgroundColor: '#f8f8f8',
  406. border: '1px solid #ccc',
  407. borderRadius: '4px',
  408. cursor: 'pointer',
  409. fontSize: '14px'
  410. });
  411. addBtn.addEventListener('click', addToDeeper);
  412.  
  413. buttonWrapper.appendChild(addBtn);
  414. container.appendChild(buttonWrapper);
  415. document.body.appendChild(container);
  416. }
  417.  
  418. async function addToDeeper() {
  419. try {
  420. const resp = await fetch('http://34.34.34.34/api/smartRoute/getRoutingWhitelist/domain?pageNo=1&pageSize=100');
  421. if (!resp.ok) {
  422. alert('[Deeper Tools] Ошибка при получении белого списка');
  423. return;
  424. }
  425. const data = await resp.json();
  426. const existingDomains = new Set();
  427. const tunnelCodes = [];
  428. if (Array.isArray(data.list)) {
  429. data.list.forEach(item => {
  430. if (item.domainName) existingDomains.add(item.domainName);
  431. if (item.tunnelCode) tunnelCodes.push(item.tunnelCode);
  432. });
  433. }
  434. if (tunnelCodes.length === 0) {
  435. tunnelCodes.push('defaultCode');
  436. }
  437.  
  438. const newItems = [];
  439. domainSet.forEach(d => {
  440. if (!existingDomains.has(d)) {
  441. const randomIndex = Math.floor(Math.random() * tunnelCodes.length);
  442. newItems.push({
  443. domainName: d,
  444. tunnelCode: tunnelCodes[randomIndex]
  445. });
  446. }
  447. });
  448.  
  449. if (newItems.length === 0) {
  450. alert('[Deeper Tools] Нет новых доменов для добавления.');
  451. return;
  452. }
  453.  
  454. for (let item of newItems) {
  455. const r = await fetch('http://34.34.34.34/api/smartRoute/addToWhitelist/domain', {
  456. method: 'POST',
  457. headers: {'Content-Type': 'application/json'},
  458. body: JSON.stringify(item)
  459. });
  460. if (!r.ok) {
  461. console.error('[Deeper Tools] Ошибка при добавлении домена:', item);
  462. }
  463. }
  464. alert('[Deeper Tools] Новые домены добавлены в deeper!');
  465. } catch (err) {
  466. console.error('[Deeper Tools] Ошибка при добавлении в deeper:', err);
  467. alert('Ошибка при добавлении. Смотрите консоль.');
  468. }
  469. }
  470.  
  471.  
  472. let scannerMenuCommandId = null;
  473.  
  474. function updateScannerMenuCommand() {
  475. if (scannerMenuCommandId && typeof GM_unregisterMenuCommand === 'function') {
  476. GM_unregisterMenuCommand(scannerMenuCommandId);
  477. }
  478. if (typeof GM_registerMenuCommand === 'function') {
  479. const currentState = getScannerEnabled();
  480. const label = 'Domain Scanner: ' + (currentState ? '🟢' : '🔴');
  481. scannerMenuCommandId = GM_registerMenuCommand(label, () => {
  482. setScannerEnabled(!getScannerEnabled());
  483. });
  484. }
  485. }
  486.  
  487. if (GM_getValue('domainScannerEnabled') === undefined) {
  488. GM_setValue('domainScannerEnabled', false);
  489. }
  490. updateScannerMenuCommand();
  491.  
  492. if (getScannerEnabled()) {
  493. if (document.readyState === 'complete' || document.readyState === 'interactive') {
  494. ensureScannerContainer();
  495. } else {
  496. document.addEventListener('DOMContentLoaded', ensureScannerContainer);
  497. }
  498. }
  499.  
  500. })();