您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Play ping sound when logged-in user is mentioned in chat
// ==UserScript== // @name Streamer Pings // @namespace http://tampermonkey.net/ // @version 1.0 // @description Play ping sound when logged-in user is mentioned in chat // @author You // @match https://fishtank.live/* // @match https://www.fishtank.live/* // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; console.log('[Streamer Pings] Plugin initialized'); const PING_SOUND_URL = 'https://cdn.fishtank.live/sounds/mention.mp3'; let currentUserName = null; let processedMessages = new Set(); // Track which messages we've already processed // Get the logged-in username function getLoggedInUsername() { const userElement = document.querySelector('.top-bar-user_display-name__bzlpw') || document.querySelector('.top-bar_username__nJaN2') || document.querySelector('[class*="display-name"]'); return userElement ? userElement.textContent.trim() : null; } // Play the ping sound function playPingSound() { try { const audio = new Audio(PING_SOUND_URL); audio.volume = 0.7; // Adjust volume as needed audio.play().catch(error => { console.log('[Streamer Pings] Error playing sound:', error); }); console.log('[Streamer Pings] Playing ping sound for mention'); } catch (error) { console.error('[Streamer Pings] Failed to create audio:', error); } } // Generate a unique ID for a message based on its content and timestamp function getMessageId(messageElement) { const messageText = messageElement.querySelector('.chat-message-default_body__iFlH4')?.textContent || ''; const username = messageElement.querySelector('.chat-message-default_user__uVNvH')?.textContent || ''; const timestamp = messageElement.querySelector('.chat-message-default_timestamp__YpVpB')?.textContent || ''; // Create a unique identifier combining username, message content, and timestamp return `${username}-${messageText.substring(0, 50)}-${timestamp}`; } // Check if a message mentions the current user function messageContainsPing(messageText, username) { if (!messageText || !username) return false; // Convert to lowercase for case-insensitive matching const lowerMessage = messageText.toLowerCase(); const lowerUsername = username.toLowerCase(); // Check for exact username match (word boundaries to avoid partial matches) const usernameRegex = new RegExp(`\\b${lowerUsername.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`, 'i'); return usernameRegex.test(messageText); } // Check for new messages that mention the user function checkForPings() { const username = getLoggedInUsername(); if (!username) return; // Update current username if it changed if (currentUserName !== username) { currentUserName = username; console.log('[Streamer Pings] Logged in user detected:', currentUserName); } // Get all chat messages const messages = document.querySelectorAll('.chat-message-default_chat-message-default__JtJQL'); messages.forEach(message => { const messageId = getMessageId(message); // Skip if we've already processed this message if (processedMessages.has(messageId)) return; // Mark this message as processed processedMessages.add(messageId); // Get the message text const messageTextElement = message.querySelector('.chat-message-default_body__iFlH4'); const messageAuthor = message.querySelector('.chat-message-default_user__uVNvH'); if (!messageTextElement || !messageAuthor) return; const messageText = messageTextElement.textContent; const author = messageAuthor.textContent.trim(); // Don't ping for our own messages if (author === currentUserName) return; // Check if this message mentions the current user if (messageContainsPing(messageText, currentUserName)) { console.log('[Streamer Pings] User mentioned in message by', author, ':', messageText); playPingSound(); } }); // Clean up old processed messages to prevent memory buildup // Keep only the last 1000 message IDs if (processedMessages.size > 1000) { const messagesArray = Array.from(processedMessages); const toKeep = messagesArray.slice(-500); // Keep last 500 processedMessages.clear(); toKeep.forEach(id => processedMessages.add(id)); } } // Start monitoring for pings function startPingMonitoring() { // Check every 500ms for new messages setInterval(checkForPings, 500); console.log('[Streamer Pings] Ping monitoring started'); } // Wait for the page to load then start monitoring if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', startPingMonitoring); } else { startPingMonitoring(); } // Also start after a short delay to ensure everything is loaded setTimeout(startPingMonitoring, 2000); })();