// ==UserScript==
// @name 小雅自动刷
// @namespace http://tampermonkey.net/
// @version 1.1
// @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";
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] };
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();
try {
const res = 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: groupId,
node_id: currentTask.nodeId,
task_id: currentTask.taskId
})
});
await res.json();
} 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 (!groupId || !token) {
alert("请先进入具体的课程页面再点击刷新!");
return;
}
const btn = document.getElementById('xy-refresh-btn');
if(btn) btn.innerText = "加载中...";
try {
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()),
fetchResources(groupId, token)
]);
if (taskRes.success) {
const resourceMap = new Map();
if (Array.isArray(resourceList)) {
resourceList.forEach(r => {
if (r.is_task) resourceMap.set(String(r.task_id), r);
});
}
const newTasks = taskRes.data.student_tasks
.filter(t => t.task_type === 1 && t.finish !== 2)
.map(t => {
const resInfo = resourceMap.get(String(t.task_id));
const realName = resInfo ? resInfo.name : (t.name || `未知任务(${t.task_id})`);
let isVideo = false;
if (resInfo) {
isVideo = (resInfo.type && String(resInfo.type).includes('video')) ||
(resInfo.mimetype && resInfo.mimetype.includes('video')) ||
(resInfo.name && /\.(mp4|avi|mov|flv|mkv)$/i.test(resInfo.name));
}
let initDuration = 0;
if (isVideo) {
initDuration = 0;
} else {
if (resInfo && resInfo.watch_min_minutes > 0) {
initDuration = (resInfo.watch_min_minutes * 60) + 20;
} else {
initDuration = DEFAULT_DURATION * 60;
}
}
return {
nodeId: String(t.node_id),
taskId: String(t.task_id),
name: realName,
duration: Math.ceil(initDuration),
isVideo: isVideo
};
});
if (newTasks.length === 0) {
alert("🎉 没有未完成的观看任务!");
}
taskQueue = newTasks;
saveData();
renderUI();
checkCurrentTask();
}
} catch (e) {
alert("获取失败,请检查网络");
} finally {
if(btn) btn.innerText = "刷新列表";
}
}
async function sendHeartbeat() {
const { groupId, resourceId } = getPageInfo();
const token = getCookie();
if (!groupId || !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}` }
});
const userData = await userRes.json();
userId = userData.data.info.id;
localStorage.setItem('xy_user_id', userId);
}
const targetResourceId = resourceId || (currentTask ? currentTask.taskId : null);
if (!targetResourceId) return;
const message = JSON.stringify({
user_id: userId,
group_id: groupId,
clientType: 1,
roleType: 1,
resourceId: targetResourceId
});
const timestamp = Date.now().toString();
const nonce = crypto.randomUUID();
const arr = [encodeURIComponent(message), timestamp, nonce, "--xy-create-signature--"].sort().join("");
const msgBuffer = new TextEncoder().encode(arr);
const hashBuffer = await crypto.subtle.digest('SHA-1', msgBuffer);
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();
alert("🎉 所有任务已完成!");
return;
}
const nextTask = taskQueue[0];
const { groupId } = getPageInfo();
const safeGroupId = groupId || window.location.href.match(/mycourse\/(\d+)/)[1];
window.location.replace(`https://${DOMAIN}/app/jx-web/mycourse/${safeGroupId}/resource/${nextTask.taskId}/${nextTask.nodeId}`);
}
async function handleTaskComplete() {
if (!currentTask) { jumpToNext(); return; }
const finishedNodeId = currentTask.nodeId;
if (currentTask.isVideo === false) {
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();
}
function checkCurrentTask() {
const { nodeId } = getPageInfo();
if (!nodeId) { stopTimer(); return; }
const task = taskQueue.find(t => String(t.nodeId) === String(nodeId));
if (task) {
if (!currentTask || String(currentTask.nodeId) !== String(task.nodeId)) startTimer(task);
} else { stopTimer(); }
}
function makeDraggable(el, handle) {
handle.addEventListener('mousedown', (e) => {
e.preventDefault();
const rect = el.getBoundingClientRect();
const offsetX = e.clientX - rect.left;
const 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';
handle.style.cursor = 'grabbing';
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);
handle.style.cursor = 'grab';
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';
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; }
.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-item { display: flex; justify-content: space-between; align-items: center; padding: 8px; border-bottom: 1px solid rgba(255,255,255,0.05); }
.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;
}
.xy-notice::-webkit-scrollbar { width: 3px; }
.xy-notice::-webkit-scrollbar-thumb { background: #666; }
`;
document.head.appendChild(style);
function renderUI() {
container.innerHTML = '';
if (!isExpanded) {
const ball = document.createElement('div');
ball.className = 'xy-ball';
ball.innerHTML = '';
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.1`;
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 = () => { if(confirm("确定重置列表吗?")) fetchTasksFromApi(); };
controls.appendChild(refreshBtn);
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 {
taskQueue.forEach((task, index) => {
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 = () => {
const { groupId } = getPageInfo();
const gid = groupId || window.location.href.match(/mycourse\/(\d+)/)[1];
if(gid) window.location.replace(`https://${DOMAIN}/app/jx-web/mycourse/${gid}/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 footer = document.createElement('div');
footer.style.textAlign = "center"; footer.style.padding = "5px";
const clearBtn = document.createElement('button');
clearBtn.className = 'xy-btn danger';
clearBtn.innerText = '清空';
clearBtn.onclick = () => { if(confirm("确定清空?")) { taskQueue=[]; saveData(); stopTimer(); renderUI(); } };
footer.appendChild(clearBtn);
const noticeBoard = document.createElement('div');
noticeBoard.className = 'xy-notice';
noticeBoard.id = 'xy-notice-board';
noticeBoard.innerText = noticeContent;
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();
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();
})();