NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name UnoAnalyzer 优诺 自动登录
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 自动完成登录,支持手动输入账号或自动填入账号,右上角显示操作进度
// @author wujinjun
// @license MIT
// @match https://cn.unoanalyzer.com/*
// @match https://unoanalyzer.com/*
// @grant none
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
const scriptName = GM_info.script.name;
const LOG_PREFIX = `[${scriptName}] `;
const log = (...args) => console.log(LOG_PREFIX, ...args);
const warn = (...args) => console.warn(LOG_PREFIX, ...args);
const error = (...args) => console.error(LOG_PREFIX, ...args);
// =========================================================================================
const SAVED_EMAIL = 'YOUR_SAVED_EMAIL'; // <-- 替换为您的邮箱 (例如: 'FirstName.LastName01@example.com')
const SAVED_PASSWORD = 'YOUR_SAVED_PASSWORD'; // <-- 替换为您的密码 (例如: 'MySecurePassword')
// =========================================================================================
// --- 元素选择器定义 ---
const EMAIL_SELECTOR = '#email';
const PASSWORD_SELECTOR = '#password';
const SUBMIT_SELECTOR = 'button[type="submit"]';
// --- 辅助函数:等待元素出现 ---
function waitForElement(selector) {
return new Promise(resolve => {
const element = document.querySelector(selector);
if (element) {
return resolve(element);
}
const observer = new MutationObserver((mutationsList, observer) => {
const target = document.querySelector(selector);
if (target) {
observer.disconnect();
resolve(target);
}
});
observer.observe(document.body, { childList: true, subtree: true });
});
}
// --- 辅助函数: React "Hack" 设置输入框内容 ---
// Credit: https://stackoverflow.com/questions/52120524/type-text-into-a-react-input-using-javascript-tampermonkey-script
// Archive: https://web.archive.org/web/20250805153214/https://stackoverflow.com/questions/52120524/type-text-into-a-react-input-using-javascript-tampermonkey-script
// Specific Answer: https://web.archive.org/web/20250805153214/https://stackoverflow.com/a/79077165
function setReactInputValue(el, value) {
// Store the last value to update React's internal tracker
const last = el.value;
// Use the native value setter to change the input's value
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
'value'
).set;
nativeInputValueSetter.call(el, value);
// Update React's internal value tracker
const event = new Event('input', { bubbles: true });
const tracker = el._valueTracker;
if (tracker) {
tracker.setValue(last);
}
// Dispatch the input event to notify React of the change
el.dispatchEvent(event);
}
// --- 主执行函数 ---
// --- URL change detection for SPA navigation ---
function hookHistoryEvents() {
const _wr = (type) => {
const orig = history[type];
return function() {
const res = orig.apply(this, arguments);
window.dispatchEvent(new Event('locationchange'));
return res;
};
};
history.pushState = _wr('pushState');
history.replaceState = _wr('replaceState');
window.addEventListener('popstate', () => window.dispatchEvent(new Event('locationchange')));
}
function isLoginUrl(href) {
try {
const u = new URL(href, location.origin);
const p = u.pathname || '';
return p.startsWith('/app/login') || p.startsWith('/login');
} catch (e) {
return false;
}
}
let _autoLoginRunning = false;
async function main() {
if (_autoLoginRunning) {
log('自动登录已在运行,跳过');
return;
}
_autoLoginRunning = true;
// 使用 Promise.all 等待关键元素加载
try {
const [emailEl, submitEl] = await Promise.all([
waitForElement(EMAIL_SELECTOR),
waitForElement(SUBMIT_SELECTOR),
]);
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
const triggerInputEvents = (el) => {
el.dispatchEvent(new Event('input', { bubbles: true }));
el.dispatchEvent(new Event('change', { bubbles: true }));
};
// 填写邮箱并提交
if (SAVED_EMAIL && SAVED_EMAIL !== 'YOUR_SAVED_EMAIL') {
emailEl.focus();
setReactInputValue(emailEl, SAVED_EMAIL);
triggerInputEvents(emailEl);
log('邮箱已填写');
} else {
log('SAVED_EMAIL 未设置,跳过填写邮箱');
}
await sleep(200);
if (submitEl) {
submitEl.focus();
await sleep(500);
submitEl.click();
log('已点击提交(邮箱阶段)');
} else {
warn('未找到提交按钮(邮箱阶段)');
}
// 等待密码输入框出现并填写
const passwordEl = await waitForElement(PASSWORD_SELECTOR);
if (passwordEl) {
if (SAVED_PASSWORD && SAVED_PASSWORD !== 'YOUR_SAVED_PASSWORD') {
passwordEl.focus();
setReactInputValue(passwordEl, SAVED_PASSWORD);
triggerInputEvents(passwordEl);
log('密码已填写');
} else {
log('SAVED_PASSWORD 未设置,跳过填写密码');
}
await sleep(200);
const submitEl2 = document.querySelector(SUBMIT_SELECTOR);
if (submitEl2) {
submitEl2.click();
log('已点击提交(密码阶段)');
} else {
warn('未找到提交按钮(密码阶段)');
}
} else {
warn('未找到密码输入框');
}
} catch (e) {
error('自动登录出错', e);
} finally {
_autoLoginRunning = false;
}
}
// 运行主函数
main();
})();