Show a timer that shows the time left to post next message.
目前為
// ==UserScript==
// @name Trade Chat Timer on Button
// @namespace http://tampermonkey.net/
// @version 1.0
// @author Weav3r
// @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";
if (!Document.prototype.find)
Object.defineProperties(Document.prototype, {
find: {
value(selector) {
return document.querySelector(selector);
},
enumerable: false
},
findAll: {
value(selector) {
return document.querySelectorAll(selector);
},
enumerable: false
}
});
if (!Element.prototype.find)
Object.defineProperties(Element.prototype, {
find: {
value(selector) {
return this.querySelector(selector);
},
enumerable: false
},
findAll: {
value(selector) {
return this.querySelectorAll(selector);
},
enumerable: false
}
});
async function waitFor(sel, parent = document) {
return new Promise((resolve) => {
const intervalID = setInterval(() => {
const el = parent.find(sel);
if (el) {
resolve(el);
clearInterval(intervalID);
}
}, 500);
});
}
(async () => {
await waitFor("head style");
document.head.insertAdjacentHTML("beforeend", `<style>
#chatRoot [class*="minimized-menu-item__"][title="Trade"].time-left {
position: relative;
background-size: cover;
background-position: center;
}
#chatRoot [class*="minimized-menu-item__"][title="Trade"].time-complete {
position: relative;
background-size: cover;
background-position: center;
background-image: url('data:image/svg+xml;charset=UTF-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><rect x="5" y="5" width="90" height="90" stroke="green" stroke-width="10" fill="none"/></svg>');
}
</style>`);
let tradeChatButton = await waitFor("#chatRoot [class*='minimized-menu-item__'][title='Trade']");
let tradeChat;
if (tradeChatButton.className.includes("minimized-menu-item--open__")) {
tradeChat = await getTradeChat();
tradeChat.find("textarea").addEventListener("keydown", (e) => {
if (e.key === "Enter" && !isTimerActive()) {
window.localStorage.setItem(STORAGE_KEY, Date.now());
}
});
}
tradeChatButton.addEventListener("click", async () => {
if (!tradeChatButton.className.includes("minimized-menu-item--open__"))
tradeChat = await getTradeChat();
if (!tradeChat) return;
tradeChat.find("textarea").addEventListener("keydown", (e) => {
if (e.key === "Enter" && !isTimerActive()) {
window.localStorage.setItem(STORAGE_KEY, Date.now());
}
});
});
func();
setInterval(func, 1000);
function func() {
if (!window.localStorage.getItem(STORAGE_KEY))
window.localStorage.setItem(STORAGE_KEY, Date.now());
const timestamp = parseInt(window.localStorage.getItem(STORAGE_KEY));
if (timestamp > 0) {
const timeLeft = Math.floor(60 - ((Date.now() - timestamp) / 1000));
const button = tradeChatButton;
if (timeLeft > 0) {
button.classList.remove("time-complete");
button.classList.add("time-left");
const percentage = timeLeft / 60;
button.style.backgroundImage = `url('data:image/svg+xml;charset=UTF-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><rect x="5" y="5" width="90" height="90" stroke="red" stroke-width="10" fill="none" stroke-dasharray="360" stroke-dashoffset="${360 * (1 - percentage)}"/></svg>')`;
} else {
button.classList.add("time-complete");
button.classList.remove("time-left");
button.style.backgroundImage = `url('data:image/svg+xml;charset=UTF-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><rect x="5" y="5" width="90" height="90" stroke="green" stroke-width="10" fill="none"/></svg>')`;
}
}
}
function isTimerActive() {
const timestamp = parseInt(window.localStorage.getItem(STORAGE_KEY));
const timeLeft = Math.floor(60 - ((Date.now() - timestamp) / 1000));
return timeLeft > 0;
}
})();
async function getTradeChat() {
await waitFor("#chatRoot [class*='chat-box-header__']");
return [...document.findAll("#chatRoot [class*='chat-box-header__']")]
.filter(x => x.textContent === "Trade")
.map(x => x.closest("[class*='chat-box__']"))?.[0];
}