您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在 etherscan.io/points 页面自动签到并开启神秘礼盒。以 Apple iOS 18 设计语言重构,拥有精美的琉光玻璃界面与自动深色模式,为你带来无感、愉悦的积分获取体验。新增:自动领礼盒并更新积分。修复:模板字符串显示bug及重复积分问题。
// ==UserScript== // @name Etherscan Points Assistant // @name:zh-CN Etherscan 积分助手 (含神秘礼盒) // @namespace http://tampermonkey.net/ // @version 3.1 // @description A reimagined auto-claimer for Etherscan points with Mystery Box support, featuring a beautiful, iOS 18-inspired interface with light and dark modes. Fixed display bugs. It just works. // @description:zh-CN 在 etherscan.io/points 页面自动签到并开启神秘礼盒。以 Apple iOS 18 设计语言重构,拥有精美的琉光玻璃界面与自动深色模式,为你带来无感、愉悦的积分获取体验。新增:自动领礼盒并更新积分。修复:模板字符串显示bug及重复积分问题。 // @author Mantancoin(AIGC) // @match https://etherscan.io/points // @grant GM_addStyle // @grant GM_log // @license MIT // ==/UserScript== (function() { 'use strict'; // 我们相信,伟大的体验始于伟大的设计。保留原色板,支持深浅模式。 // We believe a great experience starts with great design. Keeping original palette with light/dark modes. GM_addStyle(` :root { /* 浅色模式调色板 - 清新、空灵的感觉。 */ /* Light Mode Palette - a clean, airy feel. */ --bg-color-light: rgba(247, 247, 247, 0.8); --primary-text-light: #1d1d1f; --secondary-text-light: rgba(60, 60, 67, 0.7); --border-color-light: rgba(0, 0, 0, 0.1); --shadow-color-light: rgba(0, 0, 0, 0.12); --apple-blue-light: #007AFF; --apple-green-light: #34C759; --apple-red-light: #FF3B30; /* 深色模式调色板 - 专注且电影感。 */ /* Dark Mode Palette - focused and cinematic. */ --bg-color-dark: rgba(28, 28, 30, 0.75); --primary-text-dark: #f5f5f7; --secondary-text-dark: rgba(235, 235, 245, 0.65); --border-color-dark: rgba(255, 255, 255, 0.15); --shadow-color-dark: rgba(0, 0, 0, 0.25); --apple-blue-dark: #0A84FF; --apple-green-dark: #30D158; --apple-red-dark: #FF453A; } #etherscan-panel { position: fixed; top: 24px; right: 24px; width: 290px; padding: 20px; border-radius: 20px; /* 更柔和、更吸引人的曲线。 */ /* Softer, more inviting curves. */ font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; z-index: 10000; transition: opacity 0.4s cubic-bezier(0.19, 1, 0.22, 1), transform 0.4s cubic-bezier(0.19, 1, 0.22, 1); transform: translateX(0); backdrop-filter: blur(20px) saturate(180%); /* 我们新材质的魔力。 */ /* The magic of our new material. */ background-color: var(--bg-color-light); border: 1px solid var(--border-color-light); box-shadow: 0 10px 30px var(--shadow-color-light); color: var(--primary-text-light); } /* 无缝适应系统外观。 */ /* Seamlessly adapt to your system's appearance. */ @media (prefers-color-scheme: dark) { #etherscan-panel { background-color: var(--bg-color-dark); border-color: var(--border-color-dark); box-shadow: 0 10px 35px var(--shadow-color-dark); color: var(--primary-text-dark); } } #etherscan-panel.hidden { opacity: 0; transform: translateX(30px); pointer-events: none; } #etherscan-panel h3 { margin: 0 0 16px 0; padding-bottom: 12px; border-bottom: 1px solid var(--border-color-light); font-size: 17px; font-weight: 600; display: flex; align-items: center; gap: 8px; /* 完美间距,由设计决定。 */ /* Perfect spacing, by design. */ color: var(--apple-blue-light); } @media (prefers-color-scheme: dark) { #etherscan-panel h3 { border-bottom-color: var(--border-color-dark); color: var(--apple-blue-dark); } } /* 一个感觉像在家一样的图标。 */ /* An icon that feels right at home. */ #etherscan-panel h3::before { content: '💎'; font-size: 20px; } #etherscan-panel p { margin: 10px 0; line-height: 1.5; font-size: 15px; color: var(--secondary-text-light); } #etherscan-panel p strong { font-weight: 500; color: var(--primary-text-light); } @media (prefers-color-scheme: dark) { #etherscan-panel p { color: var(--secondary-text-dark); } #etherscan-panel p strong { color: var(--primary-text-dark); } } #etherscan-panel .success { color: var(--apple-green-light) !important; } #etherscan-panel .error { color: var(--apple-red-light) !important; } @media (prefers-color-scheme: dark) { #etherscan-panel .success { color: var(--apple-green-dark) !important; } #etherscan-panel .error { color: var(--apple-red-dark) !important; } } #etherscan-panel-close { position: absolute; top: 12px; right: 15px; cursor: pointer; font-size: 18px; font-weight: 500; width: 24px; height: 24px; line-height: 24px; text-align: center; border-radius: 50%; color: var(--secondary-text-light); background-color: rgba(0, 0, 0, 0.05); transition: background-color 0.2s ease, color 0.2s ease; } #etherscan-panel-close:hover { background-color: rgba(0, 0, 0, 0.1); color: var(--primary-text-light); } @media (prefers-color-scheme: dark) { #etherscan-panel-close { color: var(--secondary-text-dark); background-color: rgba(255, 255, 255, 0.1); } #etherscan-panel-close:hover { background-color: rgba(255, 255, 255, 0.2); color: var(--primary-text-dark); } } `); // --- UI 创建:这不是一个面板,而是一个窥视进程的窗口。 --- // --- UI Creation: It's not a panel, it's a window into the process. --- // 保留原UI创建逻辑,结构简单,因为简单是终极的复杂。 // Keeping original UI creation logic. The structure is simple, because simplicity is the ultimate sophistication. function createPanel() { const panel = document.createElement('div'); panel.id = 'etherscan-panel'; panel.innerHTML = ` <span id="etherscan-panel-close">×</span> <h3>Etherscan Assistant</h3> <div id="etherscan-panel-content"> <p>正在与 Etherscan 同步...</p> </div> `; document.body.appendChild(panel); document.getElementById('etherscan-panel-close').addEventListener('click', () => { panel.classList.add('hidden'); }); return panel; } // --- 数据解析:从数字中找到清晰度。 --- // --- Data Parsing: Finding clarity in the numbers. --- // 该函数的核心逻辑非常出色,我们予以保留。 // (The core logic of this function is excellent, we are keeping it.) function getPoints(element) { if (!element) return 0; const text = element.innerText || ''; const numberString = text.replace(/💎|,/g, '').trim(); return parseInt(numberString, 10) || 0; } // --- 核心引擎:美丽体验背后的强大技术。 --- // --- Core Engine: The robust technology behind the beautiful experience. --- // 核心功能和稳定性是基石,我们保留原逻辑,并新增礼盒点击。修复:使用反引号确保模板解析;合并积分显示,避免重复。 // (Core functionality and stability are the foundation, which we have preserved untouched.) // 新增:检测并点击神秘礼盒按钮,如果可用。先礼盒后签到,确保最大化积分。 // Addition: Detect and click Mystery Box button if available. Box first, then check-in for max points. // 修复:当已签到+无礼盒时,只显示单一积分和状态消息。 // Fix: When already checked in + no box, display single points and status only. function runAutoClaimer() { let pointsElement = document.getElementById('spanTotalPoints'); const checkinButton = document.getElementById('btncheckin'); const claimTextSpan = document.getElementById('ContentPlaceHolder1_spanBtncheckin'); const mysteryBoxBtn = document.getElementById('mystery-box-btn'); // 新增:获取礼盒按钮 const panelContent = document.getElementById('etherscan-panel-content'); // 检查关键组件是否存在 if (!pointsElement || !checkinButton || !claimTextSpan) { panelContent.innerHTML = '<p>检测不到关键组件,助手无法启动。</p><p class="error">请检查页面是否为最新版本。</p>'; return; } const pointsContainer = pointsElement.parentElement; if (!pointsContainer) { panelContent.innerHTML = '<p class="error">错误:无法定位积分模块,监视器启动失败。</p>'; GM_log('Script Error: Could not find the parent element of #spanTotalPoints.'); return; } const initialPoints = getPoints(pointsElement); // 初始显示:单一积分 + 检查状态(使用反引号模板) // Initial display: Single points + status check (using backticks for template) panelContent.innerHTML = `<p>当前积分: <strong>${initialPoints.toLocaleString()}</strong></p><p>正在检查签到和礼盒状态...</p>`; // 检查签到状态 if (claimTextSpan.style.display === 'none') { panelContent.innerHTML += '<p>今日已完成签到。</p>'; // 追加状态 } else { panelContent.innerHTML += '<p>检测到可签到。</p>'; } // 新增:处理神秘礼盒 // Addition: Handle Mystery Box let hasClaimedBox = false; // 标记是否已处理礼盒 if (mysteryBoxBtn && mysteryBoxBtn.style.display !== 'none' && !mysteryBoxBtn.disabled) { try { GM_log('检测到神秘礼盒,正在自动开启...'); panelContent.innerHTML += '<p>✨ 检测到神秘礼盒,正在自动开启...</p>'; mysteryBoxBtn.click(); // 模拟点击,触发submitMysteryBox('15', '') hasClaimedBox = true; } catch (error) { GM_log('Error clicking Mystery Box: ' + error); panelContent.innerHTML += '<p class="error">礼盒点击失败,请手动尝试。</p>'; } } else { GM_log('No Mystery Box available or already claimed.'); panelContent.innerHTML += '<p>无可用神秘礼盒。</p>'; // 追加状态 } // 如果可签到,继续原签到逻辑 // If check-in available, proceed with original check-in logic if (claimTextSpan.style.display !== 'none') { GM_log('Setting up observer...'); const observer = new MutationObserver((mutationsList, obs) => { GM_log('DOM change detected! Checking for new points...'); pointsElement = document.getElementById('spanTotalPoints'); const newPoints = getPoints(pointsElement); GM_log(`Initial points: ${initialPoints}, New points detected: ${newPoints}`); if (newPoints > initialPoints) { GM_log('Points increased. Updating panel.'); const growth = newPoints - initialPoints; let sourceMsg = '签到成功'; if (hasClaimedBox) { sourceMsg = '签到与礼盒成功(总获取)'; // 统一显示总增长,提示含礼盒 } // 更新整个面板为最新状态,避免重复(使用反引号) // Update entire panel to latest, avoid duplicates (backticks) panelContent.innerHTML = ` <p style="font-weight: 500; color: var(--primary-text-light);">✅ <strong>${sourceMsg}</strong></p> <p>初始积分: ${initialPoints.toLocaleString()}</p> <p>当前积分: <strong>${newPoints.toLocaleString()}</strong></p> <p>本次总获取: <strong class="success">+${growth.toLocaleString()}</strong> ${hasClaimedBox ? '(可能含礼盒积分)' : ''}</p> `; // 深色模式需要动态颜色更新。 // Dark mode requires dynamic color update. if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { panelContent.querySelector('p[style]').style.color = 'var(--primary-text-dark)'; } obs.disconnect(); // 任务完成。就这么简单。 // Task complete. It's that simple. } }); observer.observe(pointsContainer, { childList: true, subtree: true }); GM_log('Observer started. Watching the points container.'); panelContent.innerHTML += '<p>正在自动处理签到...</p>'; GM_log('Clicking the check-in button.'); try { checkinButton.click(); } catch (error) { GM_log('Error clicking check-in: ' + error); panelContent.innerHTML += '<p class="error">签到点击失败,请手动尝试。</p>'; } } else { // 已签到,无需签到observer。但如果有礼盒,检查变化 // Already checked in, no observer needed. But if box, check for changes if (hasClaimedBox) { // 用setTimeout检查礼盒结果,并更新为单一显示 // Use setTimeout to check box result, update to single display setTimeout(() => { pointsElement = document.getElementById('spanTotalPoints'); const newPoints = getPoints(pointsElement); if (newPoints > initialPoints) { const growth = newPoints - initialPoints; // 更新面板为最新积分,避免重复 // Update panel to latest points, avoid duplicates panelContent.innerHTML = ` <p style="font-weight: 500; color: var(--primary-text-light);">✅ <strong>礼盒开启成功</strong></p> <p>初始积分: ${initialPoints.toLocaleString()}</p> <p>当前积分: <strong>${newPoints.toLocaleString()}</strong></p> <p>礼盒获取: <strong class="success">+${growth.toLocaleString()}</strong></p> `; if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { panelContent.querySelector('p[style]').style.color = 'var(--primary-text-dark)'; } } else { // 无变化,追加状态消息,不重复积分 // No change, append status only, no duplicate points panelContent.innerHTML += '<p>一切正常,无积分变化。</p>'; } }, 2000); // 等待2秒检查礼盒结果 } else { // 已签到 + 无礼盒:只追加状态,不重复积分 // Already checked in + no box: Append status only, no duplicate points panelContent.innerHTML += '<p>一切正常,今日任务已完成。</p>'; } } } // --- 初始化:一切从这里开始。安静、高效。 --- // --- Initialization: It all starts here. Quietly, efficiently. --- // 启动逻辑保持不变,确保在最佳时机无缝启动。新增礼盒不影响原流程。 // (The startup logic remains unchanged to ensure a seamless start at the optimal moment.) const panel = createPanel(); const readyCheckInterval = setInterval(() => { if (document.getElementById('spanTotalPoints') && document.getElementById('btncheckin')) { clearInterval(readyCheckInterval); runAutoClaimer(); } }, 500); setTimeout(() => { clearInterval(readyCheckInterval); }, 30000); // 慷慨的超时,以防万一。增加到30秒以覆盖礼盒延迟。 // A generous timeout, just in case. Increased to 30s for box delay. })();