您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Alert when chain timer drops below user-defined threshold, flash the screen red, and play an alarm sound.
当前为
// ==UserScript== // @name Torn City Chain Watch Alert // @namespace http://tampermonkey.net/ // @version 1.8 // @description Alert when chain timer drops below user-defined threshold, flash the screen red, and play an alarm sound. // Controls organized under "Chain Saver" with toggle functionality. // @author Codex234 // @match https://www.torn.com/* // ==/UserScript== (function() { 'use strict'; let previousStateBelowThreshold = false; let alertedForCurrentThreshold = false; let alertThresholdInSeconds = parseInt(localStorage.getItem('alertThreshold')) || 240; // Default to 4 minutes let opacity = parseFloat(localStorage.getItem('flashOpacity')) || 0.8; // Default opacity let audioSrc = localStorage.getItem('alarmSound') || 'https://www.soundjay.com/transportation/sounds/train-crossing-bell-01.mp3'; // Default sound let volumeLevel = localStorage.getItem('volumeLevel') || 'Medium'; // Default volume level let audioElement; let alarmInterval; // Function to create the main dropdown container with subcategories function createDropdownsContainer() { const container = document.createElement('div'); container.id = 'dropdownsContainer'; container.style.position = 'fixed'; container.style.top = '10px'; container.style.right = '10px'; container.style.zIndex = '10000'; // Ensure it's above other page content container.style.backgroundColor = 'rgba(255, 255, 255, 0.95)'; // Slightly more opaque for better readability container.style.padding = '8px'; // Reduced padding container.style.border = '1px solid #ccc'; container.style.borderRadius = '8px'; container.style.boxShadow = '0 0 10px rgba(0,0,0,0.5)'; container.style.fontFamily = 'Arial, sans-serif'; container.style.fontSize = '12px'; // Reduced font size container.style.width = '180px'; // Adjusted width for minimized view container.style.height = '50px'; // Set to fixed height when minimized // Main Title with toggle functionality const title = document.createElement('h3'); title.textContent = 'Chain Saver'; title.style.marginTop = '0'; title.style.marginBottom = '5px'; // Reduced margin title.style.fontSize = '14px'; // Slightly smaller font size title.style.textAlign = 'center'; title.style.cursor = 'pointer'; // Indicate clickable title container.appendChild(title); // Event listener to toggle the menu title.addEventListener('click', () => { const contents = container.querySelectorAll('.dropdownContent'); const isVisible = contents[0].style.display !== 'none'; // Check if contents are currently visible contents.forEach(content => { content.style.display = isVisible ? 'none' : 'block'; // Toggle display }); container.style.height = isVisible ? '50px' : 'auto'; // Adjust height when minimized }); // Create a wrapper for the dropdown contents const dropdownContents = document.createElement('div'); dropdownContents.className = 'dropdownContent'; // Class for toggling visibility dropdownContents.style.display = 'none'; // Start hidden container.appendChild(dropdownContents); // Alarm Sound Subcategory const alarmSoundDiv = document.createElement('div'); alarmSoundDiv.id = 'alarmSoundDiv'; alarmSoundDiv.style.marginBottom = '10px'; dropdownContents.appendChild(alarmSoundDiv); const alarmSoundLabel = document.createElement('label'); alarmSoundLabel.htmlFor = 'soundDropdown'; alarmSoundLabel.textContent = 'Alarm Sound:'; alarmSoundLabel.style.display = 'block'; alarmSoundLabel.style.marginBottom = '5px'; alarmSoundDiv.appendChild(alarmSoundLabel); const soundDropdown = document.createElement('select'); soundDropdown.id = 'soundDropdown'; soundDropdown.style.width = '100%'; alarmSoundDiv.appendChild(soundDropdown); // Timer Subcategory const timerDiv = document.createElement('div'); timerDiv.id = 'timerDiv'; timerDiv.style.marginBottom = '10px'; dropdownContents.appendChild(timerDiv); const timerLabel = document.createElement('label'); timerLabel.htmlFor = 'chainTimerDropdown'; timerLabel.textContent = 'Timer:'; timerLabel.style.display = 'block'; timerLabel.style.marginBottom = '5px'; timerDiv.appendChild(timerLabel); const timerDropdown = document.createElement('select'); timerDropdown.id = 'chainTimerDropdown'; timerDropdown.style.width = '100%'; timerDiv.appendChild(timerDropdown); // Volume Control Subcategory const volumeDiv = document.createElement('div'); volumeDiv.id = 'volumeDiv'; volumeDiv.style.marginBottom = '10px'; dropdownContents.appendChild(volumeDiv); const volumeLabel = document.createElement('label'); volumeLabel.htmlFor = 'volumeDropdown'; volumeLabel.textContent = 'Volume Control:'; volumeLabel.style.display = 'block'; volumeLabel.style.marginBottom = '5px'; volumeDiv.appendChild(volumeLabel); const volumeDropdown = document.createElement('select'); volumeDropdown.id = 'volumeDropdown'; volumeDropdown.style.width = '100%'; volumeDiv.appendChild(volumeDropdown); // Opacity Control Subcategory const opacityDiv = document.createElement('div'); opacityDiv.id = 'opacityDiv'; opacityDiv.style.marginBottom = '10px'; dropdownContents.appendChild(opacityDiv); const opacityLabel = document.createElement('label'); opacityLabel.htmlFor = 'opacityDropdown'; opacityLabel.textContent = 'Flash Opacity:'; opacityLabel.style.display = 'block'; opacityLabel.style.marginBottom = '5px'; opacityDiv.appendChild(opacityLabel); const opacityDropdown = document.createElement('select'); opacityDropdown.id = 'opacityDropdown'; opacityDropdown.style.width = '100%'; opacityDiv.appendChild(opacityDropdown); // Append the container to the body document.body.appendChild(container); // Initialize the dropdowns initializeSoundDropdown(); initializeTimerDropdown(); initializeVolumeDropdown(); initializeOpacityDropdown(); } // Function to initialize the Alarm Sound dropdown function initializeSoundDropdown() { const dropdown = document.getElementById('soundDropdown'); const sounds = [ { label: 'Train Bell', src: 'https://www.soundjay.com/transportation/sounds/train-crossing-bell-01.mp3' }, { label: 'Smoke Alarm', src: 'https://www.soundjay.com/mechanical/sounds/smoke-detector-1.mp3' }, { label: 'Car Alarm', src: 'https://www.soundjay.com/transportation/sounds/car-alarm-1.mp3' }, // { label: 'Custom...', src: 'custom' } // Uncomment if you implement custom URLs ]; sounds.forEach(sound => { const option = document.createElement('option'); option.value = sound.src; option.textContent = sound.label; dropdown.appendChild(option); }); dropdown.value = audioSrc; dropdown.addEventListener('change', (e) => { audioSrc = e.target.value; localStorage.setItem('alarmSound', audioSrc); if (audioElement) { audioElement.src = audioSrc; audioElement.load(); // Ensure the new source is loaded console.log(`Alarm sound changed to: ${audioSrc}`); } }); } // Function to initialize the Timer dropdown function initializeTimerDropdown() { const dropdown = document.getElementById('chainTimerDropdown'); const timerOptions = [ { value: -1, text: 'Off' }, { value: 30, text: formatTime(30) }, { value: 60, text: formatTime(60) }, { value: 90, text: formatTime(90) }, { value: 120, text: formatTime(120) }, { value: 150, text: formatTime(150) }, { value: 180, text: formatTime(180) }, { value: 210, text: formatTime(210) }, { value: 240, text: formatTime(240) }, { value: 270, text: formatTime(270) }, { value: 297, text: formatTime(297) } ]; timerOptions.forEach(optionData => { const option = document.createElement('option'); option.value = optionData.value; option.textContent = optionData.text; dropdown.appendChild(option); }); dropdown.value = alertThresholdInSeconds; dropdown.addEventListener('change', (e) => { alertThresholdInSeconds = parseInt(e.target.value); localStorage.setItem('alertThreshold', alertThresholdInSeconds); alertedForCurrentThreshold = false; // Reset the alert state when threshold changes clearInterval(alarmInterval); // Clear any existing alarm interval console.log(`Alert threshold set to: ${formatTime(alertThresholdInSeconds)}`); }); } // Function to initialize the Volume Control dropdown function initializeVolumeDropdown() { const dropdown = document.getElementById('volumeDropdown'); const volumeOptions = [ { value: 'Silent', text: 'Silent' }, { value: 'Soft', text: 'Soft' }, { value: 'Medium', text: 'Medium' }, { value: 'Loud', text: 'Loud' } ]; volumeOptions.forEach(optionData => { const option = document.createElement('option'); option.value = optionData.value; option.textContent = optionData.text; dropdown.appendChild(option); }); dropdown.value = volumeLevel; dropdown.addEventListener('change', (e) => { volumeLevel = e.target.value; localStorage.setItem('volumeLevel', volumeLevel); setAudioVolume(); console.log(`Volume level set to: ${volumeLevel}`); }); // Set initial volume setAudioVolume(); } // Function to initialize the Opacity Control dropdown function initializeOpacityDropdown() { const dropdown = document.getElementById('opacityDropdown'); const opacityOptions = [ { value: '0.0', text: 'OFF' }, { value: '0.4', text: '10%' }, { value: '0.6', text: '20%' }, { value: '0.8', text: '30%' }, { value: '1.0', text: '40%' }, { value: '0.2', text: '50%' }, { value: '0.4', text: '60%' }, { value: '0.6', text: '70%' }, { value: '0.8', text: '80%' }, { value: '1.0', text: '100%' } ]; opacityOptions.forEach(optionData => { const option = document.createElement('option'); option.value = optionData.value; option.textContent = optionData.text; dropdown.appendChild(option); }); dropdown.value = opacity.toString(); dropdown.addEventListener('change', (e) => { opacity = parseFloat(e.target.value); localStorage.setItem('flashOpacity', opacity); console.log(`Flash opacity set to: ${opacity * 100}%`); }); } // Function to set the audio volume based on the selected volume level function setAudioVolume() { const volumeMap = { 'Silent': 0, 'Soft': 0.2, 'Medium': 0.6, 'Loud': 1.0 }; const volume = volumeMap[volumeLevel] !== undefined ? volumeMap[volumeLevel] : 0.6; // Default to Medium if undefined if (audioElement) { audioElement.volume = volume; console.log(`Audio volume set to: ${volume}`); } } // Function to format time from seconds to MM:SS function formatTime(seconds) { const minutes = Math.floor(seconds / 60); const remainingSeconds = seconds % 60; return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`; } // Function to check the chain timer on the page function checkChainTimer() { const timerElement = document.querySelector('.bar-timeleft___B9RGV'); if (timerElement) { const timerText = timerElement.textContent.trim(); const timeParts = timerText.split(':'); const minutes = parseInt(timeParts[0], 10); const seconds = parseInt(timeParts[1], 10); const totalTimeInSeconds = (minutes * 60) + seconds; if (alertThresholdInSeconds !== -1 && totalTimeInSeconds < alertThresholdInSeconds) { if (!alertedForCurrentThreshold) { console.log(`Chain timer is below ${formatTime(alertThresholdInSeconds)}!`); startAlarm(); alertedForCurrentThreshold = true; } flashScreenRed(); previousStateBelowThreshold = true; } else if (totalTimeInSeconds >= alertThresholdInSeconds) { stopAlarm(); previousStateBelowThreshold = false; alertedForCurrentThreshold = false; // Reset the alert state when timer goes back above threshold } } } // Function to flash the screen red function flashScreenRed() { let flashDiv = document.getElementById('flashDiv'); // Check if the flashDiv exists; if not, create it if (!flashDiv) { flashDiv = document.createElement('div'); flashDiv.id = 'flashDiv'; flashDiv.style.position = 'fixed'; flashDiv.style.top = '0'; flashDiv.style.left = '0'; flashDiv.style.width = '100vw'; flashDiv.style.height = '100vh'; flashDiv.style.backgroundColor = 'red'; // Red background flashDiv.style.pointerEvents = 'none'; // Prevent interaction blocking flashDiv.style.zIndex = '9999'; // High zIndex to be visible flashDiv.style.transition = 'opacity 0.5s ease-in-out'; // Smooth transition for fading flashDiv.style.opacity = '0'; // Initially transparent document.body.appendChild(flashDiv); } // Set the flash opacity to the user-defined level and fade in flashDiv.style.opacity = opacity.toString(); // Fade in to intensity level console.log(`Flashing screen with opacity: ${opacity}`); // Wait for 0.5 seconds at full opacity before fading out setTimeout(() => { flashDiv.style.opacity = '0'; // Fade out to transparent }, 1000); // The flashDiv remains in the DOM to be reused for subsequent flashes } // Function to start the alarm sound function startAlarm() { if (!audioElement) { audioElement = document.createElement('audio'); audioElement.src = audioSrc; audioElement.loop = false; audioElement.volume = getVolumeFromLevel(volumeLevel); // Set initial volume // audioElement.crossOrigin = "anonymous"; // Removed to fix playback issues document.body.appendChild(audioElement); console.log(`Audio element created with source: ${audioSrc}`); } audioElement.play().then(() => { console.log('Alarm sound is playing.'); }).catch(error => { console.error('Error playing audio:', error); }); alarmInterval = setInterval(() => { if (audioElement.paused) { // Only play if not already playing audioElement.currentTime = 0; audioElement.play().then(() => { console.log('Alarm sound replayed.'); }).catch(error => { console.error('Error replaying audio:', error); }); } }, 500); // Play alarm sound every 0.5 seconds } // Function to stop the alarm sound function stopAlarm() { clearInterval(alarmInterval); if (audioElement) { audioElement.pause(); audioElement.currentTime = 0; console.log('Alarm sound stopped.'); } } // Helper function to get volume level from volume label function getVolumeFromLevel(level) { const volumeMap = { 'Silent': 0, 'Soft': 0.2, 'Medium': 0.6, 'Loud': 1.0 }; return volumeMap[level] !== undefined ? volumeMap[level] : 0.6; // Default to Medium if undefined } // Initialize the dropdowns and start the timer check interval createDropdownsContainer(); setInterval(checkChainTimer, 1800); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址