// ==UserScript== // @name 猫咪番茄钟 // @namespace http://tampermonkey.net/ // @version 1.0 // @description 用AI写的脚本,设置工作时间和休息时间,当网页在前端超过一定时间之后,会有一只大肥猫提醒你该休息了!参照Cat Gatekeeper扩展写的。 // @author chudaxiao // @match *://*/* // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_getValue // @run-at document-start // ==/UserScript== (function() { 'use strict'; // ================= 1. 数据存储与初始化 ================= // 获取当前域名作为唯一标识 const currentHost = window.location.hostname || 'unknown'; // 默认配置 const DEFAULTS = { workTime: 9999, // X: 工作分钟 (默认极大值,不触发) breakTime: 0, // Y: 休息分钟 (默认0) neko1Url: 'https://cropgif.net/videos/1777425080910-63851dd0-133f-407e-b2e8-2b66edb83960.webm', neko2Url: 'https://cropgif.net/videos/1777425132313-1392d9f3-7b04-4a7b-a8db-f2ba1c170c14.webm' }; // 全局动画链接配置 (所有网址共用一套动画链接,保存在 'global_neko1' 等键中) let GLOBAL_CONFIG = { neko1Url: GM_getValue('global_neko1', DEFAULTS.neko1Url), neko2Url: GM_getValue('global_neko2', DEFAULTS.neko2Url) }; // 当前网址的特定配置 (从数据库读取,如果没有则使用默认值) let siteConfig = GM_getValue(`config_${currentHost}`, { workTime: DEFAULTS.workTime, breakTime: DEFAULTS.breakTime }); // 当前网址的运行状态 (是否已启动) let isRunning = GM_getValue(`status_${currentHost}`, false); // 运行变量 let activeWatchTime = 0; let timerInterval = null; let isShowing = false; let countdownTimer = null; // ================= 2. 界面元素 ================= const overlay = document.createElement('div'); overlay.style.cssText = `position:fixed!important;top:0!important;left:0!important;width:100%!important;height:100%!important;background:rgba(0,0,0,0.5)!important;z-index:2147483647!important;display:none;justify-content:center;align-items:center;`; const timeDisplay = document.createElement('div'); timeDisplay.style.cssText = `position:absolute;top:0;left:0;width:50%;height:50%;display:flex;justify-content:center;align-items:center;font-size:48px;font-weight:bold;color:white;text-shadow:2px 2px 4px #000;z-index:2147483648;pointer-events:none;`; function createVideo() { const v = document.createElement('video'); v.style.cssText = `position:absolute;top:0;left:0;width:100%;height:100%;max-width:100%;max-height:100%;object-fit:contain;opacity:0;transition:opacity 0.2s;`; v.loop = true; v.muted = true; v.playsInline = true; v.preload = 'auto'; return v; } const video1 = createVideo(); const video2 = createVideo(); overlay.appendChild(video1); overlay.appendChild(video2); overlay.appendChild(timeDisplay); document.body.appendChild(overlay); // ================= 3. 菜单逻辑 ================= // 辅助:保存当前网址配置 function saveSiteConfig() { GM_setValue(`config_${currentHost}`, siteConfig); } // 辅助:切换视频 function switchVideo(index, url) { const activeV = index === 1 ? video1 : video2; const bgV = index === 1 ? video2 : video1; activeV.style.zIndex = 10; activeV.style.opacity = 1; bgV.style.zIndex = 1; bgV.style.opacity = 0; activeV.pause(); activeV.currentTime = 0; activeV.src = url; activeV.play().catch(()=>{}); const nextIdx = index === 1 ? 2 : 1; const nextUrl = nextIdx === 1 ? GLOBAL_CONFIG.neko1Url : GLOBAL_CONFIG.neko2Url; const nextV = nextIdx === 1 ? video1 : video2; if (nextV.src !== nextUrl) { nextV.src = nextUrl; nextV.load(); } } // 菜单项 1: 立即测试 (1分钟倒计时) GM_registerMenuCommand('⚡ 立即测试 (1分钟)', () => { if (isShowing) return; isShowing = true; overlay.style.display = 'flex'; switchVideo(1, GLOBAL_CONFIG.neko1Url); let totalSec = 60; // 固定1分钟 let elapsed = 0; const fmt = (s) => `${Math.floor(s/60).toString().padStart(2,'0')}:${(s%60).toString().padStart(2,'0')}`; timeDisplay.innerText = fmt(totalSec); countdownTimer = setInterval(() => { elapsed++; totalSec--; timeDisplay.innerText = fmt(totalSec); if (elapsed === 11) switchVideo(2, GLOBAL_CONFIG.neko2Url); if (totalSec <= 0) { clearInterval(countdownTimer); overlay.style.display = 'none'; video1.pause(); video2.pause(); isShowing = false; } }, 1000); }); // 菜单项 2: 启动猫咪番茄 GM_registerMenuCommand('▶️ 启动猫咪番茄', () => { // 弹窗设置当前网址的时间 const wTime = prompt(`设置 [${currentHost}] 的工作时间 (分钟):`, siteConfig.workTime); if (wTime !== null && !isNaN(wTime)) { siteConfig.workTime = Number(wTime); const bTime = prompt(`设置 [${currentHost}] 的休息时间 (分钟):`, siteConfig.breakTime); if (bTime !== null && !isNaN(bTime)) { siteConfig.breakTime = Number(bTime); saveSiteConfig(); // 保存到数据库 // 启动状态 isRunning = true; GM_setValue(`status_${currentHost}`, true); alert(`已启动!\n工作: ${siteConfig.workTime} 分钟\n休息: ${siteConfig.breakTime} 分钟`); } } }); // 菜单项 3: 停止猫咪番茄 GM_registerMenuCommand('⏹️ 停止猫咪番茄', () => { isRunning = false; GM_setValue(`status_${currentHost}`, false); activeWatchTime = 0; // 重置计时 alert(`已停止 [${currentHost}] 的计时。`); }); // 菜单项 4: 设置动画1 GM_registerMenuCommand('🖼️ 设置动画1', () => { const val = prompt("设置 neko1 链接:", GLOBAL_CONFIG.neko1Url); if (val !== null) { GLOBAL_CONFIG.neko1Url = val; GM_setValue('global_neko1', val); alert("动画1链接已更新"); } }); // 菜单项 5: 设置动画2 GM_registerMenuCommand('🖼️ 设置动画2', () => { const val = prompt("设置 neko2 链接:", GLOBAL_CONFIG.neko2Url); if (val !== null) { GLOBAL_CONFIG.neko2Url = val; GM_setValue('global_neko2', val); alert("动画2链接已更新"); } }); // ================= 4. 核心逻辑 ================= function triggerShow() { if (isShowing) return; isShowing = true; overlay.style.display = 'flex'; // 使用休息时间 Y 进行倒计时 let totalSec = siteConfig.breakTime * 60; if (totalSec <= 0) totalSec = 60; // 防止设置为0时不显示,默认给60秒 let elapsed = 0; const fmt = (s) => `${Math.floor(s/60).toString().padStart(2,'0')}:${(s%60).toString().padStart(2,'0')}`; timeDisplay.innerText = fmt(totalSec); switchVideo(1, GLOBAL_CONFIG.neko1Url); countdownTimer = setInterval(() => { elapsed++; totalSec--; timeDisplay.innerText = fmt(totalSec); if (elapsed === 11) switchVideo(2, GLOBAL_CONFIG.neko2Url); if (totalSec <= 0) { clearInterval(countdownTimer); overlay.style.display = 'none'; video1.pause(); video2.pause(); isShowing = false; activeWatchTime = 0; // 循环结束,重置工作时间 } }, 1000); } // 主计时器 function startMainTimer() { timerInterval = setInterval(() => { // 只有当 1.页面可见 2.未在全屏展示 3.已启动状态 时,才计时 if (document.visibilityState === 'visible' && !isShowing && isRunning) { activeWatchTime++; // 检查是否达到工作时间 X if (activeWatchTime >= siteConfig.workTime * 60) { triggerShow(); } } }, 1000); } // 启动 startMainTimer(); })();