Авто поиск Google reCAPTCHA sitekey с анимацией подсветки нового ключа, копирование, история, сохранение панели
// ==UserScript==
// @name Auto Sitekey Finder + Animated Highlight + History + Clear + Persistent Position + Copy
// @namespace https://example.com
// @version 1.8
// @description Авто поиск Google reCAPTCHA sitekey с анимацией подсветки нового ключа, копирование, история, сохранение панели
// @match *://*/*
// @grant GM_setClipboard
// @grant GM_getValue
// @grant GM_setValue
// ==/UserScript==
(function () {
'use strict';
const CHECK_INTERVAL = 3000; // каждые 3 секунды проверяем
const MAX_HISTORY = 10;
const HIGHLIGHT_DURATION = 2500; // ms подсветки нового ключа
const savedPos = GM_getValue('sitekeyPanelPos', { left: '10px', top: '10px' });
let history = GM_getValue('sitekeyHistory', []);
let lastKey = null;
const panel = document.createElement('div');
panel.style.position = 'fixed';
panel.style.left = savedPos.left;
panel.style.top = savedPos.top;
panel.style.background = 'rgba(0,0,0,0.7)';
panel.style.color = '#00ff00';
panel.style.padding = '8px 12px';
panel.style.fontFamily = 'monospace';
panel.style.fontSize = '12px';
panel.style.borderRadius = '8px';
panel.style.cursor = 'grab';
panel.style.zIndex = '999999';
document.body.appendChild(panel);
const contentDiv = document.createElement('div');
panel.appendChild(contentDiv);
const refreshBtn = document.createElement('button');
refreshBtn.style.marginTop = '4px';
refreshBtn.style.fontSize = '11px';
refreshBtn.innerText = 'Обновить поиск';
panel.appendChild(refreshBtn);
const clearBtn = document.createElement('button');
clearBtn.style.marginTop = '4px';
clearBtn.style.marginLeft = '4px';
clearBtn.style.fontSize = '11px';
clearBtn.innerText = 'Очистить историю';
panel.appendChild(clearBtn);
function renderPanel(currentKey, animate=false) {
let html = '';
if (currentKey) {
html += `Sitekey найден:<br><b>${currentKey}</b><br>(скопирован)<br><br>`;
} else {
html += 'Sitekey не найден<br><br>';
}
if (history.length) {
html += 'История:<br>';
for (let i = history.length - 1; i >= 0; i--) {
const isNew = animate && history[i] === lastKey;
html += `<div class="history-item" style="font-size:11px; ${isNew ? 'background:#006400;color:#fff;padding:2px;border-radius:2px;' : ''}">${history[i]}</div>`;
}
}
contentDiv.innerHTML = html;
if (animate) {
const newElem = contentDiv.querySelector('.history-item');
if (newElem) {
newElem.animate([
{ backgroundColor: '#006400', color:'#fff' },
{ backgroundColor: 'transparent', color:'#00ff00' }
], { duration: HIGHLIGHT_DURATION, easing: 'ease-out' });
}
}
}
// Dragging
let isDragging = false, offsetX, offsetY;
panel.addEventListener('mousedown', e => {
if (e.target === refreshBtn || e.target === clearBtn) return;
isDragging = true;
offsetX = e.clientX - panel.getBoundingClientRect().left;
offsetY = e.clientY - panel.getBoundingClientRect().top;
panel.style.cursor = 'grabbing';
});
document.addEventListener('mousemove', e => {
if (isDragging) {
panel.style.left = (e.clientX - offsetX) + 'px';
panel.style.top = (e.clientY - offsetY) + 'px';
}
});
document.addEventListener('mouseup', () => {
if (isDragging) {
isDragging = false;
panel.style.cursor = 'grab';
GM_setValue('sitekeyPanelPos', { left: panel.style.left, top: panel.style.top });
}
});
function copyToClipboard(text) {
if (typeof GM_setClipboard === 'function') {
GM_setClipboard(text);
} else {
navigator.clipboard.writeText(text).catch(e => console.error(e));
}
}
function findSitekey() {
let sitekey = null;
const el = document.querySelector('.g-recaptcha');
if (el && el.dataset.sitekey) sitekey = el.dataset.sitekey;
if (!sitekey) {
const iframe = document.querySelector('iframe[src*="recaptcha/api2/anchor"]');
if (iframe) {
const url = new URL(iframe.src);
sitekey = url.searchParams.get('k');
}
}
if (!sitekey && window.grecaptcha && grecaptcha.execute) {
try {
if (grecaptcha.enterprise) sitekey = grecaptcha.enterprise.sitekey || null;
} catch (e) {}
}
if (sitekey && lastKey !== sitekey) {
lastKey = sitekey;
history.push(sitekey);
if (history.length > MAX_HISTORY) history.shift();
GM_setValue('sitekeyHistory', history);
copyToClipboard(sitekey);
renderPanel(sitekey, true); // с анимацией
} else {
renderPanel(sitekey, false);
}
}
refreshBtn.addEventListener('click', findSitekey);
clearBtn.addEventListener('click', () => {
history = [];
lastKey = null;
GM_setValue('sitekeyHistory', history);
renderPanel(null);
});
setInterval(findSitekey, CHECK_INTERVAL);
window.addEventListener('load', () => setTimeout(findSitekey, 2000));
})();