Notifies about your queue hits a certain threshold
目前為
// ==UserScript==
// @name DeepCo Queue Notifier
// @namespace https://deepco.app/
// @version 0.0.2
// @description Notifies about your queue hits a certain threshold
// @author NaN
// @match https://deepco.app/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=deepco.app
// @license MIT
// @grant GM_notification
// @grant GM_getValue
// @grant GM_setValue
// ==/UserScript==
(function () {
"use strict";
const DEFAULT_THRESHOLD = 0;
let currentObserver = null;
let notificationSent = false;
let lastElement = null;
let notifyThreshold = GM_getValue("notifyThreshold", DEFAULT_THRESHOLD);
function createSettingsButton() {
const button = document.createElement("button");
button.textContent = "⚙️ Queue Notifier";
button.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
z-index: 10000;
padding: 10px 15px;
background: #4a5568;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-family: system-ui, -apple-system, sans-serif;
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
transition: background 0.2s;
`;
button.onmouseover = () => (button.style.background = "#2d3748");
button.onmouseout = () => (button.style.background = "#4a5568");
button.onclick = showSettingsPrompt;
document.body.appendChild(button);
}
function showSettingsPrompt() {
const overlay = document.createElement("div");
overlay.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
z-index: 10001;
display: flex;
align-items: center;
justify-content: center;
`;
const modal = document.createElement("div");
modal.style.cssText = `
background: white;
padding: 30px;
border-radius: 12px;
box-shadow: 0 4px 16px rgba(0,0,0,0.3);
max-width: 400px;
font-family: system-ui, -apple-system, sans-serif;
`;
modal.innerHTML = `
<h2 style="margin: 0 0 15px 0; color: #2d3748; font-size: 20px;">
Queue Notifier Settings
</h2>
<p style="margin: 0 0 20px 0; color: #4a5568; font-size: 14px;">
Get notified when your queue reaches this number or below:
</p>
<input
type="number"
id="threshold-input"
value="${notifyThreshold}"
min="0"
style="
color: black;
width: 100%;
padding: 10px;
border: 2px solid #e2e8f0;
border-radius: 6px;
font-size: 16px;
box-sizing: border-box;
"
/>
<div style="margin-top: 20px; display: flex; gap: 10px;">
<button id="save-btn" style="
flex: 1;
padding: 10px;
background: #4299e1;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
">Save</button>
<button id="cancel-btn" style="
flex: 1;
padding: 10px;
background: #e2e8f0;
color: #2d3748;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
">Cancel</button>
</div>
`;
overlay.appendChild(modal);
document.body.appendChild(overlay);
const input = modal.querySelector("#threshold-input");
const saveBtn = modal.querySelector("#save-btn");
const cancelBtn = modal.querySelector("#cancel-btn");
input.focus();
input.select();
saveBtn.onclick = () => {
const value = parseInt(input.value);
if (!isNaN(value) && value >= 0) {
notifyThreshold = value;
GM_setValue("notifyThreshold", value);
notificationSent = false; // Reset notification state
console.log(`Threshold updated to: ${value}`);
overlay.remove();
} else {
alert("Please enter a valid number (0 or greater)");
}
};
cancelBtn.onclick = () => overlay.remove();
overlay.onclick = (e) => {
if (e.target === overlay) overlay.remove();
};
input.onkeypress = (e) => {
if (e.key === "Enter") saveBtn.click();
};
}
function checkQueueStatus() {
const queueStatusElm = document.getElementById("queue-status");
if (!queueStatusElm) return;
if (lastElement && lastElement !== queueStatusElm) {
console.log("Element was replaced, re-initializing observer");
initObserver(queueStatusElm);
}
const innerText = queueStatusElm.innerText.trim();
const queueCount = parseInt(innerText);
console.log("Queue status text:", innerText);
if (!isNaN(queueCount) && queueCount <= notifyThreshold) {
if (!notificationSent) {
console.log(
`Sending notification - queue is at ${queueCount} (threshold: ${notifyThreshold})`
);
GM_notification({
title: "DeepCo Queue Alert",
text: `Your queue has ${queueCount} item${queueCount === 1 ? "" : "s"} left!`,
timeout: 5000,
});
notificationSent = true;
}
} else if (queueCount > notifyThreshold) {
notificationSent = false;
}
}
function initObserver(element) {
if (currentObserver) {
currentObserver.disconnect();
}
lastElement = element;
currentObserver = new MutationObserver(checkQueueStatus);
currentObserver.observe(element, {
childList: true,
characterData: true,
subtree: true,
});
console.log("Observer attached to element");
}
function init() {
const queueStatusElm = document.getElementById("queue-status");
if (queueStatusElm) {
console.log("Found queue-status element");
initObserver(queueStatusElm);
checkQueueStatus();
}
}
// Wait for initial element
const checkInterval = setInterval(() => {
if (document.getElementById("queue-status")) {
clearInterval(checkInterval);
init();
}
}, 500);
// Create settings button once page loads
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", createSettingsButton);
} else {
createSettingsButton();
}
// Poll every 2 seconds to catch element replacements
setInterval(checkQueueStatus, 2000);
// Re-check when tab becomes visible
document.addEventListener("visibilitychange", () => {
if (!document.hidden) {
checkQueueStatus();
}
});
})();