// ==UserScript== // @name 聚影(jying.top)自动签到 // @namespace https://www.jying.top/ // @version 1.1.0 // @author OpenClaw Agent // @description 聚影网盘每日自动签到,支持后台定时执行 + 网站访问时配置账号。每天北京时间 00:05 自动签到,访问 jying.top 可配置账号。 // @license MIT // @icon https://www.jying.top/favicon.ico // @supportURL https://github.com/openclaw/jying-checkin/issues // @homepageURL https://scriptcat.org/zh-CN/script-show-page/ // @match https://www.jying.top/* // @match https://jying.top/* // @crontab 5 16 * * * // @cloudcat true // @grant GM_xmlhttpRequest // @grant GM_notification // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_registerMenuCommand // @connect www.jying.top // @connect jying.top // @tag 签到 // @tag 自动化 // @tag 效率 // ==/UserScript== /** * 聚影自动签到脚本 v1.1.0 * * 【更新日志】 * * v1.1.0 (2026-08-06) * - 统一为单脚本:@match + @crontab 共存,各自独立运行 * - 后台定时:每天北京时间 00:05 静默签到 + 浏览器通知 * - 页面访问:自动检测账号,未配置则弹出配置引导面板 * - 共享存储:定时和页面共用同一套 GM_storage 数据 * - 移除冗余代码,逻辑更清晰 * * v1.0.x (2026-08-06) * - 早期版本,因 @crontab 导致后台模式优先,页面配置面板无法弹出 */ // ═══════════════════════════════════════════════════════════ // 存储键(定时版和页面版共享) // ═══════════════════════════════════════════════════════════ var STORE = { username: 'jy3_username', password: 'jy3_password_enc', token: 'jy3_token', tokenExpiry:'jy3_token_expiry', lastCheckin:'jy3_last_checkin', configShown:'jy3_config_shown' }; // ═══════════════════════════════════════════════════════════ // 工具函数 // ═══════════════════════════════════════════════════════════ function b64enc(str) { try { return btoa(unescape(encodeURIComponent(str))); } catch (e) { return btoa(str); } } function b64dec(str) { try { return decodeURIComponent(escape(atob(str))); } catch (e) { return atob(str); } } function save(key, value) { try { GM_setValue(key, value); } catch (e) {} } function load(key) { try { return GM_getValue(key, null); } catch (e) { return null; } } function remove(key) { try { GM_deleteValue(key); } catch (e) {} } function delay(ms) { return new Promise(function(res) { setTimeout(res, ms); }); } function notify(title, text) { console.log('[聚影] ' + title + ': ' + text); try { GM_notification({ title: title, text: text, timeout: 8000 }); } catch (e) {} } function maskAccount(acct) { if (!acct) return '未配置'; if (acct.indexOf('@') !== -1) { var p = acct.split('@'); return p[0].substring(0, 2) + '***@' + p[1]; } return acct.substring(0, 2) + '***'; } // ═══════════════════════════════════════════════════════════ // 账号管理 // ═══════════════════════════════════════════════════════════ function getAccount() { var u = load(STORE.username); var p = load(STORE.password); if (!u || !p) return null; try { return { username: u, password: b64dec(p) }; } catch (e) { return null; } } function saveAccount(username, password) { if (!username || !password) return false; save(STORE.username, username.trim()); save(STORE.password, b64enc(password.trim())); save(STORE.token, null); save(STORE.tokenExpiry, null); return true; } function isConfigured() { return getAccount() !== null; } function clearConfig() { Object.keys(STORE).forEach(function(k) { remove(STORE[k]); }); notify('配置已清除', '请重新配置账号密码'); } // ═══════════════════════════════════════════════════════════ // API 请求 // ═══════════════════════════════════════════════════════════ function api(method, path, data, token) { return new Promise(function(resolve, reject) { var headers = { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' }; if (token) headers['X-App-User-Token'] = token; var opt = { method: method, url: 'https://www.jying.top' + path, headers: headers, timeout: 15000, onload: function(r) { console.log('[聚影] ' + method + ' ' + path + ' -> ' + r.status); if (r.status >= 200 && r.status < 300) { try { resolve(JSON.parse(r.responseText)); } catch (e) { reject(new Error('JSON解析失败')); } } else { try { var j = JSON.parse(r.responseText); reject(new Error(j.message || j.msg || 'HTTP ' + r.status)); } catch (e2) { reject(new Error('HTTP ' + r.status)); } } }, onerror: function() { reject(new Error('网络请求失败')); }, ontimeout: function() { reject(new Error('请求超时')); } }; if (data) opt.data = JSON.stringify(data); try { opt.withCredentials = true; } catch (e) {} GM_xmlhttpRequest(opt); }); } async function getToken() { var tok = load(STORE.token); var exp = load(STORE.tokenExpiry); if (tok && exp && Date.now() < exp) return tok; var acct = getAccount(); if (!acct) throw new Error('未配置账号密码'); try { await api('GET', '/api/csrf/'); } catch (e) {} var r = await api('POST', '/api/app/login/', { username: acct.username, password: acct.password }); if (r.status !== 'success' || !r.token) throw new Error(r.message || '登录失败'); save(STORE.token, r.token); save(STORE.tokenExpiry, Date.now() + 7 * 24 * 60 * 60 * 1000); return r.token; } async function getStats(tok) { var r = await api('GET', '/api/app/checkin/stats/', null, tok); if (r.status === 'success') { return { total: r.my_total_days || 0, today: r.checked_today || false, pts: r.reward_points || 0 }; } throw new Error(r.message || '获取状态失败'); } async function doCheckin(tok) { try { var r = await api('POST', '/api/app/checkin/do/', null, tok); if (r.status === 'success') return { ok: true, pts: r.points || 5 }; return { ok: false, msg: r.message || r.msg || '签到失败' }; } catch (e) { if (e.message.indexOf('已签') !== -1) return { ok: false, already: true, msg: e.message }; throw e; } } // ═══════════════════════════════════════════════════════════ // 签到主逻辑(定时和页面共用) // ═══════════════════════════════════════════════════════════ async function runCheckin() { if (!isConfigured()) { notify('聚影签到', '[聚影] 请先配置账号:访问 jying.top 即可设置'); throw new Error('未配置账号'); } var acct = getAccount(); console.log('[聚影] 账号: ' + maskAccount(acct.username)); try { var tok = await getToken(); var stats = await getStats(tok); if (stats.today) { notify('今日已签到', '累计 ' + stats.total + ' 天'); save(STORE.lastCheckin, new Date().toISOString()); return; } var r = await doCheckin(tok); if (r.ok) { notify('签到成功', '获得 ' + r.pts + ' 积分,累计 ' + (stats.total + 1) + ' 天'); save(STORE.lastCheckin, new Date().toISOString()); } else if (r.already) { notify('今日已签到', '累计 ' + stats.total + ' 天'); } else { notify('签到失败', r.msg); } } catch (e) { console.error('[聚影] 异常:', e.message); if (e.message.indexOf('登录') !== -1 || e.message.indexOf('密码') !== -1 || e.message.indexOf('未配置') !== -1) { save(STORE.token, null); save(STORE.tokenExpiry, null); } notify('签到失败', e.message); throw e; } } // ═══════════════════════════════════════════════════════════ // 页面模式:配置面板 + 菜单(仅在访问 jying.top 时运行) // ═══════════════════════════════════════════════════════════ function showConfigPanel() { if (document.getElementById('jy3-overlay')) return; var css = [ '.jy3-o{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.5);z-index:2147483647;display:flex;align-items:center;justify-content:center}', '.jy3-p{background:#fff;border-radius:12px;padding:24px;max-width:400px;width:90%;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;box-shadow:0 4px 24px rgba(0,0,0,.3)}', '.jy3-t{font-size:20px;font-weight:700;margin:0 0 12px;color:#333}', '.jy3-d{color:#666;font-size:14px;margin:0 0 20px;line-height:1.6}', '.jy3-f{margin-bottom:14px}', '.jy3-l{display:block;font-size:14px;font-weight:500;color:#333;margin-bottom:5px}', '.jy3-i{width:100%;padding:9px 11px;border:1px solid #ddd;border-radius:6px;font-size:14px;box-sizing:border-box;transition:border-color .2s}', '.jy3-i:focus{outline:none;border-color:#4CAF50}', '.jy3-b{display:flex;gap:10px;margin-top:18px}', '.jy3-bt{flex:1;padding:9px 14px;border:none;border-radius:6px;font-size:14px;font-weight:500;cursor:pointer}', '.jy3-pri{background:#4CAF50;color:#fff}', '.jy3-pri:hover{background:#45a049}', '.jy3-sec{background:#f0f0f0;color:#555}', '.jy3-sec:hover{background:#e0e0e0}', '.jy3-fo{margin-top:14px;padding-top:14px;border-top:1px solid #eee;color:#999;font-size:12px;text-align:center}' ].join(''); var s = document.createElement('style'); s.textContent = css; document.head.appendChild(s); var html = [ '
聚影自动签到
', '
', '欢迎使用聚影自动签到!请输入您的账号密码,每天 00:05 自动签到。', '
', '
', '', '', '
', '
', '', '', '
', '
', '', '', '
', '
账号密码仅保存在浏览器本地,不会上传
' ].join(''); var ov = document.createElement('div'); ov.id = 'jy3-overlay'; ov.className = 'jy3-o'; ov.innerHTML = '
' + html + '
'; document.body.appendChild(ov); var uIn = document.getElementById('jy3-u'); var pIn = document.getElementById('jy3-p'); document.getElementById('jy3-cancel').addEventListener('click', function() { ov.remove(); save(STORE.configShown, true); }); document.getElementById('jy3-save').addEventListener('click', function() { var u = uIn.value.trim(); var pw = pIn.value.trim(); if (!u || !pw) { notify('配置失败', '账号和密码不能为空'); uIn.focus(); return; } if (saveAccount(u, pw)) { ov.remove(); save(STORE.configShown, true); notify('配置成功', '账号: ' + maskAccount(u) + ',每天 00:05 自动签到'); runCheckin(); } }); pIn.addEventListener('keypress', function(e) { if (e.key === 'Enter') document.getElementById('jy3-save').click(); }); var escH = function(e) { if (e.key === 'Escape') { ov.remove(); save(STORE.configShown, true); document.removeEventListener('keydown', escH); } }; document.addEventListener('keydown', escH); ov.addEventListener('click', function(e) { if (e.target === ov) { ov.remove(); save(STORE.configShown, true); } }); setTimeout(function() { uIn.focus(); }, 100); } function registerMenu() { try { GM_registerMenuCommand('\u23FA 立即签到', function() { runCheckin(); }); GM_registerMenuCommand('\u2699\uFE0F 配置账号', showConfigPanel); GM_registerMenuCommand('\uD83D\uDDD1\uFE0F 清除配置', clearConfig); if (isConfigured()) { var acct = load(STORE.username); GM_registerMenuCommand('\uD83D\uDCCB ' + maskAccount(acct), function() { alert('\u5F53\u524D\u8D26\u53F7: ' + maskAccount(acct)); }); } } catch (e) { console.error('[聚影] 菜单注册失败:', e); } } // ═══════════════════════════════════════════════════════════ // 入口 // ═══════════════════════════════════════════════════════════ // 同时声明 @match 和 @crontab 时: // - 定时触发(每天 00:05)→ 后台沙盒运行,不弹面板,只发通知 // - 用户访问 jying.top → 页面模式运行,显示配置面板 / 菜单 / 手动签到 (function init() { // 注册脚本菜单(两种模式都会执行) registerMenu(); // 仅页面模式下自动弹配置面板 // 后台模式无 document,typeof document === 'undefined' 为 true if (typeof document !== 'undefined') { // 页面加载时:已配置则静默,未配置则弹面板 if (!isConfigured() && !load(STORE.configShown)) { setTimeout(showConfigPanel, 800); } } // ScriptCat 后台脚本规范:返回 Promise // 定时触发时后台执行 runCheckin(),页面加载时 resolve return new Promise(function(resolve) { if (typeof document === 'undefined') { // 后台定时模式:执行签到 console.log('[聚影] 后台定时签到开始'); runCheckin() .then(function() { resolve('签到完成'); }) .catch(function(err) { resolve('签到异常: ' + err.message); }); } else { // 页面模式:不阻塞 resolve('页面已加载'); } }); })();