// ==UserScript== // @name 原神激励领奖 // @namespace http://tampermonkey.net/ // @version 2025-06-08 // @description B站原神激励活动自动领奖脚本,支持自动解锁按钮、可调节点击间隔、智能停止检测,提供一键控制和优雅的消息提示系统 // @author You // @match https://www.bilibili.com/blackboard/new-award-exchange.html* // @icon https://www.google.com/s2/favicons?sz=64&domain=bilibili.com // @license MIT // @grant none // @downloadURL none // ==/UserScript== (function() { 'use strict'; let clickInterval = null; let isAutoClicking = false; let clickIntervalMs = 100; // Default click interval let timeUpdateInterval = null; // List of stop texts const stopTexts = ["查看奖励", "每日库存已达上限", "暂无领取资格"]; // Message container for stacking notifications let messageContainer = null; // Load saved click interval from localStorage function loadClickInterval() { const saved = localStorage.getItem('auto-click-interval'); if (saved && [100, 200, 500, 800, 1000].includes(parseInt(saved))) { clickIntervalMs = parseInt(saved); } } // Save click interval to localStorage function saveClickInterval(interval) { localStorage.setItem('auto-click-interval', interval.toString()); } // Function to create real-time clock display function createRealTimeClock() { // Create clock container const clockContainer = document.createElement('div'); clockContainer.id = 'real-time-clock'; clockContainer.style.cssText = ` position: fixed; top: 20px; left: 20px; z-index: 9999; background-color: rgba(0, 0, 0, 0.8); color: #00FF00; padding: 8px 12px; border-radius: 6px; font-family: 'Courier New', monospace; font-size: 14px; font-weight: bold; border: 1px solid #333; box-shadow: 0 2px 10px rgba(0,0,0,0.3); backdrop-filter: blur(5px); `; // Create time display element const timeDisplay = document.createElement('div'); timeDisplay.id = 'time-display'; timeDisplay.textContent = '加载中...'; clockContainer.appendChild(timeDisplay); // Create hidden iframe to load bjtime.net const iframe = document.createElement('iframe'); iframe.src = 'https://www.bjtime.net/'; iframe.style.cssText = ` position: absolute; top: -9999px; left: -9999px; width: 1px; height: 1px; border: none; opacity: 0; pointer-events: none; `; iframe.id = 'time-iframe'; document.body.appendChild(clockContainer); document.body.appendChild(iframe); // Handle iframe load iframe.onload = function() { try { startTimeUpdater(iframe, timeDisplay); } catch (error) { console.log('Failed to access iframe content, using local time'); startLocalTimeUpdater(timeDisplay); } }; // Fallback to local time if iframe fails to load iframe.onerror = function() { console.log('Failed to load bjtime.net, using local time'); startLocalTimeUpdater(timeDisplay); }; // Timeout fallback setTimeout(() => { if (timeDisplay.textContent === '加载中...') { console.log('Timeout loading bjtime.net, using local time'); startLocalTimeUpdater(timeDisplay); } }, 5000); } // Function to start time updater using iframe function startTimeUpdater(iframe, timeDisplay) { timeUpdateInterval = setInterval(() => { try { const iframeDoc = iframe.contentDocument || iframe.contentWindow.document; const timeElement = iframeDoc.querySelector('#cTime'); if (timeElement && timeElement.textContent) { timeDisplay.textContent = timeElement.textContent; } else { // Fallback to local time if can't find element updateLocalTime(timeDisplay); } } catch (error) { // Cross-origin error, fallback to local time updateLocalTime(timeDisplay); } }, 100); // Update every 100ms } // Function to start local time updater function startLocalTimeUpdater(timeDisplay) { timeUpdateInterval = setInterval(() => { updateLocalTime(timeDisplay); }, 100); } // Function to update with local time function updateLocalTime(timeDisplay) { const now = new Date(); const timeString = now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0') + '-' + String(now.getDate()).padStart(2, '0') + ' ' + String(now.getHours()).padStart(2, '0') + ':' + String(now.getMinutes()).padStart(2, '0') + ':' + String(now.getSeconds()).padStart(2, '0') + '.' + String(now.getMilliseconds()).padStart(3, '0'); timeDisplay.textContent = timeString; } // Function to create message container function createMessageContainer() { if (!messageContainer) { messageContainer = document.createElement('div'); messageContainer.id = 'message-container'; messageContainer.style.cssText = ` position: fixed; top: 70px; right: 20px; z-index: 10000; display: flex; flex-direction: column; gap: 10px; pointer-events: none; `; document.body.appendChild(messageContainer); } return messageContainer; } // Function to show non-blocking message notification function showMessage(message, type = 'info') { const container = createMessageContainer(); const notification = document.createElement('div'); notification.textContent = message; notification.style.cssText = ` padding: 12px 20px; background-color: ${type === 'error' ? '#FF6B6B' : type === 'success' ? '#51CF66' : '#339AF0'}; color: white; border-radius: 8px; font-size: 14px; font-weight: bold; box-shadow: 0 4px 12px rgba(0,0,0,0.3); max-width: 300px; word-wrap: break-word; transform: translateX(100%); opacity: 0; transition: all 0.3s ease-out; pointer-events: auto; `; container.appendChild(notification); // Trigger slide-in animation setTimeout(() => { notification.style.transform = 'translateX(0)'; notification.style.opacity = '1'; }, 10); // Auto remove after 3 seconds setTimeout(() => { notification.style.transform = 'translateX(100%)'; notification.style.opacity = '0'; setTimeout(() => { if (notification.parentNode) { notification.remove(); } }, 300); }, 3000); } // Function to remove disable class from button function removeDisableClass() { const button = document.querySelector("#app > div > div.home-wrap.select-disable > section.tool-wrap > div.button.exchange-button"); if (button) { button.classList.remove("disable"); console.log("Disable class removed from button"); } } // Function to start/stop auto clicking function toggleAutoClick() { const button = document.querySelector("#app > div > div.home-wrap.select-disable > section.tool-wrap > div.button.exchange-button"); if (isAutoClicking) { // Stop clicking if (clickInterval) { clearInterval(clickInterval); clickInterval = null; } isAutoClicking = false; updateControlButton(); showMessage("已停止自动领奖", "info"); return; } if (!button) { showMessage("找不到领奖按钮!", "error"); return; } // Start clicking isAutoClicking = true; updateControlButton(); showMessage(`开始自动领奖 (${clickIntervalMs}ms间隔)`, "success"); clickInterval = setInterval(() => { if (button) { // Check if button text is in stopTexts if (stopTexts.includes(button.textContent.trim())) { clearInterval(clickInterval); clickInterval = null; isAutoClicking = false; updateControlButton(); showMessage(`已停止领奖!原因:${button.textContent.trim()}`, "info"); return; } button.click(); } else { clearInterval(clickInterval); clickInterval = null; isAutoClicking = false; updateControlButton(); } }, clickIntervalMs); // Use selected interval // Stop clicking after 30 seconds for safety setTimeout(() => { if (clickInterval) { clearInterval(clickInterval); clickInterval = null; isAutoClicking = false; updateControlButton(); showMessage("自动领奖已超时停止", "info"); } }, 30000); } // Function to create and add control button function createControlButton() { const controlButton = document.createElement("button"); controlButton.id = "auto-click-control"; controlButton.textContent = "开始自动领奖"; controlButton.style.cssText = ` position: fixed; top: 20px; right: 20px; z-index: 9999; padding: 10px 15px; background-color: #00A1D6; color: white; border: none; border-radius: 5px; cursor: pointer; font-size: 14px; font-weight: bold; box-shadow: 0 2px 10px rgba(0,0,0,0.2); `; controlButton.addEventListener("click", toggleAutoClick); document.body.appendChild(controlButton); // Add hover effect controlButton.addEventListener("mouseenter", function() { this.style.backgroundColor = "#0088CC"; }); controlButton.addEventListener("mouseleave", function() { this.style.backgroundColor = isAutoClicking ? "#FF6B6B" : "#00A1D6"; }); } // Function to update control button text and style function updateControlButton() { const controlButton = document.getElementById("auto-click-control"); if (controlButton) { if (isAutoClicking) { controlButton.textContent = "停止自动领奖"; controlButton.style.backgroundColor = "#FF6B6B"; } else { controlButton.textContent = "开始自动领奖"; controlButton.style.backgroundColor = "#00A1D6"; } } } // Function to create interval selector function createIntervalSelector() { const selectorContainer = document.createElement("div"); selectorContainer.style.cssText = ` position: fixed; top: 60px; right: 20px; z-index: 9999; display: flex; align-items: center; gap: 8px; `; const label = document.createElement("label"); label.textContent = "间隔:"; label.style.cssText = ` color: #333; font-size: 12px; font-weight: bold; text-shadow: 1px 1px 2px rgba(255,255,255,0.8); `; const selector = document.createElement("select"); selector.id = "interval-selector"; selector.style.cssText = ` padding: 4px 8px; border: 1px solid #ccc; border-radius: 4px; background-color: white; font-size: 12px; cursor: pointer; box-shadow: 0 2px 4px rgba(0,0,0,0.1); `; // Add options const options = [ { value: 100, text: "100ms (极快)" }, { value: 200, text: "200ms (快)" }, { value: 500, text: "500ms (中)" }, { value: 800, text: "800ms (慢)" }, { value: 1000, text: "1000ms (很慢)" } ]; options.forEach(option => { const optionElement = document.createElement("option"); optionElement.value = option.value; optionElement.textContent = option.text; if (option.value === clickIntervalMs) { optionElement.selected = true; } selector.appendChild(optionElement); }); // Handle selection change selector.addEventListener("change", function() { clickIntervalMs = parseInt(this.value); saveClickInterval(clickIntervalMs); showMessage(`点击间隔已设置为 ${clickIntervalMs}ms`, "info"); // If currently auto clicking, restart with new interval if (isAutoClicking) { toggleAutoClick(); // Stop setTimeout(() => { toggleAutoClick(); // Start with new interval }, 100); } }); selectorContainer.appendChild(label); selectorContainer.appendChild(selector); document.body.appendChild(selectorContainer); } // Initialize when page loads function init() { // Wait for page to fully load setTimeout(() => { loadClickInterval(); // Load saved click interval removeDisableClass(); createControlButton(); createIntervalSelector(); createRealTimeClock(); console.log("Auto-click userscript initialized"); }, 1000); } // Run initialization when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } // Also remove disable class periodically in case it gets re-added setInterval(removeDisableClass, 2000); })();