- // ==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();
- }
- })();