您需要先安装一个扩展,例如 篡改猴、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();
- }
- })();