// ==UserScript==
// @name 小雅自动刷
// @namespace http://tampermonkey.net/
// @version 1.2
// @description 小雅平台自动刷课脚本。
// @author Qy
// @match https://*.ai-augmented.com/*
// @connect docs.qq.com
// @grant GM_xmlhttpRequest
// @icon data:image/png;base64,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
// @run-at document-end
// ==/UserScript==
(function () {
'use strict';
const STORAGE_KEY = 'xy_qy_task_queue';
const POS_KEY = 'xy_qy_ui_pos';
const NOTICE_CACHE_KEY = 'xy_qy_notice_cache';
const DOMAIN = window.location.hostname;
const DEFAULT_DURATION = 10;
const NOTICE_URL = "https://docs.qq.com/doc/DYXZ5VUpyVXRnU1Fx";
const VIDEO_EXT = /\.(mp4|flv|avi|mov|mkv|wmv|rmvb|m4v|webm)$/i;
let taskQueue = [];
let heartbeatInterval = null;
let timerInterval = null;
let isExpanded = false;
let currentTask = null;
let remainingTime = 0;
let uiPos = null;
let noticeContent = "⏳ 初始化中...";
function loadData() {
try {
const q = localStorage.getItem(STORAGE_KEY);
const p = localStorage.getItem(POS_KEY);
if (q) taskQueue = JSON.parse(q);
if (p) uiPos = JSON.parse(p);
} catch (e) { taskQueue = []; }
}
function saveData() {
localStorage.setItem(STORAGE_KEY, JSON.stringify(taskQueue));
}
function savePos(top, left) {
uiPos = { top, left };
localStorage.setItem(POS_KEY, JSON.stringify(uiPos));
}
function getCookie(keyword = 'prd-access-token') {
const cookies = document.cookie.split('; ');
for (const cookie of cookies) {
const [name, value] = cookie.split('=');
if (name.includes(keyword)) return value;
}
return null;
}
function getPageInfo() {
const url = window.location.href;
const match = url.match(/mycourse\/(\d+)\/resource\/(\d+)\/(\d+)/);
if (match) return { groupId: match[1], resourceId: match[2], nodeId: match[3] };
const listMatch = url.match(/mycourse\/(\d+)/);
if (listMatch) return { groupId: listMatch[1], resourceId: null, nodeId: null };
return { groupId: null, resourceId: null, nodeId: null };
}
function formatSeconds(s) {
if (!s || s < 0) return "0:00";
const min = Math.floor(s / 60);
const sec = Math.floor(s % 60);
return `${min}:${sec.toString().padStart(2, '0')}`;
}
function runOneShotNoticeCheck() {
const isDangerZone = /\/mycourse\/\d+/.test(window.location.href);
if (isDangerZone) {
const cached = localStorage.getItem(NOTICE_CACHE_KEY);
if (cached) noticeContent = cached;
else noticeContent = "⚠️ 读取失败,为了防止检测,请离开课程区返回首页后再尝试读取公告。";
} else {
GM_xmlhttpRequest({
method: "GET",
url: NOTICE_URL,
onload: function(response) {
try {
const match = response.responseText.match(/
(.*?)<\/title>/);
let title = match ? match[1] : "暂无公告";
title = title.replace(" - 腾讯文档", "").trim();
const tempSpan = document.createElement('span');
tempSpan.innerHTML = title;
const finalContent = tempSpan.innerText;
localStorage.setItem(NOTICE_CACHE_KEY, finalContent);
noticeContent = finalContent;
const el = document.getElementById('xy-notice-board');
if (el) el.innerText = finalContent;
} catch (e) { noticeContent = "❌ 公告解析失败"; }
},
onerror: function() { noticeContent = "❌ 公告网络错误"; }
});
}
}
async function submitFinishActivity() {
if (!currentTask) return;
const { groupId } = getPageInfo();
const token = getCookie();
const targetGroupId = groupId || currentTask.groupId;
try {
await fetch(`https://${DOMAIN}/api/jx-iresource/resource/finishActivity`, {
method: 'POST',
headers: { 'authorization': `Bearer ${token}`, 'content-type': 'application/json; charset=UTF-8' },
body: JSON.stringify({ group_id: targetGroupId, node_id: currentTask.nodeId, task_id: currentTask.taskId })
});
} catch (e) { }
}
function tryReadVideoDuration(retryCount = 0) {
if (!currentTask || (currentTask.duration && currentTask.duration > 0)) return;
const video = document.querySelector('video');
const { nodeId } = getPageInfo();
if (!nodeId || String(nodeId) !== String(currentTask.nodeId)) return;
if (video && video.duration && video.duration > 1) {
const realDuration = Math.ceil(video.duration) + 5;
updateTaskDuration(realDuration);
} else {
if (retryCount < 2) setTimeout(() => tryReadVideoDuration(retryCount + 1), 2000);
else updateTaskDuration(DEFAULT_DURATION * 60);
}
}
function updateTaskDuration(seconds) {
if (!currentTask) return;
currentTask.duration = seconds;
remainingTime = seconds;
const index = taskQueue.findIndex(t => String(t.nodeId) === String(currentTask.nodeId));
if (index !== -1) {
taskQueue[index].duration = seconds;
saveData();
}
renderUI();
}
function tryAutoPlay() {
const video = document.querySelector('video');
if (video && video.paused) {
video.muted = true;
video.play().catch(() => {});
}
}
async function fetchResources(groupId, token) {
try {
const res = await fetch(`https://${DOMAIN}/api/jx-iresource/resource/queryCourseResources?group_id=${groupId}`, {
headers: { "authorization": `Bearer ${token}` }
});
const data = await res.json();
return data.success ? data.data : [];
} catch (e) { return []; }
}
async function fetchTasksFromApi() {
const { groupId } = getPageInfo();
const token = getCookie();
if (!token) return;
const btn = document.getElementById('xy-refresh-btn');
if(btn) btn.innerText = "加载中...";
try {
if (groupId) {
const [taskRes, resourceList] = await Promise.all([
fetch(`https://${DOMAIN}/api/jx-stat/group/task/queryTaskNotices?group_id=${groupId}&role=1`, {
headers: { "authorization": `Bearer ${token}` }
}).then(r => r.json()),
fetch(`https://${DOMAIN}/api/jx-iresource/resource/queryCourseResources?group_id=${groupId}`, {
headers: { "authorization": `Bearer ${token}` }
}).then(r => r.json())
]);
if (taskRes.success) {
const resourceMap = new Map();
if (resourceList.success) resourceList.data.forEach(r => { if (r.is_task) resourceMap.set(String(r.task_id), r); });
const courseName = document.title.replace(/-.*/, '').trim() || "当前课程";
taskQueue = taskRes.data.student_tasks.filter(t => t.task_type === 1 && t.finish !== 2).map(t => {
const resInfo = resourceMap.get(String(t.task_id));
let isVideo = false;
if (resInfo) isVideo = (resInfo.type && String(resInfo.type).includes('video')) || (resInfo.mimetype && resInfo.mimetype.includes('video')) || (resInfo.name && VIDEO_EXT.test(resInfo.name));
let dur = isVideo ? 0 : ((resInfo && resInfo.watch_min_minutes > 0) ? (resInfo.watch_min_minutes * 60 + 20) : DEFAULT_DURATION * 60);
return { groupId, nodeId: String(t.node_id), taskId: String(t.task_id), name: resInfo ? resInfo.name : t.name, duration: Math.ceil(dur), isVideo, groupName: courseName };
});
}
} else {
const res = await fetch(`https://${DOMAIN}/api/jx-stat/group/task/un_finish`, {
headers: { "authorization": `Bearer ${token}` }
}).then(r => r.json());
if (res.success) {
taskQueue = res.data.filter(t => t.task_type === 1).map(t => ({
groupId: String(t.group_id), nodeId: String(t.node_id), taskId: String(t.resource_id || t.task_id), name: t.name, duration: 0, isVideo: VIDEO_EXT.test(t.name), groupName: t.group_name
}));
}
}
taskQueue.sort((a, b) => (a.groupName || "").localeCompare(b.groupName || ""));
saveData();
renderUI();
checkCurrentTask();
} catch (e) { } finally { if(btn) btn.innerText = "刷新列表"; }
}
async function sendHeartbeat() {
const { groupId, resourceId } = getPageInfo();
const token = getCookie();
if (!token) return;
try {
let userId = localStorage.getItem('xy_user_id');
if (!userId) {
const userRes = await fetch(`https://${DOMAIN}/api/jx-auth/oauth2/info`, { headers: { "authorization": `Bearer ${token}` } }).then(r => r.json());
userId = userRes.data.info.id;
localStorage.setItem('xy_user_id', userId);
}
const targetGroupId = groupId || (currentTask ? currentTask.groupId : null);
const targetResourceId = resourceId || (currentTask ? currentTask.taskId : null);
if (!targetGroupId || !targetResourceId) return;
const message = JSON.stringify({ user_id: userId, group_id: targetGroupId, clientType: 1, roleType: 1, resourceId: targetResourceId });
const timestamp = Date.now().toString(), nonce = crypto.randomUUID();
const arr = [encodeURIComponent(message), timestamp, nonce, "--xy-create-signature--"].sort().join("");
const hashBuffer = await crypto.subtle.digest('SHA-1', new TextEncoder().encode(arr));
const signature = Array.from(new Uint8Array(hashBuffer)).map(b => b.toString(16).padStart(2, '0')).join('');
await fetch(`https://${DOMAIN}/api/jx-iresource/learnLength/learnRecord`, {
method: 'POST',
headers: { 'authorization': `Bearer ${token}`, 'content-type': 'application/json; charset=UTF-8' },
body: JSON.stringify({ message, signature, timestamp, nonce })
});
} catch (e) {}
}
function stopTimer() {
if (timerInterval) clearInterval(timerInterval);
if (heartbeatInterval) clearInterval(heartbeatInterval);
timerInterval = null; heartbeatInterval = null; currentTask = null;
renderUI();
}
function jumpToNext() {
if (taskQueue.length === 0) { stopTimer(); return; }
const nextTask = taskQueue[0];
window.location.replace(`https://${DOMAIN}/app/jx-web/mycourse/${nextTask.groupId}/resource/${nextTask.taskId}/${nextTask.nodeId}`);
}
async function handleTaskComplete() {
if (!currentTask) { jumpToNext(); return; }
const finishedNodeId = currentTask.nodeId;
if (!currentTask.isVideo) await submitFinishActivity();
stopTimer();
const removeIndex = taskQueue.findIndex(t => String(t.nodeId) === String(finishedNodeId));
if (removeIndex !== -1) { taskQueue.splice(removeIndex, 1); saveData(); }
setTimeout(jumpToNext, 1000);
}
function startTimer(task) {
currentTask = task;
remainingTime = parseInt(task.duration) || 0;
if (heartbeatInterval) clearInterval(heartbeatInterval);
heartbeatInterval = setInterval(sendHeartbeat, 30000);
sendHeartbeat();
if (timerInterval) clearInterval(timerInterval);
timerInterval = setInterval(() => {
if (remainingTime > 0) {
remainingTime--;
updateTimerDisplay();
if (remainingTime <= 0) handleTaskComplete();
}
}, 1000);
if (task.duration === 0) setTimeout(() => tryReadVideoDuration(0), 2000);
if (task.isVideo) setTimeout(tryAutoPlay, 2000);
renderUI();
}
async function checkCurrentTask() {
const { nodeId, groupId } = getPageInfo();
if (!nodeId) { stopTimer(); return; }
const taskIndex = taskQueue.findIndex(t => String(t.nodeId) === String(nodeId));
const task = taskQueue[taskIndex];
if (task) {
if (!task.isVideo && (!task.duration || task.duration === 0 || task.duration === DEFAULT_DURATION * 60)) {
const token = getCookie();
try {
const taskRes = await fetch(`https://${DOMAIN}/api/jx-stat/group/task/queryTaskNotices?group_id=${groupId}&role=1`, {
headers: { "authorization": `Bearer ${token}` }
}).then(r => r.json());
if (taskRes.success) {
const realTaskNotice = taskRes.data.student_tasks.find(t => String(t.node_id) === String(nodeId));
if (realTaskNotice) {
const realTaskId = String(realTaskNotice.task_id);
task.taskId = realTaskId;
taskQueue[taskIndex].taskId = realTaskId;
const resourceRes = await fetch(`https://${DOMAIN}/api/jx-iresource/resource/queryCourseResources?group_id=${groupId}`, {
headers: { "authorization": `Bearer ${token}` }
}).then(r => r.json());
if (resourceRes.success) {
const resInfo = resourceRes.data.find(r => String(r.task_id) === realTaskId);
if (resInfo && resInfo.watch_min_minutes > 0) {
task.duration = Math.ceil((resInfo.watch_min_minutes * 60) + 20);
} else {
if (task.duration !== DEFAULT_DURATION * 60) task.duration = DEFAULT_DURATION * 60;
}
saveData();
renderUI();
}
}
}
} catch (e) { }
}
if (!currentTask || String(currentTask.nodeId) !== String(task.nodeId)) startTimer(task);
} else stopTimer();
}
function makeDraggable(el, handle) {
handle.addEventListener('mousedown', (e) => {
const rect = el.getBoundingClientRect();
const offsetX = e.clientX - rect.left, offsetY = e.clientY - rect.top;
el.style.right = 'auto'; el.style.bottom = 'auto';
el.style.left = rect.left + 'px'; el.style.top = rect.top + 'px';
function onMouseMove(e) { el.style.left = (e.clientX - offsetX) + 'px'; el.style.top = (e.clientY - offsetY) + 'px'; }
function onMouseUp() {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
savePos(el.style.top, el.style.left);
}
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
});
}
const container = document.createElement('div');
container.id = 'xy-helper-container';
loadData();
if (uiPos) { container.style.top = uiPos.top; container.style.left = uiPos.left; }
else { container.style.top = '20%'; container.style.right = '10px'; }
Object.assign(container.style, { position: 'fixed', zIndex: '999999', fontFamily: "'PingFang SC', sans-serif" });
document.body.appendChild(container);
const style = document.createElement('style');
style.textContent = `
.xy-ball { width: 50px; height: 50px; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAKSWlDQ1BzUkdCIElFQzYxOTY2LTIuMQAASImdU3dYk/cWPt/3ZQ9WQtjwsZdsgQAiI6wIyBBZohCSAGGEEBJAxYWIClYUFRGcSFXEgtUKSJ2I4qAouGdBiohai1VcOO4f3Ke1fXrv7e371/u855zn/M55zw+AERImkeaiagA5UoU8Otgfj09IxMm9gAIVSOAEIBDmy8JnBcUAAPADeXh+dLA//AGvbwACAHDVLiQSx+H/g7pQJlcAIJEA4CIS5wsBkFIAyC5UyBQAyBgAsFOzZAoAlAAAbHl8QiIAqg0A7PRJPgUA2KmT3BcA2KIcqQgAjQEAmShHJAJAuwBgVYFSLALAwgCgrEAiLgTArgGAWbYyRwKAvQUAdo5YkA9AYACAmUIszAAgOAIAQx4TzQMgTAOgMNK/4KlfcIW4SAEAwMuVzZdL0jMUuJXQGnfy8ODiIeLCbLFCYRcpEGYJ5CKcl5sjE0jnA0zODAAAGvnRwf44P5Dn5uTh5mbnbO/0xaL+a/BvIj4h8d/+vIwCBAAQTs/v2l/l5dYDcMcBsHW/a6lbANpWAGjf+V0z2wmgWgrQevmLeTj8QB6eoVDIPB0cCgsL7SViob0w44s+/zPhb+CLfvb8QB7+23rwAHGaQJmtwKOD/XFhbnauUo7nywRCMW735yP+x4V//Y4p0eI0sVwsFYrxWIm4UCJNx3m5UpFEIcmV4hLpfzLxH5b9CZN3DQCshk/ATrYHtctswH7uAQKLDljSdgBAfvMtjBoLkQAQZzQyefcAAJO/+Y9AKwEAzZek4wAAvOgYXKiUF0zGCAAARKCBKrBBBwzBFKzADpzBHbzAFwJhBkRADCTAPBBCBuSAHAqhGJZBGVTAOtgEtbADGqARmuEQtMExOA3n4BJcgetwFwZgGJ7CGLyGCQRByAgTYSE6iBFijtgizggXmY4EImFINJKApCDpiBRRIsXIcqQCqUJqkV1II/ItchQ5jVxA+pDbyCAyivyKvEcxlIGyUQPUAnVAuagfGorGoHPRdDQPXYCWomvRGrQePYC2oqfRS+h1dAB9io5jgNExDmaM2WFcjIdFYIlYGibHFmPlWDVWjzVjHVg3dhUbwJ5h7wgkAouAE+wIXoQQwmyCkJBHWExYQ6gl7CO0EroIVwmDhDHCJyKTqE+0JXoS+cR4YjqxkFhGrCbuIR4hniVeJw4TX5NIJA7JkuROCiElkDJJC0lrSNtILaRTpD7SEGmcTCbrkG3J3uQIsoCsIJeRt5APkE+S+8nD5LcUOsWI4kwJoiRSpJQSSjVlP+UEpZ8yQpmgqlHNqZ7UCKqIOp9aSW2gdlAvU4epEzR1miXNmxZDy6Qto9XQmmlnafdoL+l0ugndgx5Fl9CX0mvoB+nn6YP0dwwNhg2Dx0hiKBlrGXsZpxi3GS+ZTKYF05eZyFQw1zIbmWeYD5hvVVgq9ip8FZHKEpU6lVaVfpXnqlRVc1U/1XmqC1SrVQ+rXlZ9pkZVs1DjqQnUFqvVqR1Vu6k2rs5Sd1KPUM9RX6O+X/2C+mMNsoaFRqCGSKNUY7fGGY0hFsYyZfFYQtZyVgPrLGuYTWJbsvnsTHYF+xt2L3tMU0NzqmasZpFmneZxzQEOxrHg8DnZnErOIc4NznstAy0/LbHWaq1mrX6tN9p62r7aYu1y7Rbt69rvdXCdQJ0snfU6bTr3dQm6NrpRuoW623XP6j7TY+t56Qn1yvUO6d3RR/Vt9KP1F+rv1u/RHzcwNAg2kBlsMThj8MyQY+hrmGm40fCE4agRy2i6kcRoo9FJoye4Ju6HZ+M1eBc+ZqxvHGKsNN5l3Gs8YWJpMtukxKTF5L4pzZRrmma60bTTdMzMyCzcrNisyeyOOdWca55hvtm82/yNhaVFnMVKizaLx5balnzLBZZNlvesmFY+VnlW9VbXrEnWXOss623WV2xQG1ebDJs6m8u2qK2brcR2m23fFOIUjynSKfVTbtox7PzsCuya7AbtOfZh9iX2bfbPHcwcEh3WO3Q7fHJ0dcx2bHC866ThNMOpxKnD6VdnG2ehc53zNRemS5DLEpd2lxdTbaeKp26fesuV5RruutK10/Wjm7ub3K3ZbdTdzD3Ffav7TS6bG8ldwz3vQfTw91jicczjnaebp8LzkOcvXnZeWV77vR5Ps5wmntYwbcjbxFvgvct7YDo+PWX6zukDPsY+Ap96n4e+pr4i3z2+I37Wfpl+B/ye+zv6y/2P+L/hefIW8U4FYAHBAeUBvYEagbMDawMfBJkEpQc1BY0FuwYvDD4VQgwJDVkfcpNvwBfyG/ljM9xnLJrRFcoInRVaG/owzCZMHtYRjobPCN8Qfm+m+UzpzLYIiOBHbIi4H2kZmRf5fRQpKjKqLupRtFN0cXT3LNas5Fn7Z72O8Y+pjLk722q2cnZnrGpsUmxj7Ju4gLiquIF4h/hF8ZcSdBMkCe2J5MTYxD2J43MC52yaM5zkmlSWdGOu5dyiuRfm6c7Lnnc8WTVZkHw4hZgSl7I/5YMgQlAvGE/lp25NHRPyhJuFT0W+oo2iUbG3uEo8kuadVpX2ON07fUP6aIZPRnXGMwlPUit5kRmSuSPzTVZE1t6sz9lx2S05lJyUnKNSDWmWtCvXMLcot09mKyuTDeR55m3KG5OHyvfkI/lz89sVbIVM0aO0Uq5QDhZML6greFsYW3i4SL1IWtQz32b+6vkjC4IWfL2QsFC4sLPYuHhZ8eAiv0W7FiOLUxd3LjFdUrpkeGnw0n3LaMuylv1Q4lhSVfJqedzyjlKD0qWlQyuCVzSVqZTJy26u9Fq5YxVhlWRV72qX1VtWfyoXlV+scKyorviwRrjm4ldOX9V89Xlt2treSrfK7etI66Trbqz3Wb+vSr1qQdXQhvANrRvxjeUbX21K3nShemr1js20zcrNAzVhNe1bzLas2/KhNqP2ep1/XctW/a2rt77ZJtrWv913e/MOgx0VO97vlOy8tSt4V2u9RX31btLugt2PGmIbur/mft24R3dPxZ6Pe6V7B/ZF7+tqdG9s3K+/v7IJbVI2jR5IOnDlm4Bv2pvtmne1cFoqDsJB5cEn36Z8e+NQ6KHOw9zDzd+Zf7f1COtIeSvSOr91rC2jbaA9ob3v6IyjnR1eHUe+t/9+7zHjY3XHNY9XnqCdKD3x+eSCk+OnZKeenU4/PdSZ3Hn3TPyZa11RXb1nQ8+ePxd07ky3X/fJ897nj13wvHD0Ivdi2yW3S609rj1HfnD94UivW2/rZffL7Vc8rnT0Tes70e/Tf/pqwNVz1/jXLl2feb3vxuwbt24m3Ry4Jbr1+Hb27Rd3Cu5M3F16j3iv/L7a/eoH+g/qf7T+sWXAbeD4YMBgz8NZD+8OCYee/pT/04fh0kfMR9UjRiONj50fHxsNGr3yZM6T4aeypxPPyn5W/3nrc6vn3/3i+0vPWPzY8Av5i8+/rnmp83Lvq6mvOscjxx+8znk98ab8rc7bfe+477rfx70fmSj8QP5Q89H6Y8en0E/3Pud8/vwv94Tz+y1HOM8AAAAJcEhZcwAALiMAAC4jAXilP3YAAA7xSURBVHic7Z17bFXVnsc/55y+DpcKpdOWShvbAhdB1MoMSG+mDh2vDNar4ZLcmmAUJPhorokaH0G0SqxAtYGZGFRMiTMSjeZi5EoqjEy8sZFgETS0RAr0acuztaVQ+m7Pmj/2Lp7W0/bsdfY+a5/TfpPvPz1da/3W7/vba+29ng7CE3HA74F5wM1ABpAExOt0AxFArP7/HcAA0A206rwE1AGngNPAGeBy0GoQJDhUG2ACkoF/Bf4ALEITPNGisprRAuJH4DBwCLhgUVmTGAXRwHJgO1AFCMWs0m1Zrts2CQsQCawA/hutGVYt+mi8rNu4Qrd5EgFiNrAFOId6cY3ynG77bNO9MgGQA5QCHtQLGSg9el1yTPVQmOJPwFHUi2YVjwL3m+atMMJy4AjqBQoWj+h1nvCYg9Y8qhZEFb/UfTDh4Aa2Ar2oF0E1e3VfuAPyaAhhGdqommrH241nCPMXxWi0AZNweLO3ih7dR2E3oDQfqEC9g0OFFbrPwgJ5aJMtqp0aauzQfReycAFvMdnkB0IPUKz7MqTwO+Bz1DswXLhX92lIIJGJNagTLB7Bumlu03AjcBL1zgpXntR9bEukoa2gUe2kcGed7mtbIQWoRr1zJgprdJ/bAglMNvsqWKX7XimmAOWod8ZEZbmugRK4gC/GMXCS1vMLFI0TbJMwdpLWcNs4WpmOvzA5wmcnenRNDENmX8B84HtgqtGEt956K1FRURJFBg+Dg4NcvXp11N+7urro7e0d9ffLly9bYZY/6ASWoL2Q+w2jARCNJv5tBtPhcDjo6uoiJibGaFLbo6WlhYqKCqqqqjh58iT19fWcPXuWpqamMYPJAlSiBcHoEToCEQYL2IqE+ACzZs0KG/Hr6+vZv38/5eXlfPfdd9TW1qo2aQi3AUXAs1ZkvowA+v3s7GwRyqisrBSvvvqquP3221X39/68D5i+sshNgCN9a9asUa2hYXg8HrFnzx6RlZWlWlSjrMbkNYZFgRpVWFioWk9DKC8vF5mZmaqFDIRFJugOaNus+wI16KOPPlKtqd944403hMvlUi1goOzTtRsT/rwEbsOEjY4ZGRmG0zQ3N1NZWWk4XXJyMrfccovhdAAvvvgixcXFUmlthki0BaZ/CiSTP2JSRF66dMnwk7hz506psp5//nmpJ3/fvn2qn1or+MexBHaO9SPwxji/+4XY2FgSE40vZmloaJAqLy0tTSrdK6+8IpXO5tg81o9jBcD9wJ1mWJCeni6VTjYAZMprb2+X6m5CAEsYY0PqWAGwySwLZPp/gLq6Oql0MgEgW1aIYNNoP4wWAMvQztsxBXPmyO1/rK+vl0on0wXU1NRIlRUiWISm6W8wWgA8Z2bpMk9kZ2cnLS0thtPNnDkTt9v4GIhssIUQfGrqKwAygPvMLFmmC5BtkoPd3YQQ7kPTdhh8BcB6TD4+bvZs48fkBPsLwEYTOlbBATw28o8jAyACeNTMUl0uFzfddJPhdMEOgAnQAgCsZcTg38gAuAeYaWaJKSkpUotAgvkC2N/fT1NTk1R5IYaZjDiaZmQAmL4bNdh9skx309jYyMDAgFR5IYhhS8e8AyAaWGl2aTKCQHC7gAnQ/3tjJV4HUHgHwL8B080uTbYFkAkAl8tFamqq4XQTpP8fwnQ0rYHhAfAfVpQmEwBtbW1cuXLFcLqUlBQiI41PXE6wAAAvrb0DINeKkmQCQPYFUHbOYQIGwHWthwLgRrRj1k2HzDtAML8AIHTeAdxuN2vXrmXLli2BZnUzMAt+/Sa8K9AcfWH69OnMmDHDcLrJFmA45s6dyxNPPMGjjz5KXFwcS5YsMSPbbODToQBYakaOIyEriGwAyHQ3ra2twV677xdcLhcPPPAATz75JPfccw8OhzY4+9lnn3Hs2DEziliKVwCYElIjITsLGMxPQLvNAiYnJ7N+/Xoef/xxUlKGHwEwMDDAxo0bzSrqX0DrAhzAQrNy9UYwPwFBLgDs0vzn5OSQn5/PypUrR/2S+eCDD6iurjaryNvQ53zSsWg92vvvv294XZ7H4xExMTGGy4qKihKDg4OGy9u8ebOy9XrTpk0TTz31lPjpp5/GtbOzs1MkJyebbUN6BLDA/6AxBpkW4MKFC/T09BhOl56ejtM53hLH30LFF0BmZib5+fmsXr2aqVP922P79ttvc+GC6fdTLYjAwgOHJqeBf0V0dDR5eXnk5+eTlZVlKG1bWxtvvfWWFWalWRYAkZGRUsOywR4DEEJIv6sMoaGhAY/HM+rvDoeDffv2sXy53B0RRUVFVm07TwP4Gxb0bxkZGYb7YyGEKCwslCqvqKhIqjwzsHjx4nHti4uLE42NjYbzbmpqEm6326r3kD1O4J8CCyLfCPYnoOyYgxm4667xx9FiY2NJSkoynPemTZvo7u6WMcsfxEdg0VFjss1qa2srcXFxhtPJTjubgezsbLZtG/uYnueee87wwphTp07x4YcfBmLaeEgA7epT05uXN99804LG1p745ZdfhMPhGNUXiYmJoqury3C+q1atsqrpH+J5JxbdVaPyiQw24uPjWbBg9K/pZ555xvBS9SNHjrB3795ATRsPU5xYdMbc3r17EUJYkbUtkZ2d7fPv06dPJz8/33B+L730UjD85wILm5jXXnvN7NbWtvj44499+mDjxo2G8zpw4IDVTb83rcvc4XCE1MEQgaCxsfE39Xe73aK5udlQPoODg+KOO+4IagBYep9PVFSUOHz4sEVutxfS09OH1f3pp582nMdoLYlF7HAA7cA0LERCQgJff/31b6Y3rUJ/fz/Xrl0LKI+0tDTDcwtr1qxh9+7dAERFRVFTU2NoNLS/v5/58+cHc37iClj0GRjqLC8vN/z0lpSUXE+/bt06w+l37NgR7HqeBzih2tl2ZEFBgWEBT58+LQDhdDrFmTNnDKW9du2aSEpKCnY9TwD8Q7Wz7chFixYZDgAhhEhKShIPPvig4XSvv/66inr+AyyaDAp1OhwOce7cOcNC5uXliePHjxtK09LSIm644QYV9dwD2sWEyh1uR5aUlBgOgBMnThhO8+yzz6qqYzHAX1U72q5cuXKlYTGNoqGhQURHR6uq419BOzlCubPtyKlTp4qenh5LA2Dt2rUq63gfWLgoNBz41VdfWSb+iRMnVB9Jm+4EGtBGAyfhA19++aVleb/88ssMDg5alv846EDTHoDD2OBpsyNnz55tydN/6NAh1XU7BL9uDv2eSfhEbW0tp06dMj3fDRs2mJ6nQRyDXwOgXKEhtofZ3UBpaSmHDh0yNU8JDNN8FjZobu3KnJwc05r+wcFBsXDhQuV10jUfhiobGGVLRkZGivb2dlMCYPfu3crro2sNDD8h5ACT8In+/n4OHjwYcD69vb0UFBSYYFHAuK61dwD8rwJDQgZmvAfs3LmTn3/+2QRrAoZPraOBy6hvnmzJxMREqd3HQ7hy5YpISEhQXg9dY5/HxPWi3UQ9CR9obm7m6NGj0um3bdsmdfq5BfiCMW4WzUV9hNqWMotEhBDi0qVLIjY2Vrn9OoedBjdy0dtB4CKT8AnZ/fmFhYV0dNhitP0i8H/j/dNW1Eep7RgZGSlqamoMP/11dXUiKipKuf06t46h+3VkEMAdweHK/Px8qeZ/9erVym3X6QH83q9XagODbcMpU6aI8+fPGxb/+PHjwul0Krdfp6Hv2GU2MNg23LBhg9TTf++99yq33YvLRpfbN36wgdHKGRcXJ9ra2gyL/8033yi33Ys/jC21b/zZBoYr59atWw2L7/F47Hbl/J/Hlto3HMCPNjBeGZOTk0VnZ6fhAPj888+V2+7FHwngErDlNqiAMr777ruGxR8YGBALFixQbrsXA74HYkJ+EaSnp4u+vj7DAbBr1y7ltnuxdHx5x8c8oM8GlQkaHQ6H2LNnj2Hxu7u7RWpqqnL7dfZh4h0QRTaoUNDEf++99wyLL4QQxcXFyu334pt+Kesn3EC1DSplKaOjo8Wnn34qJX57e7uYMWOG8jrorMaCw79yCOMh4tTUVKkzAYTQPvsee+wx5XXQ6dG1sgT/aYMKms68vDzR2toqJX5fX594+OGHldfBi//lt5oSiAYqbFBJUzhnzhxRWloqJbwQQly9elWsWLFCeT28WAHEGNBTCguw+GApqzl37lxRUlIi+vv7pcVvamoSmZmZyuvixWtYePfDSPyFEHsfiI+PF4888og4ePBgQGv7hNBm+VJSUpTXaQRNv/d5PGwzyXC/6Ha7xfz580Vubq5YvHjxmG/cMTExYt68eWLVqlViy5YtoqysTAwMDAQk+hD2799vp+VdQxz7pGqL4EJbYBjUyrpcLpGbmys++eQT0draKhobG0Vtba2orq4WtbW10i9z/mDHjh0iIiJCtdgj+QUWHffrD6ag7TFTUvlp06aJ9evXi7KyMuHxeCwTvqenR6xbt0610L5YrmugFAnYYFtZRkaGKCgokFq3NxbOnj0rli5dqlpoXzyFRXc9yCAFqEG9UwQg7rzzTrF9+3bR1NQUkPilpaUiPj5eeX18sFb3ua2QDtSh3jnX6XQ6RXZ2tnjnnXfExYsX/Ra+u7tbvPDCC2NeAqGQdbqvbYkbsUF34Isul0vcfffdYteuXWOe4F1WVibmzZun3N5RWIWPbd12QyLaiSOqnTUqnU6nyMrKEps3bxbffvut6OjoEBUVFeKhhx6y61MvdJ8mSuihBL8D9qLeaeHCv+s+DSm4mDyF1AwWo/A73ww8SIjPHShih+67sMAtQCXqnRoqrAQWSnnaxohBW08QUpNIQaZH95HlU7oq8e9MgOVlEqzWfTMh4EbbqtyLeserZi/aoltLLvC0O+ainVilWgRVPAD8PmAvhgGWY/PBI5P5vV7nSYzA/Whn2KoWyCoe0+sovVdvoiAHbVtTOHwxePS6WLZUO5wxG+0F6TzqhTTKC7rtE+cadQsRiXa82f9g78MsL+s25uo2T8ICRKO9QG3HHlPPVboty/E6gTNUEA4vI7OAP+j8Z7QdsVYtlWpBW4r1A9otK4eBcxaVFRSEQwD4Qhza9/XNaNvbM4CZQLzOGLSndWhBZRfagEwP0KrzItrqm9Noop9Ba+LDCv8PBDWO82JAo0wAAAAASUVORK5CYII='); background-size: 100% 100%; background-repeat: no-repeat; background-position: center; border-radius: 50%; box-shadow: 0 4px 10px rgba(0,0,0,0.3); cursor: grab; display: flex; align-items: center; justify-content: center; color: white; font-size: 24px; user-select: none; }
.xy-panel { width: 280px; background: rgba(30, 30, 30, 0.95); color: #fff; border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,0.5); overflow: hidden; display: flex; flex-direction: column; border: 1px solid rgba(255,255,255,0.1); }
.xy-header { padding: 12px; background: rgba(255,255,255,0.1); display: flex; justify-content: space-between; align-items: center; cursor: grab; user-select: none; }
.xy-controls { padding: 8px; background: rgba(0,0,0,0.2); text-align: center; }
.xy-btn { background: #0072FF; border: none; color: white; padding: 6px 15px; border-radius: 4px; cursor: pointer; font-size: 12px; margin: 0 2px; }
.xy-btn:hover { background: #005bb5; }
.xy-btn.danger { background: #ff4757; }
.xy-list { max-height: 300px; overflow-y: auto; padding: 5px; }
.xy-list::-webkit-scrollbar { width: 5px; }
.xy-list::-webkit-scrollbar-thumb { background: #555; border-radius: 5px; }
.xy-group-header { padding: 6px 10px; background: rgba(255,255,255,0.08); font-weight: bold; font-size: 11px; color: #bde0ff; border-top: 1px solid rgba(255,255,255,0.05); }
.xy-item { display: flex; justify-content: space-between; align-items: center; padding: 8px; border-bottom: 1px solid rgba(255,255,255,0.05); padding-left: 15px; }
.xy-item.active { background: rgba(0, 114, 255, 0.2); border-left: 3px solid #00C6FF; }
.xy-item-name { flex: 1; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-right: 10px; cursor: pointer; }
.xy-item-right { display: flex; align-items: center; gap: 5px; }
.xy-time-input { width: 45px; background: #222; border: 1px solid #444; color: #fff; font-size: 11px; text-align: center; border-radius: 3px; }
.xy-time-input.waiting { color: #aaa; font-style: italic; border-color: #666; }
.xy-delete { cursor: pointer; color: #ff6b81; font-size: 16px; padding: 0 5px; }
.xy-status-bar { padding: 8px; text-align: center; font-size: 12px; color: #00C6FF; background: rgba(0,0,0,0.3); }
.xy-tag { font-size:10px; padding:1px 3px; border-radius:3px; border:1px solid #aaa; color:#aaa; }
.xy-tag.video { border-color:#3498db; color:#3498db; }
.xy-notice { padding: 8px 10px; background: rgba(0,0,0,0.4); color: #e0e0e0; font-size: 11px; border-top: 1px solid rgba(255,255,255,0.1); max-height: 60px; overflow-y: auto; line-height: 1.4; text-align: left; white-space: pre-wrap; word-break: break-all; cursor: pointer; }
.xy-notice.expanded { max-height: none; }
`;
document.head.appendChild(style);
function renderUI() {
container.innerHTML = '';
if (!isExpanded) {
const ball = document.createElement('div');
ball.className = 'xy-ball';
ball.ondblclick = () => { isExpanded = true; renderUI(); };
makeDraggable(container, ball);
container.appendChild(ball);
} else {
const panel = document.createElement('div');
panel.className = 'xy-panel';
const header = document.createElement('div');
header.className = 'xy-header';
header.innerHTML = `小雅自动刷 v1.2`;
const minBtn = document.createElement('button');
minBtn.innerHTML = '➖'; minBtn.style = "background:none;border:none;color:white;cursor:pointer;";
minBtn.onclick = () => { isExpanded = false; renderUI(); };
header.appendChild(minBtn); makeDraggable(container, header); panel.appendChild(header);
const controls = document.createElement('div');
controls.className = 'xy-controls';
const refreshBtn = document.createElement('button');
refreshBtn.className = 'xy-btn'; refreshBtn.id = 'xy-refresh-btn'; refreshBtn.innerText = '刷新列表';
refreshBtn.onclick = fetchTasksFromApi;
const clearBtn = document.createElement('button');
clearBtn.className = 'xy-btn danger'; clearBtn.innerText = '清空';
clearBtn.onclick = () => { if(confirm("确定清空本地列表吗?")) { taskQueue=[]; saveData(); stopTimer(); renderUI(); } };
controls.appendChild(refreshBtn); controls.appendChild(clearBtn); panel.appendChild(controls);
const status = document.createElement('div');
status.className = 'xy-status-bar'; status.id = 'xy-timer-display';
if(currentTask) {
if(remainingTime > 0) status.innerHTML = `▶ 进行中: ${formatSeconds(remainingTime)}`;
else status.innerHTML = `⏳ 正在读取时长...`;
} else { status.innerHTML = `⏸ 待机中`; }
panel.appendChild(status);
const list = document.createElement('div');
list.className = 'xy-list';
if (taskQueue.length === 0) { list.innerHTML = `列表为空
`; }
else {
let lastGroupName = null;
taskQueue.forEach((task, index) => {
if (task.groupName && task.groupName !== lastGroupName) {
const groupHeader = document.createElement('div');
groupHeader.className = 'xy-group-header';
groupHeader.innerText = `📂 ${task.groupName}`;
list.appendChild(groupHeader);
lastGroupName = task.groupName;
}
const item = document.createElement('div');
const isActive = currentTask && String(task.nodeId) === String(currentTask.nodeId);
item.className = `xy-item ${isActive ? 'active' : ''}`;
const name = document.createElement('div');
name.className = 'xy-item-name';
name.innerHTML = `${task.isVideo ? '视' : '文'} ${task.name || '未知任务'}`;
name.onclick = () => window.location.replace(`https://${DOMAIN}/app/jx-web/mycourse/${task.groupId}/resource/${task.taskId}/${task.nodeId}`);
const right = document.createElement('div');
right.className = 'xy-item-right';
const timeInput = document.createElement('input');
timeInput.type = 'text'; timeInput.className = 'xy-time-input';
if (task.duration && task.duration > 0) timeInput.value = (task.duration / 60).toFixed(1);
else { timeInput.value = ""; timeInput.placeholder = "待读取"; timeInput.className += " waiting"; }
timeInput.onchange = (e) => {
const val = parseFloat(e.target.value);
if(val && val > 0) { task.duration = Math.ceil(val * 60); if(isActive) remainingTime = task.duration; saveData(); renderUI(); }
};
const delBtn = document.createElement('div');
delBtn.className = 'xy-delete'; delBtn.innerHTML = '✖';
delBtn.onclick = () => { if (isActive) stopTimer(); taskQueue.splice(index, 1); saveData(); renderUI(); };
right.appendChild(timeInput); if(task.duration > 0) right.appendChild(document.createTextNode('m'));
right.appendChild(delBtn); item.appendChild(name); item.appendChild(right); list.appendChild(item);
});
}
panel.appendChild(list);
const noticeBoard = document.createElement('div');
noticeBoard.className = 'xy-notice'; noticeBoard.id = 'xy-notice-board'; noticeBoard.innerText = noticeContent;
noticeBoard.onclick = () => noticeBoard.classList.toggle('expanded');
panel.appendChild(noticeBoard); container.appendChild(panel);
}
}
function updateTimerDisplay() {
const el = document.getElementById('xy-timer-display');
if (el && currentTask) {
if(remainingTime > 0) el.innerHTML = `▶ 进行中: ${formatSeconds(remainingTime)}`;
else el.innerHTML = `⏳ 正在读取时长...`;
}
}
function init() {
loadData();
const { groupId } = getPageInfo();
if (!groupId && taskQueue.length === 0) fetchTasksFromApi();
renderUI();
setTimeout(checkCurrentTask, 1500);
}
runOneShotNoticeCheck();
let lastUrl = location.href;
new MutationObserver(() => { if (location.href !== lastUrl) { lastUrl = location.href; init(); } }).observe(document, {subtree: true, childList: true});
init();
})();