您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Feed reminder overlay with 3 snooze modes (1hr @ hh:02, 10s, 24hr), sync, Open Sans, Snooze button, and visual mode indicators. Updated 1hr mode to resume 2 minutes past the hour. 🐾🐈🕒⏱️📅🧭🔕
// ==UserScript== // @name Feed Thomas Reminder // @namespace https://farmrpg.com/ // @version 2.8 // @description Feed reminder overlay with 3 snooze modes (1hr @ hh:02, 10s, 24hr), sync, Open Sans, Snooze button, and visual mode indicators. Updated 1hr mode to resume 2 minutes past the hour. 🐾🐈🕒⏱️📅🧭🔕 // @author Clientcoin // @match *://*/* // @icon https://farmrpg.com/favicon.ico // @grant GM_setValue // @grant GM_getValue // @grant GM_addValueChangeListener // @grant GM_removeValueChangeListener // @license Unlicense // @homepage https://farmrpg.com/ // @supportURL https://farmrpg.com/ // @run-at document-end // ==/UserScript== (async function () { 'use strict'; const FORCE_SHOW_NEXT_LOAD = false; const ID = 'feed-thomas-box'; const SCALE_KEY = 'feedThomasScale'; const STORAGE_KEY = 'feedThomasHiddenUntil'; const MODE_KEY = 'feedThomasMode'; const FORCE_KEY = 'feedThomasForceFlag'; let scale = parseFloat(localStorage.getItem(SCALE_KEY)) || 1.0; let mode = parseInt(await GM_getValue(MODE_KEY, 0), 10); let hiddenUntil = parseInt(await GM_getValue(STORAGE_KEY, 0), 10) || 0; let clockInterval = null; const fontLink = document.createElement('link'); fontLink.href = 'https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;700&display=swap'; fontLink.rel = 'stylesheet'; document.head.appendChild(fontLink); if (FORCE_SHOW_NEXT_LOAD) { await GM_setValue(FORCE_KEY, true); } const forceOverride = await GM_getValue(FORCE_KEY, false); if (forceOverride) { hiddenUntil = 0; await GM_setValue(FORCE_KEY, false); } function shouldShowBox() { return Date.now() >= hiddenUntil; } async function handleSnooze() { const now = new Date(); let next; if (mode === 1) { next = new Date(now); const remainder = 10 - (now.getSeconds() % 10); next.setSeconds(now.getSeconds() + remainder, 0); } else if (mode === 2) { next = new Date(now); next.setDate(now.getDate() + 1); } else { next = new Date(now); next.setMinutes(2, 0, 0); // set to next hour @ 02:00 next.setHours(next.getHours() + 1); } await GM_setValue(STORAGE_KEY, next.getTime()); } function createBox() { if (document.getElementById(ID)) return; const box = document.createElement('div'); box.id = ID; box.title = 'Click to snooze until next hour +2min, 10s or 24h depending on mode'; Object.assign(box.style, { position: 'fixed', top: '10px', right: '10px', zIndex: '999999', backgroundColor: 'blue', color: 'yellow', fontSize: `${scale}em`, padding: '10px', borderRadius: '5px', cursor: 'pointer', display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center', fontFamily: "'Open Sans', sans-serif", lineHeight: '1.1' }); const text = document.createElement('div'); text.textContent = 'Feed Thomas'; text.style.fontSize = '1em'; box.appendChild(text); const clock = document.createElement('div'); clock.style.fontSize = '0.8em'; clock.style.marginTop = '4px'; box.appendChild(clock); const date = document.createElement('div'); date.style.fontSize = '0.75em'; box.appendChild(date); updateClockAndDate(clock, date); clockInterval = setInterval(() => updateClockAndDate(clock, date), 1000); const controls = document.createElement('div'); controls.style.display = 'flex'; controls.style.gap = '6px'; controls.style.marginTop = '6px'; const minusBtn = document.createElement('button'); minusBtn.textContent = '-'; minusBtn.title = 'Decrease size'; styleControlButton(minusBtn); minusBtn.style.width = '28px'; minusBtn.onclick = (e) => { e.stopPropagation(); scale = Math.max(0.5, scale - 0.1); localStorage.setItem(SCALE_KEY, scale.toFixed(2)); document.getElementById(ID).style.fontSize = `${scale}em`; }; const plusBtn = document.createElement('button'); plusBtn.textContent = '+'; plusBtn.title = 'Increase size'; styleControlButton(plusBtn); plusBtn.style.width = '28px'; plusBtn.onclick = (e) => { e.stopPropagation(); scale = Math.min(3.0, scale + 0.1); localStorage.setItem(SCALE_KEY, scale.toFixed(2)); document.getElementById(ID).style.fontSize = `${scale}em`; }; controls.appendChild(minusBtn); controls.appendChild(plusBtn); box.appendChild(controls); const modeBtn = document.createElement('button'); updateModeButtonStyle(modeBtn, mode); modeBtn.title = 'Click to cycle reminder interval (1hr, 10s, 24hr)'; modeBtn.onclick = async (e) => { e.stopPropagation(); mode = (mode + 1) % 3; await GM_setValue(MODE_KEY, mode); }; box.appendChild(modeBtn); const snoozeBtn = document.createElement('button'); snoozeBtn.textContent = 'Snooze'; snoozeBtn.title = 'Manually snooze this reminder'; Object.assign(snoozeBtn.style, { marginTop: '4px', fontSize: '11px' }); styleControlButton(snoozeBtn); snoozeBtn.onclick = async (e) => { e.stopPropagation(); await handleSnooze(); }; box.appendChild(snoozeBtn); box.onclick = async () => { await handleSnooze(); }; document.body.appendChild(box); } function styleControlButton(btn) { btn.style.background = 'yellow'; btn.style.color = 'blue'; btn.style.border = 'none'; btn.style.cursor = 'pointer'; btn.style.padding = '2px 6px'; btn.style.fontWeight = 'bold'; btn.style.borderRadius = '4px'; btn.style.fontFamily = "'Open Sans', sans-serif"; } function updateModeButtonStyle(btn, modeValue) { const labels = ['Mode: 1hr', 'Mode: 10s', 'Mode: 24hr']; btn.textContent = labels[modeValue]; btn.style.marginTop = '6px'; btn.style.fontSize = '10px'; styleControlButton(btn); if (modeValue === 1) { btn.style.opacity = '0.6'; } else if (modeValue === 2) { btn.style.background = 'blue'; btn.style.color = 'yellow'; btn.style.opacity = '1.0'; } else { btn.style.opacity = '1.0'; btn.style.background = 'yellow'; btn.style.color = 'blue'; } } function updateClockAndDate(clockEl, dateEl) { const now = new Date(); const timeStr = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' }); const year = now.getFullYear(); const month = now.toLocaleString('default', { month: 'short' }); const day = String(now.getDate()).padStart(2, '0'); const weekday = now.toLocaleString('default', { weekday: 'short' }); const dateStr = `${year}-${month}-${day} (${weekday})`; clockEl.textContent = timeStr; dateEl.textContent = dateStr; } function checkAndShowBox() { const box = document.getElementById(ID); if (shouldShowBox()) { if (!box) createBox(); } else if (box) { clearInterval(clockInterval); box.remove(); } } GM_addValueChangeListener(STORAGE_KEY, (_, __, newValue) => { hiddenUntil = parseInt(newValue, 10); checkAndShowBox(); }); GM_addValueChangeListener(MODE_KEY, (_, __, newValue) => { mode = parseInt(newValue, 10); const box = document.getElementById(ID); if (box) { const btn = [...box.querySelectorAll('button')] .find(b => b.textContent.startsWith('Mode:')); if (btn) { updateModeButtonStyle(btn, mode); } } }); function init() { checkAndShowBox(); setInterval(checkAndShowBox, 2000); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();