您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Automatically switches theme on unrealsoftware.de based on browser's color scheme preference
// ==UserScript== // @name UnrealSoftware Theme Switcher // @version 1.0.0 // @description Automatically switches theme on unrealsoftware.de based on browser's color scheme preference // @author Adrian Gajos // @match https://unrealsoftware.de/* // @license MIT // @namespace https://greasyfork.org/users/1486099 // ==/UserScript== (function() { 'use strict'; // Configuration const CONFIG = { SELECTORS: { SAH_LINK: 'a.js-tt[href*="sah="]' }, ENDPOINTS: { SETTINGS: 'https://unrealsoftware.de/settings.php' }, COOKIES: { LANGUAGE: 'language' }, THEMES: { DARK: '', BRIGHT: 'bright' }, TIMEOUT: 5000 // 5 seconds timeout for fetch requests }; // Logging utility const Logger = { info: (message, ...args) => console.info(`[UnrealSW Theme]: ${message}`, ...args), warn: (message, ...args) => console.warn(`[UnrealSW Theme]: ${message}`, ...args), error: (message, ...args) => console.error(`[UnrealSW Theme]: ${message}`, ...args) }; /** * Extracts the SAH (security hash) parameter from the page * @returns {string|null} The SAH parameter or null if not found */ function extractSecurityHash() { try { const sahElement = document.querySelector(CONFIG.SELECTORS.SAH_LINK); if (!sahElement) { Logger.warn('Security hash element not found'); return null; } const href = sahElement.getAttribute('href'); if (!href) { Logger.warn('No href attribute found on security hash element'); return null; } const match = href.match(/[?&]sah=([^&]+)/); if (!match || !match[1]) { Logger.warn('Security hash parameter not found in URL:', href); return null; } return match[1]; } catch (error) { Logger.error('Error extracting security hash:', error); return null; } } /** * Retrieves a cookie value by name * @param {string} name - Cookie name * @returns {string|null} Cookie value or null if not found */ function getCookieValue(name) { try { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) { return parts.pop().split(';').shift(); } return null; } catch (error) { Logger.error(`Error retrieving cookie '${name}':`, error); return null; } } /** * Determines if the browser prefers dark mode * @returns {boolean} True if dark mode is preferred */ function prefersDarkMode() { return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; } /** * Checks if the current theme matches the browser preference * @returns {boolean} True if theme is already correct */ function isThemeCorrect() { const isDarkPreferred = prefersDarkMode(); const languageCookie = getCookieValue(CONFIG.COOKIES.LANGUAGE) || ''; const isBrightTheme = languageCookie.includes(CONFIG.THEMES.BRIGHT); // Theme is correct if: // - Dark mode preferred AND not using bright theme // - Light mode preferred AND using bright theme return (isDarkPreferred && !isBrightTheme) || (!isDarkPreferred && isBrightTheme); } /** * Creates a fetch request with timeout * @param {string} url - Request URL * @param {Object} options - Fetch options * @param {number} timeout - Timeout in milliseconds * @returns {Promise} Fetch promise with timeout */ function fetchWithTimeout(url, options = {}, timeout = CONFIG.TIMEOUT) { return Promise.race([ fetch(url, options), new Promise((_, reject) => setTimeout(() => reject(new Error('Request timeout')), timeout) ) ]); } /** * Switches the theme by calling the settings endpoint * @param {string} securityHash - The SAH parameter * @returns {Promise<boolean>} Success status */ async function switchTheme(securityHash) { try { const isDarkPreferred = prefersDarkMode(); const themeParam = isDarkPreferred ? CONFIG.THEMES.DARK : CONFIG.THEMES.BRIGHT; const themeName = isDarkPreferred ? 'dark' : 'bright'; const url = new URL(CONFIG.ENDPOINTS.SETTINGS); url.searchParams.set('sah', securityHash); url.searchParams.set('set_style', themeParam); Logger.info(`Switching to ${themeName} theme...`); const response = await fetchWithTimeout(url.toString(), { method: 'GET', credentials: 'include', headers: { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Cache-Control': 'no-cache' } }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } Logger.info(`Theme successfully switched to ${themeName}. Refreshing page...`); return true; } catch (error) { Logger.error('Failed to switch theme:', error); return false; } } /** * Main execution function */ async function main() { try { // Check if theme adjustment is needed if (isThemeCorrect()) { Logger.info('Theme is already correct, no action needed'); return; } // Extract security hash const securityHash = extractSecurityHash(); if (!securityHash) { Logger.error('Cannot proceed without security hash'); return; } // Switch theme const success = await switchTheme(securityHash); if (success) { // Add a small delay to ensure the server processes the request setTimeout(() => { window.location.reload(); }, 100); } } catch (error) { Logger.error('Unexpected error in main execution:', error); } } // Wait for DOM to be ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', main); } else { // DOM is already ready, execute immediately main(); } // Optional: Listen for theme changes during the session if (window.matchMedia) { const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); mediaQuery.addEventListener('change', () => { Logger.info('System theme changed, checking if adjustment needed...'); setTimeout(main, 500); // Small delay to ensure page is stable }); } })();