您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Show a timer that shows the time left to post next message.
当前为
// ==UserScript== // @name Trade Chat Timer on Button // @namespace http://tampermonkey.net/ // @version 2.0 // @description Show a timer that shows the time left to post next message. // @match https://www.torn.com/* // ==/UserScript== const STORAGE_KEY = "localStorage__Trade_Chat_Timer__Do_Not_Edit"; async function waitFor(selector, parent = document) { return new Promise(resolve => { const checkExist = () => { const el = parent.querySelector(selector); if (el) { resolve(el); } else { requestAnimationFrame(checkExist); } }; checkExist(); }); } function addStyle() { if (!document.head.querySelector("#trade-chat-timer-style")) { const style = document.createElement('style'); style.id = "trade-chat-timer-style"; style.textContent = ` #chatRoot [class*="minimized-menu-item__"][title="Trade"] { position: relative; } #chatRoot [class*="minimized-menu-item__"][title="Trade"] .timer-svg { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; } `; document.head.appendChild(style); } } function createTimerSvg() { const timerSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); timerSvg.setAttribute("viewBox", "0 0 100 100"); timerSvg.classList.add("timer-svg"); timerSvg.innerHTML = '<rect x="5" y="5" width="90" height="90" stroke-width="10" fill="none" />'; return timerSvg; } function updateTimerVisual(timerRect, timeLeft) { if (timeLeft > 0) { timerRect.setAttribute('stroke', 'red'); timerRect.setAttribute('stroke-dasharray', '360'); timerRect.setAttribute('stroke-dashoffset', 360 * (1 - timeLeft / 60000)); } else { timerRect.setAttribute('stroke', 'green'); timerRect.setAttribute('stroke-dasharray', 'none'); timerRect.setAttribute('stroke-dashoffset', '0'); } } function setTimer(timerRect, nextAllowedTime) { const now = new Date(); const timeUntil = Math.max(nextAllowedTime - now, 0); updateTimerVisual(timerRect, timeUntil); return timeUntil; } function resetTimer(timerRect, nextAllowedTime) { nextAllowedTime.setTime(Date.now() + 60000); localStorage.setItem(STORAGE_KEY, nextAllowedTime.toISOString()); setTimer(timerRect, nextAllowedTime); } async function checkForBlockMessage(chatBody) { const lastMessage = chatBody.lastElementChild; return lastMessage && lastMessage.classList.contains("chat-box-body__block-message-wrapper___JjbKr") && lastMessage.textContent.includes("Trade chat allows one message per 60 seconds"); } async function handleNewMessage(chat, timerRect, nextAllowedTime) { const chatBody = chat.querySelector("[class*='chat-box-body___']"); return new Promise(resolve => { const observer = new MutationObserver(async (mutations) => { const mutation = mutations.find(mutation => mutation.addedNodes.length); if (!mutation) return; observer.disconnect(); if (await checkForBlockMessage(chatBody)) { resolve(false); } else { resetTimer(timerRect, nextAllowedTime); resolve(true); } }); observer.observe(chatBody, { childList: true }); }); } function attachKeyUpListener(chat, timerRect, nextAllowedTime) { const textarea = chat.querySelector("textarea"); if (textarea) { textarea.addEventListener("keyup", async e => { if (e.key === "Enter") { await handleNewMessage(chat, timerRect, nextAllowedTime); } }); } } async function getTradeChat() { await waitFor("#chatRoot [class*='chat-box-header__']"); return [...document.querySelectorAll("#chatRoot [class*='chat-box-header__']")].find(x => x.textContent === "Trade")?.closest("[class*='chat-box__']"); } (async () => { addStyle(); const tradeChatButton = await waitFor("#chatRoot [class*='minimized-menu-item__'][title='Trade']"); let tradeChat = tradeChatButton.className.includes("minimized-menu-item--open__") ? await getTradeChat() : null; const timerSvg = createTimerSvg(); tradeChatButton.appendChild(timerSvg); const timerRect = timerSvg.querySelector('rect'); let nextAllowedTime = new Date(localStorage.getItem(STORAGE_KEY) || Date.now()); setTimer(timerRect, nextAllowedTime); let timerInterval = setInterval(() => setTimer(timerRect, nextAllowedTime), 1000); if (tradeChat) { attachKeyUpListener(tradeChat, timerRect, nextAllowedTime); } tradeChatButton.addEventListener("click", async () => { if (!tradeChatButton.className.includes("minimized-menu-item--open__")) { tradeChat = await getTradeChat(); } if (tradeChat) { attachKeyUpListener(tradeChat, timerRect, nextAllowedTime); } }); document.addEventListener("click", async e => { const specificButton = document.querySelector("button.chat-box-footer__send-icon-wrapper___fGx9E"); if (specificButton && specificButton.contains(e.target)) { await handleNewMessage(tradeChat, timerRect, nextAllowedTime); } }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址