您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Replace text emotes with styled emote divs on vyneer.me vod chat (existing and new messages)
// ==UserScript== // @name Vyneer.me VODs Chat Emote Replacer (RainbowPls + pokiKick + melW + nathanW) // @namespace FishVernanda // @version 2025-07-08 // @description Replace text emotes with styled emote divs on vyneer.me vod chat (existing and new messages) // @author FishVernanda // @match https://vyneer.me/vods* // @grant GM_addStyle // ==/UserScript== // With thanks to yuniDev and intercropse shamefully lifted and edited PEPE (function() { 'use strict'; // Add emote styles GM_addStyle(` .emote.RainbowPls { width: 32px; height: 32px; background-image: url('https://wikicdn.destiny.gg/f/f4/GRainbowPls.png'); } .msg-chat .emote.RainbowPls { margin-top: -30px; top: 7.5px; } .emote.pokiKick { width: 61px; height: 32px; background-image: url('https://wikicdn.destiny.gg/b/b1/Pokikick.gif'); } .msg-chat .emote.pokiKick { margin-top: -32px; top: 8px; } .emote.nathanW { width: 28px; height: 28px; background-image: url('https://wikicdn.destiny.gg/e/ef/NathanW.png'); } .msg-chat .emote.nathanW { margin-top: -28px; top: 7px; } .emote.melW { width: 28px; height: 28px; background-image: url('https://wikicdn.destiny.gg/2/2e/MelWemote.png'); } .msg-chat .emote.melW { margin-top: -28px; top: 7px; } `); // Emote text to class map const emoteMap = { 'rainbowpls': 'RainbowPls', 'pokikick': 'pokiKick', 'nathanw': 'nathanW', 'melw': 'melW', }; function replaceTextWithEmotesInNode(textNode) { const parent = textNode.parentNode; const text = textNode.nodeValue; const pattern = new RegExp(`\\b(${Object.keys(emoteMap).join('|')})\\b`, 'gi'); if (!pattern.test(text)) return; const parts = text.split(pattern); parent.removeChild(textNode); for (const part of parts) { if (!part) continue; const key = part.toLowerCase(); if (emoteMap[key]) { const emoteDiv = document.createElement('div'); emoteDiv.className = 'emote ' + emoteMap[key]; emoteDiv.title = emoteMap[key]; parent.appendChild(emoteDiv); } else { parent.appendChild(document.createTextNode(part)); } } } function processMessageSpan(span) { const childNodes = Array.from(span.childNodes); for (const node of childNodes) { if (node.nodeType === Node.TEXT_NODE) { replaceTextWithEmotesInNode(node); } } } function processExistingMessages() { const chatMessages = document.querySelectorAll('#chat-stream .msg-chat .message'); chatMessages.forEach(processMessageSpan); } function setupObserver() { const chatContainer = document.querySelector('#chat-stream'); if (!chatContainer) { console.warn('Vyneer.me chat container (#chat-stream) not found'); return; } const observer = new MutationObserver((mutationsList) => { for (const mutation of mutationsList) { if (mutation.type === 'childList') { for (const addedNode of mutation.addedNodes) { if (addedNode.classList?.contains('msg-chat')) { const messageSpan = addedNode.querySelector('.message'); if (messageSpan) processMessageSpan(messageSpan); } } } } }); observer.observe(chatContainer, { childList: true }); } processExistingMessages(); setupObserver(); })();