您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Automatically detects your username, highlights messages, and plays a sound in the TorrentBD shoutbox when keywords are mentioned.
// ==UserScript== // @name TorrentBD Shoutbox Notifier // @version 1.3 // @description Automatically detects your username, highlights messages, and plays a sound in the TorrentBD shoutbox when keywords are mentioned. // @author 5ifty6ix // @namespace 5ifty6ix // @match https://*.torrentbd.com/* // @match https://*.torrentbd.net/* // @match https://*.torrentbd.org/* // @match https://*.torrentbd.me/* // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @license MIT // @run-at document-end // ==/UserScript== (function() { 'use strict'; let processedMessages = GM_getValue('TBD_processed_messages', []); const MAX_PROCESSED_MESSAGES = 200; function detectUsername() { const allRankElements = document.querySelectorAll('.tbdrank'); for (const rankElement of allRankElements) { if (!rankElement.closest('#shoutbox-container')) { if (rankElement && rankElement.firstChild && rankElement.firstChild.nodeType === Node.TEXT_NODE) { return rankElement.firstChild.nodeValue.trim(); } } } return 'Not Found'; } const settings = { username: GM_getValue('TBD_shout_username', detectUsername() || ''), keywords: GM_getValue('TBD_shout_keywords', []), soundVolume: GM_getValue('TBD_shout_soundVolume', 0.5), highlightColor: GM_getValue('TBD_shout_highlightColor', '#2E4636') }; function playSound() { if (settings.soundVolume < 0.01) return; try { const audio = new Audio("https://raw.githubusercontent.com/5ifty6ix/custom-sounds/refs/heads/main/new-notification-010-352755.mp3"); audio.volume = settings.soundVolume; audio.play(); } catch (e) { console.error('Shoutbox Notifier: Could not play custom sound.', e); } } function notifyUser() { if (!document.title.startsWith('(1)')) { document.title = '(1) ' + document.title; } playSound(); } function highlightShout(shoutElement) { shoutElement.style.backgroundColor = settings.highlightColor; shoutElement.style.borderLeft = '3px solid #14a76c'; shoutElement.style.paddingLeft = '5px'; } function checkShout(shoutElement) { if (!shoutElement || !shoutElement.id) return; const messageBody = shoutElement.querySelector('.shout-text'); if (!messageBody) return; const messageText = messageBody.textContent.toLowerCase(); const activeUsername = (settings.username && settings.username !== 'Not Found') ? [settings.username] : []; const allKeywords = [...activeUsername, ...settings.keywords] .filter(k => k && k.trim() !== '') .map(k => k.toLowerCase()); if (allKeywords.length === 0) return; let keywordFound = false; for (const keyword of allKeywords) { const escapeRegExp = (string) => string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const keywordRegex = new RegExp(`\\b${escapeRegExp(keyword)}\\b`, 'i'); if (keywordRegex.test(messageText)) { highlightShout(shoutElement); keywordFound = true; break; } } if (keywordFound) { if (!processedMessages.includes(shoutElement.id)) { notifyUser(); processedMessages.push(shoutElement.id); if (processedMessages.length > MAX_PROCESSED_MESSAGES) { processedMessages.shift(); } GM_setValue('TBD_processed_messages', processedMessages); } } } function createSettingsUI() { const uiWrapper = document.createElement('div'); uiWrapper.id = 'sbn-modal-wrapper'; uiWrapper.style.display = 'none'; uiWrapper.innerHTML = ` <div id="sbn-container"> <div id="sbn-header"> <h1>Shoutbox Notifier</h1> <p>Get notified when your username or any specific words appear in the Shoutbox</p> <button id="sbn-close-btn" title="Close">×</button> </div> <div id="sbn-content"> <div class="sbn-form-group"> <label>Username</label> <div id="sbn-username-display">Detecting...</div> </div> <div class="sbn-form-group"> <div class="sbn-label-row"> <label for="sbn-keywords">Other Keywords (One per line)</label> <button id="sbn-reset-keywords">Reset</button> </div> <textarea id="sbn-keywords" placeholder="Add other keywords here..."></textarea> </div> <div class="sbn-controls-row"> <div class="sbn-form-group"> <label>Highlight Colour</label> <div id="sbn-color-picker-wrapper"> <input type="color" id="sbn-color"> </div> </div> <div class="sbn-form-group"> <label for="sbn-volume">Notification Volume</label> <div class="sbn-volume-control"> <div id="sbn-volume-icon"></div> <input type="range" id="sbn-volume" min="0" max="100" step="1"> </div> </div> </div> </div> </div> `; document.body.appendChild(uiWrapper); const settingsButton = document.createElement('div'); settingsButton.id = 'sbn-settings-btn'; settingsButton.title = 'Shoutbox Notifier Settings'; settingsButton.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path fill-rule="evenodd" d="M11.078 2.25c-.917 0-1.699.663-1.85 1.567L9.05 4.889c-.02.12-.115.26-.297.348a7.493 7.493 0 0 0-.986.57c-.166.115-.334.126-.45.083L6.3 5.508a1.875 1.875 0 0 0-2.282.819l-.922 1.597a1.875 1.875 0 0 0 .432 2.385l.84.692c.095.078.17.229.154.43a7.598 7.598 0 0 0 0 1.139c.015.2-.059.352-.153.43l-.841.692a1.875 1.875 0 0 0-.432 2.385l.922 1.597a1.875 1.875 0 0 0 2.282.818l1.019-.382c.115-.043.283-.031.45.082.312.214.641.405.985.57.182.088.277.228.297.35l.178 1.071c.151.904.933 1.567 1.85 1.567h1.844c.916 0 1.699-.663 1.85-1.567l.178-1.072c.02-.12.114-.26.297-.349.344-.165.673-.356.985-.57.167-.114.335-.125.45-.082l1.02.382a1.875 1.875 0 0 0 2.28-.819l.923-1.597a1.875 1.875 0 0 0-.432-2.385l-.84-.692c-.095-.078-.17-.229-.154-.43a7.614 7.614 0 0 0 0-1.139c-.016-.2.059-.352.153-.43l.84-.692c.708-.582.891-1.59.433-2.385l-.922-1.597a1.875 1.875 0 0 0-2.282-.818l-1.02.382c-.114.043-.282.031-.449-.083a7.49 7.49 0 0 0-.985-.57c-.183-.087-.277-.227-.297-.348l-.179-1.072a1.875 1.875 0 0 0-1.85-1.567h-1.843ZM12 15.75a3.75 3.75 0 1 0 0-7.5 3.75 3.75 0 0 0 0 7.5Z" clip-rule="evenodd" /></svg>`; settingsButton.addEventListener('click', (e) => { e.stopPropagation(); uiWrapper.style.display = 'flex'; loadSettings(); }); const titleElement = document.querySelector('#shoutbox-container .content-title h6.left'); if (titleElement) { titleElement.style.display = 'flex'; titleElement.style.alignItems = 'center'; titleElement.appendChild(settingsButton); } const usernameDisplay = document.getElementById('sbn-username-display'); const keywordsInput = document.getElementById('sbn-keywords'); const resetKeywordsBtn = document.getElementById('sbn-reset-keywords'); const colorInput = document.getElementById('sbn-color'); const colorPickerWrapper = document.getElementById('sbn-color-picker-wrapper'); const volumeSlider = document.getElementById('sbn-volume'); const volumeIconContainer = document.getElementById('sbn-volume-icon'); const closeBtn = document.getElementById('sbn-close-btn'); function loadSettings() { const detectedUser = detectUsername(); settings.username = detectedUser; GM_setValue('TBD_shout_username', settings.username); usernameDisplay.textContent = settings.username; settings.keywords = GM_getValue('TBD_shout_keywords', []); settings.highlightColor = GM_getValue('TBD_shout_highlightColor', '#2E4636'); settings.soundVolume = GM_getValue('TBD_shout_soundVolume', 0.5); keywordsInput.value = settings.keywords.join('\n'); colorInput.value = settings.highlightColor; colorPickerWrapper.style.backgroundColor = settings.highlightColor; volumeSlider.value = settings.soundVolume * 100; updateVolumeIcon(); } const volumeIcons = { mute: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M13.5 4.06c0-1.336-1.616-2.005-2.56-1.06l-4.5 4.5H4.508c-1.141 0-2.318.664-2.66 1.905A9.76 9.76 0 0 0 1.5 12c0 .898.121 1.768.35 2.595.341 1.24 1.518 1.905 2.659 1.905h1.93l4.5 4.5c.945.945 2.561.276 2.561-1.06V4.06ZM17.78 9.22a.75.75 0 1 0-1.06 1.06L18.44 12l-1.72 1.72a.75.75 0 1 0 1.06 1.06l1.72-1.72 1.72 1.72a.75.75 0 1 0 1.06-1.06L20.56 12l1.72-1.72a.75.75 0 1 0-1.06-1.06l-1.72 1.72-1.72-1.72Z" /></svg>`, on: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M13.5 4.06c0-1.336-1.616-2.005-2.56-1.06l-4.5 4.5H4.508c-1.141 0-2.318.664-2.66 1.905A9.76 9.76 0 0 0 1.5 12c0 .898.121 1.768.35 2.595.341 1.24 1.518 1.905 2.659 1.905h1.93l4.5 4.5c.945.945 2.561.276 2.561-1.06V4.06ZM18.584 5.106a.75.75 0 0 1 1.06 0c3.808 3.807 3.808 9.98 0 13.788a.75.75 0 0 1-1.06-1.06 8.25 8.25 0 0 0 0-11.668.75.75 0 0 1 0-1.06Z" /><path d="M15.932 7.757a.75.75 0 0 1 1.061 0 6 6 0 0 1 0 8.486.75.75 0 0 1-1.06-1.061 4.5 4.5 0 0 0 0-6.364.75.75 0 0 1 0-1.06Z" /></svg>` }; function updateVolumeIcon() { const value = parseFloat(volumeSlider.value); if (value == 0) { volumeIconContainer.innerHTML = volumeIcons.mute; } else { volumeIconContainer.innerHTML = volumeIcons.on; } } closeBtn.addEventListener('click', () => { uiWrapper.style.display = 'none'; }); uiWrapper.addEventListener('click', (event) => { if (event.target.id === 'sbn-modal-wrapper') { uiWrapper.style.display = 'none'; } }); resetKeywordsBtn.addEventListener('click', () => { keywordsInput.value = ''; settings.keywords = []; GM_setValue('TBD_shout_keywords', []); }); keywordsInput.addEventListener('input', () => { settings.keywords = keywordsInput.value.split('\n').map(k => k.trim()).filter(k => k); GM_setValue('TBD_shout_keywords', settings.keywords); }); colorInput.addEventListener('input', () => { settings.highlightColor = colorInput.value; colorPickerWrapper.style.backgroundColor = colorInput.value; GM_setValue('TBD_shout_highlightColor', settings.highlightColor); }); volumeSlider.addEventListener('input', () => { settings.soundVolume = parseFloat(volumeSlider.value) / 100; GM_setValue('TBD_shout_soundVolume', settings.soundVolume); updateVolumeIcon(); }); loadSettings(); } GM_addStyle(` #sbn-modal-wrapper { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; position: fixed; z-index: 9999; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.6); display: flex; justify-content: center; align-items: center; backdrop-filter: blur(4px); } #sbn-container { background-color: #2c2c2c; color: #e0e0e0; border: 1px solid #4a4a4a; border-radius: 16px; width: 90%; max-width: 420px; box-shadow: 0 10px 30px rgba(0,0,0,0.5); } #sbn-header { padding: 24px; text-align: center; position: relative; } #sbn-header h1 { font-size: 26px; font-weight: 700; color: #fff; margin: 0 0 8px 0; } #sbn-header p { font-size: 14px; color: #a0a0a0; margin: 0; } #sbn-close-btn { position: absolute; top: 05px; right: 10px; border: none; background: none; color: #888; cursor: pointer; font-size: 24px; font-weight: bold; transition: color .2s; padding: 4px; line-height: 1; } #sbn-close-btn:hover { color: #fff; } #sbn-content { padding: 0 24px 24px 24px; } .sbn-form-group { margin-bottom: 24px; } .sbn-form-group:last-child { margin-bottom: 0; } #sbn-content label { display: block; margin-bottom: 8px; font-weight: 500; color: #CFCFCF; font-size: 13px; } .sbn-label-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; } #sbn-reset-keywords { background: none; border: none; color: #FF453A; font-size: 13px; font-weight: 500; cursor: pointer; padding: 0; } #sbn-reset-keywords:hover { text-decoration: underline; } #sbn-username-display { background-color: #1e1e1e; border: 1px solid #555; border-radius: 8px; color: #fff; padding: 12px 16px; font-size: 16px; min-height: 45px; box-sizing: border-box; } #sbn-keywords { width: 100%; height: 180px; resize: none; background-color: #1e1e1e; border: 1px solid #555; border-radius: 8px; color: #fff; padding: 10px; font-size: 14px; box-sizing: border-box; } #sbn-keywords:focus { outline: none; border-color: #888; } .sbn-controls-row { display: grid; grid-template-columns: 1fr 1.5fr; gap: 24px; } #sbn-color-picker-wrapper { position: relative; width: 100%; height: 44px; border: 1px solid #555; border-radius: 8px; overflow: hidden; } #sbn-color { position: absolute; top: -5px; left: -5px; width: calc(100% + 10px); height: calc(100% + 10px); border: none; padding: 0; cursor: pointer; } .sbn-volume-control { display: flex; align-items: center; gap: 12px; height: 44px; background-color: #1e1e1e; border: 1px solid #555; border-radius: 8px; padding: 0 20px; box-sizing: border-box; } #sbn-volume-icon { color: #a0a0a0; width: 24px; height: 24px; flex-shrink: 0; } #sbn-volume { -webkit-appearance: none; appearance: none; width: 100%; height: 6px; background: #4a4a4a; border-radius: 3px; outline: none; } #sbn-volume::-webkit-slider-runnable-track { background: #4a4a4a; border-radius: 3px; height: 7px; margin-right: -5px; margin-left: -5px; } #sbn-volume::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 18px; height: 18px; background: #d1d5db; border-radius: 50%; cursor: pointer; margin-top: -6px; } #sbn-volume::-moz-range-track { background: #4a4a4a; border-radius: 3px; height: 6px; } #sbn-volume::-moz-range-thumb { width: 18px; height: 18px; background: #d1d5db; border-radius: 50%; cursor: pointer; border: none; } #sbn-settings-btn { cursor: pointer; margin-left: 10px; display: inline-flex; align-items: center; color: #9e9e9e; transition: color .2s ease; } #sbn-settings-btn:hover { color: #fff; } #sbn-settings-btn svg { width: 20px; height: 20px; } `); window.addEventListener('load', () => { const observer = new MutationObserver((mutationsList) => { for (const mutation of mutationsList) { if (mutation.addedNodes.length) { mutation.addedNodes.forEach(node => { if (node.nodeType === 1 && node.classList.contains('shout-item')) { checkShout(node); } }); } } }); function startObserver() { const shoutbox = document.getElementById('shouts-container'); if (shoutbox) { shoutbox.querySelectorAll('.shout-item').forEach(checkShout); observer.observe(shoutbox, { childList: true }); } else { setTimeout(startObserver, 500); } } createSettingsUI(); startObserver(); }); })();