// ==UserScript==
// @name 共享账号搜索
// @namespace http://tampermonkey.net/
// @version 1.3
// @description 获取共享账号并自动填写
// @author Hồng Minh Tâm & Gemini
// @icon http://bugmenot.com/favicon.ico
// @include *
// @connect bugmenot.com
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
// @license GNU GPLv3
// @downloadURL https://update.greasyfork.icu/scripts/553059/%E5%85%B1%E4%BA%AB%E8%B4%A6%E5%8F%B7%E6%90%9C%E7%B4%A2.user.js
// @updateURL https://update.greasyfork.icu/scripts/553059/%E5%85%B1%E4%BA%AB%E8%B4%A6%E5%8F%B7%E6%90%9C%E7%B4%A2.meta.js
// ==/UserScript==
(function () {
'use strict';
const icons = {
mail: ''
}
GM_addStyle([
'.bmn-list { display:none; list-style: none; border: 1px solid rgba(128, 128, 128, 0.5); padding: 0; margin: 0; background-color: rgb(44,44,44); color: rgb(203, 203, 203); position: fixed; cursor: default; z-index: 9999999999; box-sizing: border-box; overflow: auto; text-align: left; width: 300px; }',
'.bmn-list.show { display:block; }',
'.bmn-list .bmn-item { position: relative; padding: 8px 10px 8px 15px; margin: 0; cursor: pointer; border-bottom: 1px solid rgba(128, 128, 128, 0.2); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; font-size: 9pt; line-height: 1.4;}',
'.bmn-list .bmn-item:last-child { border-bottom: 0; }',
'.bmn-list .bmn-item:hover { background-color: rgba(255, 255, 255, 0.1); }',
'.bmn-list .bmn-item:before { position: absolute; content: ""; width: 5px; top: 0; left: 0; bottom: 0; background-color: #f7704f; }',
'.bmn-list .bmn-item .bmn-label { font-weight: 600; }',
'.bmn-list .bmn-item .bmn-username { color: rgb(246, 182, 78); }',
'.bmn-list .bmn-item .bmn-password { color: rgb(118, 202, 83); }',
'.bmn-list .bmn-item .bmn-email-entry { cursor: pointer; }',
'.bmn-list .bmn-item .bmn-email-entry .bmn-value { color: rgb(138, 180, 248); text-decoration: none; }',
'.bmn-list .bmn-item .bmn-email-entry:hover .bmn-value { text-decoration: underline; }',
'.bmn-list .bmn-item .bmn-success-rate { float: right; font-weight: 700; margin-left: 10px; }',
'.bmn-list .bmn-item.bmn-success-100 .bmn-success-rate { color: rgb(0,198,0); }',
'.bmn-list .bmn-item.bmn-success-100:before { background-color: rgb(0,198,0); }',
'.bmn-list .bmn-item.bmn-success-90 .bmn-success-rate { color: rgb(50,180,0); }',
'.bmn-list .bmn-item.bmn-success-90:before { background-color: rgb(50,180,0); }',
'.bmn-list .bmn-item.bmn-success-80 .bmn-success-rate { color: rgb(99,164,0); }',
'.bmn-list .bmn-item.bmn-success-80:before { background-color: rgb(99,164,0); }',
'.bmn-list .bmn-item.bmn-success-70 .bmn-success-rate { color: rgb(149,146,0); }',
'.bmn-list .bmn-item.bmn-success-70:before { background-color: rgb(149,146,0); }',
'.bmn-list .bmn-item.bmn-success-60 .bmn-success-rate { color: rgb(199,129,0); }',
'.bmn-list .bmn-item.bmn-success-60:before { background-color: rgb(199,129,0); }',
'.bmn-list .bmn-item.bmn-success-50 .bmn-success-rate { color: rgb(247,112,0); }',
'.bmn-list .bmn-item.bmn-success-50:before { background-color: rgb(247,112,0); }',
'.bmn-list .bmn-item.bmn-success-40 .bmn-success-rate { color: rgb(247,90,0); }',
'.bmn-list .bmn-item.bmn-success-40:before { background-color: rgb(247,90,0); }',
'.bmn-list .bmn-item.bmn-success-30 .bmn-success-rate { color: rgb(247,67,0); }',
'.bmn-list .bmn-item.bmn-success-30:before { background-color: rgb(247,67,0); }',
'.bmn-list .bmn-item.bmn-success-20 .bmn-success-rate { color: rgb(247,45,0); }',
'.bmn-list .bmn-item.bmn-success-20:before { background-color: rgb(247,45,0); }',
'.bmn-list .bmn-item.bmn-success-10 .bmn-success-rate { color: rgb(247,22,0); }',
'.bmn-list .bmn-item.bmn-success-10:before { background-color: rgb(247,22,0); }',
'.bmn-list .bmn-no-logins-found, .bmn-list .bmn-loading { padding: 10px 15px; margin: 0; cursor: default; text-align: center; background-color: #a90000; color: #fff; font-size: 9pt; }',
'.bmn-list .bmn-loading { background-color: #007bff; color: #fff; }',
// --- [核心修复] 强制 box-sizing 防止尺寸计算错误 ---
'.bmn-floating-button { position: fixed !important; background: transparent !important; border: none !important; cursor: pointer !important; padding: 2px !important; display: flex !important; align-items: center; justify-content: center; z-index: 999999 !important; opacity: 0.5; transition: opacity 0.2s ease, transform 0.2s ease !important; color: grey !important; pointer-events: auto !important; margin: 0 !important; width: 24px; height: 24px; box-sizing: border-box !important; }',
'.bmn-floating-button:hover { opacity: 1 !important; transform: scale(1.1); }',
'.bmn-floating-button svg { width: 18px; height: 18px; display: block; }',
'.bmn-list::-webkit-scrollbar { width: 8px; }',
'.bmn-list::-webkit-scrollbar-track { background: rgb(44, 44, 44); }',
'.bmn-list::-webkit-scrollbar-thumb { background-color: rgb(159, 159, 159); border-radius: 4px; }',
'.bmn-list::-webkit-scrollbar-thumb:hover { background-color: rgb(190, 190, 190); }'
].join(''));
// --- 工具函数 ---
Object.defineProperty(String.prototype, 'toDOM', {
value: function (isFull) {
var parser = new DOMParser(),
dom = parser.parseFromString(this, 'text/html');
return isFull ? dom : dom.body.childNodes[0];
},
enumerable: false
});
function debounce(func, wait) {
let timeout;
return function(...args) {
const context = this;
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(context, args), wait);
};
}
function setValueInput(input, value, isInputSimulate) {
var setValue = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
setValue.call(input, value);
if (isInputSimulate) {
var e = new Event('input', {
bubbles: true
});
input.dispatchEvent(e);
}
}
function getOffset(element) {
var elementRect = element.getBoundingClientRect();
return {
left: elementRect.left,
right: elementRect.right,
top: elementRect.top,
bottom: elementRect.bottom,
width: elementRect.width,
height: elementRect.height
};
}
function handleEvent(func, data) {
return function (event) {
func.bind(this)(event, data);
};
}
// --- 核心变量 ---
var accounts = [];
var dataFetched = false;
var isLoading = false;
var inputUsernameCurrentEl, inputPasswordCurrentEl;
var listBMNEl = null;
var buttonMap = new Map();
// --- 主要功能 ---
function fetchData(callback) {
if (isLoading) return;
isLoading = true;
listBMNEl.innerHTML = '
正在加载...';
showListBMNEl();
GM_xmlhttpRequest({
method: 'GET',
url: 'http://bugmenot.com/view/' + location.hostname,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
onload: function (response) {
var bmnEl = response.responseText.toDOM(true);
var accountEls = bmnEl.getElementsByClassName('account');
accounts = [];
for (var i = 0; i < accountEls.length; i++) {
var accountEl = accountEls[i];
var infoEl = accountEl.getElementsByTagName('kbd');
var statsEl = accountEl.getElementsByClassName('stats')[1].getElementsByTagName('li');
var account = {
username: infoEl[0].innerHTML || '',
password: infoEl[1].innerHTML || '',
email: (infoEl[2] && infoEl[2].innerHTML) ? infoEl[2].innerHTML : '',
success: parseInt(statsEl[0].innerHTML.match(/\d+(?=%)/)[0]),
vote: parseInt(statsEl[1].innerHTML.match(/\d+(?=\svotes)/)[0]),
time: statsEl[2].innerHTML
};
accounts.push(account);
}
dataFetched = true;
isLoading = false;
populateList();
if (callback) callback();
},
onerror: function (response) {
isLoading = false;
listBMNEl.innerHTML = '获取数据失败';
}
});
}
function initListContainer() {
if (listBMNEl) return;
listBMNEl = document.createElement('ul');
listBMNEl.classList.add('bmn-list');
document.body.appendChild(listBMNEl);
}
function populateList() {
if (!listBMNEl) return;
listBMNEl.innerHTML = '';
if (accounts.length) {
accounts.forEach(function (account, index) {
var itemBMNEl = document.createElement('li');
itemBMNEl.classList.add('bmn-item');
itemBMNEl.classList.add(getClassSuccess(account.success));
var emailHTML = '';
if (account.email && account.email.trim() !== '') {
emailHTML = `
邮箱:
${account.email}
`;
}
var itemBMNElHTML = `
${account.success}%
账号:
${account.username}
密码:
${account.password}
${emailHTML}
`;
itemBMNEl.innerHTML = itemBMNElHTML;
var titleLines = [
'账号: ' + account.username,
'密码: ' + account.password,
];
if (account.email && account.email.trim() !== '') {
titleLines.push('邮箱: ' + account.email);
}
titleLines.push(
'',
'成功率: ' + account.success + '%',
'投票数: ' + account.vote,
'提交于: ' + account.time
);
itemBMNEl.title = titleLines.join('\n');
itemBMNEl.onmousedown = handleEvent(onMouseDownItem);
itemBMNEl.onclick = handleEvent(onClickItem, account);
itemBMNEl.onmouseover = handleEvent(onMouseOverItem, account);
itemBMNEl.onmouseout = handleEvent(onMouseOutItem);
var emailEl = itemBMNEl.querySelector('.bmn-email-entry');
if (emailEl) {
emailEl.onmousedown = handleEvent(onMouseDownItem);
emailEl.onclick = handleEvent(onClickEmailItem, account);
emailEl.onmouseover = handleEvent(onMouseOverEmailItem, account);
}
listBMNEl.appendChild(itemBMNEl);
});
} else {
var itemBMNNoLoginsFoundEl = document.createElement('li');
itemBMNNoLoginsFoundEl.classList.add('bmn-no-logins-found');
itemBMNNoLoginsFoundEl.innerHTML = '未找到共享账号';
listBMNEl.appendChild(itemBMNNoLoginsFoundEl);
}
}
function closeOnClickOutside(event) {
if (listBMNEl && !listBMNEl.contains(event.target) && !event.target.closest('.bmn-floating-button')) {
hideListBMNEl();
}
}
function showListBMNEl() {
if (listBMNEl) {
listBMNEl.classList.add('show');
document.addEventListener('mousedown', closeOnClickOutside, true);
}
}
function hideListBMNEl() {
if (listBMNEl) {
listBMNEl.classList.remove('show');
document.removeEventListener('mousedown', closeOnClickOutside, true);
}
}
var enableMouseOut = true;
function setValueInputItem(inputUsernameEl, inputPasswordEl, username, password, isInputSimulate) {
setValueInput(inputUsernameEl, username, isInputSimulate);
setValueInput(inputPasswordEl, password, isInputSimulate);
}
function onMouseDownItem(event) {
event.stopPropagation();
event.preventDefault();
}
function onClickItem(event, account) {
event.stopPropagation();
enableMouseOut = false;
if (inputUsernameCurrentEl && inputPasswordCurrentEl) {
setValueInputItem(inputUsernameCurrentEl, inputPasswordCurrentEl, account.username, account.password, true);
inputUsernameCurrentEl.setAttribute('value', account.username);
hideListBMNEl();
}
}
function onClickEmailItem(event, account) {
event.stopPropagation();
enableMouseOut = false;
if (inputUsernameCurrentEl && inputPasswordCurrentEl) {
setValueInputItem(inputUsernameCurrentEl, inputPasswordCurrentEl, account.email, account.password, true);
inputUsernameCurrentEl.setAttribute('value', account.email);
hideListBMNEl();
}
}
function onMouseOverItem(event, account) {
if (inputUsernameCurrentEl && inputPasswordCurrentEl) {
setValueInputItem(inputUsernameCurrentEl, inputPasswordCurrentEl, account.username, account.password);
}
}
function onMouseOverEmailItem(event, account) {
event.stopPropagation();
if (inputUsernameCurrentEl && inputPasswordCurrentEl) {
setValueInputItem(inputUsernameCurrentEl, inputPasswordCurrentEl, account.email, account.password);
}
}
function onMouseOutItem(event) {
if (!enableMouseOut) {
enableMouseOut = true;
return;
}
if (inputUsernameCurrentEl && inputPasswordCurrentEl) {
setValueInputItem(inputUsernameCurrentEl, inputPasswordCurrentEl, '', '');
}
}
function getClassSuccess(success) {
if (success > 91) return 'bmn-success-100';
else if (success > 81) return 'bmn-success-90';
else if (success > 71) return 'bmn-success-80';
else if (success > 61) return 'bmn-success-70';
else if (success > 51) return 'bmn-success-60';
else if (success > 31) return 'bmn-success-50';
else if (success > 21) return 'bmn-success-30';
else if (success > 11) return 'bmn-success-20';
else return 'bmn-success-10';
}
function addStyleListBMNEl(inputEl) {
if (!listBMNEl) return;
const offsetTarget = getOffset(inputEl);
const windowHeight = document.documentElement.clientHeight;
const windowWidth = document.documentElement.clientWidth;
const listWidth = listBMNEl.offsetWidth || 300;
const gap = 5;
listBMNEl.style.top = offsetTarget.top + 'px';
listBMNEl.style.bottom = '';
listBMNEl.style.maxHeight = (windowHeight - offsetTarget.top - 10) + 'px';
const spaceOnRight = windowWidth - offsetTarget.right;
const spaceOnLeft = offsetTarget.left;
if (spaceOnRight >= listWidth + gap) {
listBMNEl.style.left = (offsetTarget.right + gap) + 'px';
listBMNEl.style.right = '';
}
else if (spaceOnLeft >= listWidth + gap) {
listBMNEl.style.left = (offsetTarget.left - listWidth - gap) + 'px';
listBMNEl.style.right = '';
}
else {
listBMNEl.style.right = gap + 'px';
listBMNEl.style.left = '';
}
}
function updateButtonPosition(inputEl, button) {
if (!inputEl || !button || !document.body.contains(inputEl)) {
if (button) button.style.display = 'none';
return;
}
const rect = getOffset(inputEl);
const buttonSize = 24;
const rightMargin = 10;
button.style.top = (rect.top + (rect.height - buttonSize) / 2) + 'px';
button.style.left = (rect.right - buttonSize - rightMargin) + 'px';
if (rect.width > 0 && rect.height > 0 && isVisible(inputEl)) {
button.style.display = 'flex';
} else {
button.style.display = 'none';
}
}
function updateAllButtonPositions() {
buttonMap.forEach((button, inputEl) => {
updateButtonPosition(inputEl, button);
});
}
function onButtonClick(event, data) {
event.preventDefault();
event.stopPropagation();
if (listBMNEl && listBMNEl.classList.contains('show')) {
hideListBMNEl();
return;
}
inputUsernameCurrentEl = data.inputUsernameEl;
inputPasswordCurrentEl = data.inputPasswordEl;
initListContainer();
addStyleListBMNEl(inputUsernameCurrentEl);
if (dataFetched) {
populateList();
showListBMNEl();
addStyleListBMNEl(inputUsernameCurrentEl);
} else {
fetchData(() => {
showListBMNEl();
addStyleListBMNEl(inputUsernameCurrentEl);
});
}
}
function onInputInput(event) {
enableMouseOut = false;
hideListBMNEl();
}
function onInputFocus(event, data) {
const button = data.button;
if (button) {
button.style.opacity = '0.8';
}
}
function onInputBlur(event, data) {
const button = data.button;
if (button) {
button.style.opacity = '0.5';
}
}
function isVisible(el) {
return !!(el && (el.offsetWidth || el.offsetHeight || el.getClientRects().length));
}
function isValidUsernameInput(el) {
if (!el || !(el instanceof Element) || el.dataset.bmnChecked) {
return false;
}
const isPassword = el.type === 'password';
const isInput = el.tagName.toLowerCase() === 'input';
const isValidType = ['text', 'email', 'tel', 'url', 'number', undefined, ''].includes(el.type);
return isInput && !isPassword && isValidType && isVisible(el);
}
function attachButton(inputUsernameEl, inputPasswordEl) {
inputUsernameEl.dataset.bmnChecked = true;
inputPasswordEl.dataset.bmnChecked = true;
const data = {
inputUsernameEl: inputUsernameEl,
inputPasswordEl: inputPasswordEl
};
const button = document.createElement('button');
button.className = 'bmn-floating-button';
button.type = 'button';
button.title = '获取共享账号';
button.innerHTML = icons.mail;
data.button = button;
button.onclick = handleEvent(onButtonClick, data);
document.body.appendChild(button);
buttonMap.set(inputUsernameEl, button);
updateButtonPosition(inputUsernameEl, button);
inputUsernameEl.oninput = handleEvent(onInputInput);
inputUsernameEl.onfocus = handleEvent(onInputFocus, data);
inputUsernameEl.onblur = handleEvent(onInputBlur, data);
const resizeObserver = new ResizeObserver(() => {
updateButtonPosition(inputUsernameEl, button);
});
resizeObserver.observe(inputUsernameEl);
const cleanupObserver = new MutationObserver((mutations) => {
if (!document.body.contains(inputUsernameEl)) {
button.remove();
buttonMap.delete(inputUsernameEl);
resizeObserver.disconnect();
cleanupObserver.disconnect();
}
});
cleanupObserver.observe(document.body, {
childList: true,
subtree: true
});
}
function checkAndEventToInput() {
const passwordInputs = document.querySelectorAll('input[type=password]:not([data-bmn-checked])');
if (passwordInputs.length === 0) return;
const allPotentialInputs = Array.from(document.querySelectorAll(
'input:not([type]), input[type=text], input[type=email], input[type=tel], input[type=password]'
));
passwordInputs.forEach(passwordInput => {
if (!isVisible(passwordInput)) return;
let foundUsernameInput = null;
let siblingCandidate = passwordInput.previousElementSibling;
for (let i = 0; i < 3 && siblingCandidate; i++) {
if (isValidUsernameInput(siblingCandidate)) {
foundUsernameInput = siblingCandidate;
break;
}
siblingCandidate = siblingCandidate.previousElementSibling;
}
if (foundUsernameInput) {
attachButton(foundUsernameInput, passwordInput);
return;
}
const passwordDomIndex = allPotentialInputs.indexOf(passwordInput);
if (passwordDomIndex > 0) {
for (let i = passwordDomIndex - 1; i >= 0; i--) {
const globalCandidate = allPotentialInputs[i];
if (isValidUsernameInput(globalCandidate)) {
foundUsernameInput = globalCandidate;
break;
}
}
}
if (foundUsernameInput) {
attachButton(foundUsernameInput, passwordInput);
}
});
}
// --- 启动与监视 ---
function initialScan() {
checkAndEventToInput();
setTimeout(checkAndEventToInput, 500);
}
if (document.readyState === 'complete') {
initialScan();
} else {
window.addEventListener('load', initialScan);
}
const debouncedUpdateAllPositions = debounce(updateAllButtonPositions, 150);
window.addEventListener('scroll', debouncedUpdateAllPositions, true);
window.addEventListener('resize', debouncedUpdateAllPositions);
const observer = new MutationObserver(() => {
checkAndEventToInput();
debouncedUpdateAllPositions();
});
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true
});
})();