您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Интерфейс для сайта stackblitz предоставляющую возможность загружать свою базу аккаунтов (локально) и использовать ее для упрощения логина в разные аккаунты
// ==UserScript== // @name Stackblitz Account Selector (RU) // @namespace http://tampermonkey.net/ // @version 3.5 // @description Интерфейс для сайта stackblitz предоставляющую возможность загружать свою базу аккаунтов (локально) и использовать ее для упрощения логина в разные аккаунты // @author t.me/dud3lk // @match https://stackblitz.com/* // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @license GNU GPLv3 // ==/UserScript== (function () { 'use strict'; const BACKGROUND_COLOR = '#1D1F24'; const BUTTON_LOGIN_COLOR = '#28a745'; const BUTTON_DELETE_COLOR = '#dc3545'; const BUTTON_UPLOAD_COLOR = '#007bff'; let logoutObserver = null; let lastUsedAccount = null; if (window.location.href.includes('sign_in')) { handleLoginPage(); } else { handleMainPage(); } function handleLoginPage() { waitForLoginFields(); startLogoutDetection(); } function handleMainPage() { waitForSignInButton(); startLogoutDetection(); } function startLogoutDetection() { if (logoutObserver) return; logoutObserver = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.addedNodes.length) { const signInLink = document.querySelector('a[href="/sign_in"]'); if (signInLink) { handleLogoutEvent(); } } }); }); logoutObserver.observe(document.body, { childList: true, subtree: true }); } function handleLogoutEvent() { const email = GM_getValue('lastUsedAccount', null); if (email) { logLogout(email); GM_deleteValue('lastUsedAccount'); const container = document.getElementById('account-selector-container'); if (container) { container.remove(); showAccountSelector(); } } } function logLogout(email) { const logs = GM_getValue('logoutLogs', {}); logs[email] = new Date().toISOString(); GM_setValue('logoutLogs', logs); console.log(`[LOGOUT] ${email} at ${logs[email]}`); } function logLogin(email) { const logs = GM_getValue('loginLogs', {}); logs[email] = new Date().toISOString(); GM_setValue('loginLogs', logs); GM_setValue('lastUsedAccount', email); console.log(`[LOGIN] ${email} at ${logs[email]}`); } function showAccountSelector() { const accounts = GM_getValue('accounts', []); const logoutLogs = GM_getValue('logoutLogs', {}); const loginLogs = GM_getValue('loginLogs', {}); if (accounts.length === 0) { alert('Нет доступных аккаунтов. Загрузите файл.'); return; } const container = document.createElement('div'); container.id = 'account-selector-container'; container.style.position = 'fixed'; container.style.bottom = '10px'; container.style.right = '10px'; container.style.backgroundColor = BACKGROUND_COLOR; container.style.padding = '20px'; container.style.border = '1px solid #333'; container.style.zIndex = 9999; container.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)'; container.style.borderRadius = '8px'; container.style.width = '450px'; container.style.maxHeight = '600px'; container.style.overflowY = 'auto'; container.style.color = '#fff'; const title = document.createElement('h3'); title.textContent = 'Выберите аккаунт:'; title.style.marginBottom = '15px'; title.style.color = '#aaa'; title.style.fontSize = '16px'; const list = document.createElement('ul'); list.style.listStyleType = 'none'; list.style.padding = '0'; list.style.margin = '0'; accounts.forEach(account => { const [email] = account.split(':'); const lastLogout = logoutLogs[email]; const lastLogin = loginLogs[email]; const listItem = document.createElement('li'); listItem.style.display = 'flex'; listItem.style.justifyContent = 'space-between'; listItem.style.alignItems = 'center'; listItem.style.padding = '10px'; listItem.style.borderBottom = '1px solid #333'; listItem.style.cursor = 'pointer'; listItem.style.transition = 'background-color 0.3s'; const emailSpan = document.createElement('span'); emailSpan.style.display = 'flex'; emailSpan.style.flexDirection = 'column'; emailSpan.style.gap = '5px'; const emailText = document.createElement('span'); emailText.textContent = email; emailText.style.color = '#ccc'; emailText.style.fontSize = '14px'; const lastLoginSpan = document.createElement('span'); lastLoginSpan.style.color = '#888'; lastLoginSpan.style.fontSize = '12px'; lastLoginSpan.textContent = lastLogin ? `Вход: ${new Date(lastLogin).toLocaleString()}` : ''; const lastLogoutSpan = document.createElement('span'); lastLogoutSpan.style.color = '#888'; lastLogoutSpan.style.fontSize = '12px'; if (lastLogout) { const logoutDate = new Date(lastLogout); const now = new Date(); const diffHours = Math.floor((now - logoutDate) / 36e5); lastLogoutSpan.textContent = `Выход: ${logoutDate.toLocaleString()}`; if (diffHours < 24) { lastLogoutSpan.textContent += ` (Заблокирован на ${24 - diffHours}ч)`; } } else { lastLogoutSpan.textContent = 'Выходов не было'; } emailSpan.appendChild(emailText); emailSpan.appendChild(lastLoginSpan); emailSpan.appendChild(lastLogoutSpan); const loginButton = createButton('Войти', BUTTON_LOGIN_COLOR); loginButton.style.padding = '5px 10px'; loginButton.style.fontSize = '12px'; if (lastLogout) { const logoutDate = new Date(lastLogout); const now = new Date(); const diffHours = Math.floor((now - logoutDate) / 36e5); if (diffHours < 24) { loginButton.style.backgroundColor = BUTTON_DELETE_COLOR; loginButton.title = `Доступно через: ${24 - diffHours} часов`; } } loginButton.addEventListener('click', () => { GM_setValue('selectedAccount', account); redirectToSignIn(); container.remove(); }); listItem.appendChild(emailSpan); listItem.appendChild(loginButton); list.appendChild(listItem); }); const deleteButton = createButton('Удалить аккаунты', BUTTON_DELETE_COLOR); deleteButton.style.marginTop = '15px'; deleteButton.addEventListener('click', () => { GM_deleteValue('accounts'); GM_deleteValue('logoutLogs'); GM_deleteValue('loginLogs'); alert('Все данные удалены.'); container.remove(); showUploadBasePrompt(); }); container.appendChild(title); container.appendChild(list); container.appendChild(deleteButton); document.body.appendChild(container); } function createButton(text, color) { const button = document.createElement('button'); button.textContent = text; button.style.display = 'inline-block'; button.style.padding = '10px 20px'; button.style.backgroundColor = color; button.style.color = '#fff'; button.style.border = 'none'; button.style.borderRadius = '4px'; button.style.cursor = 'pointer'; button.style.fontSize = '14px'; button.style.marginTop = '10px'; return button; } function redirectToSignIn() { waitForElement('a[href="/sign_in"]', (signInButton) => { signInButton.click(); const checkForSignInPage = setInterval(() => { if (window.location.href.includes('sign_in')) { clearInterval(checkForSignInPage); waitForLoginFields(); } }, 200); setTimeout(() => { clearInterval(checkForSignInPage); alert('Переход на страницу входа не выполнен. Попробуйте войти вручную.'); }, 10000); }); } function waitForLoginFields() { waitForElement('input[name="login"], input[name="password"], button[type="submit"]', () => { const selectedAccount = GM_getValue('selectedAccount', null); if (selectedAccount) { const [email, password] = selectedAccount.split(':'); performLogin(email, password); } else { alert('Не выбран аккаунт. Вернитесь на главную страницу и выберите аккаунт.'); } }); } function performLogin(email, password) { const emailInput = document.querySelector('input[name="login"]'); const passwordInput = document.querySelector('input[name="password"]'); const submitButton = document.querySelector('button[type="submit"]'); if (emailInput && passwordInput && submitButton) { emailInput.value = email; passwordInput.value = password; setTimeout(() => { submitButton.click(); logLogin(email); GM_deleteValue('selectedAccount'); }, 500); } else { alert('Не удалось найти поля для входа. Пожалуйста, войдите вручную.'); } } function waitForSignInButton() { waitForElement('a._link_kkm2m_1[href="/sign_in"]', () => { loadAccounts(); showAccountSelector(); }); } function waitForElement(selector, callback) { const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { const element = document.querySelector(selector); if (element) { observer.disconnect(); callback(element); } }); }); observer.observe(document.body, { childList: true, subtree: true }); } function loadAccounts() { const accounts = GM_getValue('accounts', null); if (!accounts) { showUploadBasePrompt(); } } function showUploadBasePrompt() { const container = document.createElement('div'); container.style.position = 'fixed'; container.style.bottom = '10px'; container.style.right = '10px'; container.style.backgroundColor = BACKGROUND_COLOR; container.style.padding = '10px'; container.style.border = '1px solid #333'; container.style.zIndex = 9999; container.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)'; container.style.borderRadius = '5px'; container.style.color = '#fff'; const label = document.createElement('label'); label.textContent = 'Загрузите базу аккаунтов:'; label.style.display = 'block'; label.style.marginBottom = '10px'; label.style.color = '#aaa'; const uploadButton = createButton('Загрузить базу', BUTTON_UPLOAD_COLOR); uploadButton.addEventListener('click', () => { createFileInput(); container.remove(); }); container.appendChild(label); container.appendChild(uploadButton); document.body.appendChild(container); } function createFileInput() { const fileInputContainer = document.createElement('div'); fileInputContainer.style.position = 'fixed'; fileInputContainer.style.top = '10px'; fileInputContainer.style.left = '10px'; fileInputContainer.style.backgroundColor = BACKGROUND_COLOR; fileInputContainer.style.padding = '10px'; fileInputContainer.style.border = '1px solid #333'; fileInputContainer.style.zIndex = 9999; fileInputContainer.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)'; fileInputContainer.style.borderRadius = '5px'; fileInputContainer.style.color = '#fff'; const label = document.createElement('label'); label.textContent = 'Выберите файл:'; label.style.display = 'block'; label.style.marginBottom = '10px'; label.style.color = '#aaa'; const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.accept = '.txt'; fileInput.style.display = 'block'; fileInput.addEventListener('change', (event) => { const file = event.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = (e) => { const content = e.target.result; const accountList = content.split('\n').map(line => line.trim()).filter(line => line !== ''); GM_setValue('accounts', accountList); alert('Аккаунты успешно загружены!'); fileInputContainer.remove(); showAccountSelector(); }; reader.readAsText(file); } }); fileInputContainer.appendChild(label); fileInputContainer.appendChild(fileInput); document.body.appendChild(fileInputContainer); fileInput.click(); } })();