// ==UserScript== // @name 国开助手 // @namespace https://eduai.lovie.me/ // @version 0.6.7 // @description 国开课程视频自动刷课 + 自动答全部课程(形考/终考/大作业);脚本本身不含题库。 // @icon data:image/png;base64,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 // @author 国开助手 // @match *://*.ouchn.cn/* // @grant GM_xmlhttpRequest // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant GM_notification // @grant GM_setClipboard // @connect eduai.lovie.me // @connect menhu.pt.ouchn.cn // @run-at document-idle // ==/UserScript== (function () { 'use strict'; // ======== 运营配置(发之前只改这里)======== const CFG = { QQ_GROUP: '3930058219', QQ_LINK: '', HOMEPAGE: 'https://eduai.lovie.me/s/', SERVER_BASE: 'https://eduai.lovie.me', VIDEO_RATE: 4, CHUNK: 120, PANEL_KEY: 'gk_trial_panel_open', AUTH_KEY: 'gk_trial_auth_v3' }; // ======== 通用小工具 ======== const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); function gmGet(k, d) { try { if (typeof GM_getValue === 'function') { const v = GM_getValue(k, null); return v === null ? d : v; } const v = localStorage.getItem(k); return v === null ? d : JSON.parse(v); } catch (e) { return d; } } function gmSet(k, v) { try { if (typeof GM_setValue === 'function') GM_setValue(k, v); else localStorage.setItem(k, JSON.stringify(v)); } catch (e) {} } function gmXhr(opts) { if (typeof GM_xmlhttpRequest === 'function') { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: opts.method || 'GET', url: opts.url, headers: opts.headers || {}, data: opts.data, timeout: opts.timeout || 30000, onload: resolve, onerror: reject, ontimeout: () => reject(new Error('请求超时')) }); }); } return fetch(opts.url, { method: opts.method || 'GET', headers: opts.headers, body: opts.data }) .then(async (r) => ({ status: r.status, responseText: await r.text() })); } function deviceId() { let d = gmGet('gk_trial_device', ''); if (!d) { const hex = '0123456789ABCDEF'; for (let i = 0; i < 8; i++) d += hex[Math.floor(Math.random() * 16)]; gmSet('gk_trial_device', d); } return d; } // ======== 页面识别 ======== function guessStudent() { try { const hay = location.href + ' ' + location.hash; let m = hay.match(/(?:student_?id|studentId|user_?no|userNumber|username)[=:_-]?(\d{6,20})/i); if (m) return m[1]; m = ((document.body && document.body.innerText) || '').match(/(?:学号|学号[::])\s*(\d{6,20})/); if (m) return m[1]; for (let i = 0; i < localStorage.length; i++) { const k = localStorage.key(i) || ''; if (!/user|student|account|学号/i.test(k)) continue; const v = localStorage.getItem(k) || ''; const mm = v.match(/\d{8,20}/); if (mm) return mm[0]; } } catch (e) {} return ''; } function guessCourse() { try { const h = document.querySelector('h1, .course-title, .course-name'); const s = ((h && h.innerText) || document.title || '').trim(); return s.replace(/\s+/g, ' ').slice(0, 40); } catch (e) { return ''; } } // 学号不让客户手填:先看页面,再从学校门户取(浏览器里本来就有登录态) const studentCache = { no: String(gmGet('gk_student', '') || ''), at: 0 }; let resolvingStudent = null; async function fetchPortalStudent() { try { const r = await gmXhr({ method: 'POST', url: 'https://menhu.pt.ouchn.cn/ouchnapp/wap/user/get-info', headers: { 'Accept': 'application/json, text/plain, */*', 'Content-Type': 'application/x-www-form-urlencoded' }, data: '', timeout: 6000 }); const j = JSON.parse((r && r.responseText) || '{}'); const d = (j && j.d) || {}; const role = d.role || {}; const no = String(role.number || d.uid || '').trim(); if (/^\d{6,20}$/.test(no)) return no; } catch (e) {} return ''; } function resolveStudent(force) { if (!force && studentCache.no && Date.now() - studentCache.at < 120000) { return Promise.resolve(studentCache.no); } if (resolvingStudent) return resolvingStudent; resolvingStudent = (async () => { let no = guessStudent(); const portal = await fetchPortalStudent(); // 门户当前登录账号最权威:换号后不会拿旧学号去扣额度 if (portal) no = portal; resolvingStudent = null; if (no) { studentCache.no = no; studentCache.at = Date.now(); gmSet('gk_student', no); } return no; })(); return resolvingStudent; } const maskStudent = (no) => no ? (no.slice(0, 3) + '****' + no.slice(-4)) : ''; // ======== 授权 / 额度 ======== let AUTH = gmGet(CFG.AUTH_KEY, {}) || {}; let USAGE = null; const now = () => Date.now(); const token = () => AUTH.token || AUTH.trialToken || ''; const trialActive = () => !AUTH.token && AUTH.trialToken && (AUTH.trialEnd || 0) > now(); const isAuthed = () => !!AUTH.token || trialActive(); let expiredLogged = false; function saveAuth() { gmSet(CFG.AUTH_KEY, AUTH); } function trialLeftText() { if (AUTH.token) return '已登录:' + (AUTH.user || '用户'); if (trialActive()) { const s = Math.max(0, Math.floor((AUTH.trialEnd - now()) / 1000)); const h = Math.floor(s / 3600), m = Math.floor((s % 3600) / 60), ss = s % 60; return '体验剩余 ' + String(h).padStart(2, '0') + ':' + String(m).padStart(2, '0') + ':' + String(ss).padStart(2, '0'); } if (AUTH.trialUsed) return '体验已结束,注册后继续'; return '未登录:点下面「注册 / 登录」或「免费体验 120 分钟」'; } function quotaText() { if (!USAGE) return isAuthed() ? '正在读取额度…' : '登录/体验后显示额度'; if (USAGE.kind === 'trial') { return '体验已用学号 ' + (USAGE.used || 0) + '/' + (USAGE.limit || 3) + ';到期后注册,每天免费 1 个学号'; } const valid = (USAGE.students || USAGE.today || []); return '有效学号 ' + valid.length + ' 个(60天)· 今日免费名额' + (USAGE.free_used ? '已用' : '可用') + ' · 明天还有 1 个免费(只认学号)'; } function renderCredit() { const n = el('#creditNum'), h = el('#creditHint'); if (!n) return; if (USAGE && USAGE.kind === 'user') { n.textContent = String(USAGE.credits || 0); h.textContent = '1 点 = 1 个额外学号 / 60 天'; } else if (isAuthed()) { n.textContent = '—'; h.textContent = '体验中 · 注册后显示点数'; } else { n.textContent = '—'; h.textContent = '登录后显示'; } } function renderQuota() { el('#quota').textContent = quotaText(); authline.textContent = trialLeftText(); renderCredit(); const cta = el('#authCta'); if (cta) cta.style.display = isAuthed() ? 'none' : 'block'; } async function api(path, body, timeoutMs) { const r = await gmXhr({ method: 'POST', url: CFG.SERVER_BASE + path, headers: { 'Content-Type': 'application/json' }, data: JSON.stringify(body), timeout: timeoutMs || 30000 }); let j = {}; try { j = JSON.parse(r.responseText || '{}'); } catch (e) {} j._status = r.status; return j; } async function loadUsage() { if (!isAuthed()) { USAGE = null; renderQuota(); return; } try { const j = await api('/dati/api/usage', { token: token(), device: deviceId() }); if (j.ok) { USAGE = j; if (j.kind === 'trial' && j.left) AUTH.trialEnd = now() + j.left * 1000; } else if (j._status === 401) { AUTH = {}; saveAuth(); USAGE = null; } } catch (e) {} renderQuota(); } async function startTrial() { authMsg.textContent = '正在开通体验…'; try { const j = await api('/dati/api/trial/start', { device: deviceId() }); if (!j.ok) { authMsg.textContent = j.error || '体验开通失败'; if (j._status === 429) authForm.style.display = 'block'; return; } AUTH.trialToken = j.token; AUTH.trialEnd = (j.end || 0) * 1000; AUTH.trialUsed = true; saveAuth(); log('体验已开始:120 分钟(最多 3 个学号)'); closeGate(true); loadUsage(); } catch (e) { authMsg.textContent = '体验开通失败:' + e.message; } } // ======== 面板 ======== const host = document.createElement('div'); host.id = 'gk-trial-host'; (document.body || document.documentElement).appendChild(host); const root = host.attachShadow({ mode: 'open' }); root.innerHTML = `
国开助手
国开助手 v0.6.7
关闭
我的点数—
登录后显示

账号

未登录:点下面「注册 / 登录」或「免费体验 120 分钟」
还没有开通:先免费体验 120 分钟,或注册一个账号
检测中…
复制页面信息

刷课 / 答题

自动答全部课程:形考、终考、大作业;上传类作业会生成文档上传,失败会在日志提示。

额度

登录/体验后显示额度(学号自动识别)

点数

每天可免费登记 1 个新学号;额外学号 1 点 = 1 个 / 60 天(60 天内重复登录不再扣点)。购买点数请展开下方「服务咨询」。
服务咨询
服务咨询QQ 正式版 检查更新
支付暂未开放;购买点数或正式版请通过服务咨询QQ联系。
已完成

开始使用

先选一种方式:免费体验 120 分钟(全功能,最多 3 个学号,不注册),或 注册(每天免费 1 个学号,更多按点数)。

体验结束或学号用完,可继续注册;正式版可在面板底部查看服务咨询。
以后再说
`; const el = (s) => root.querySelector(s); const panel = el('#panel'), fab = el('#fab'); const logBox = el('#log'); const btnSz = el('#szbrush'), btnAns = el('#ansall'), btnOne = el('#onebrush'); const courseInput = el('#coursename'), btnSzPause = el('#szpause'), btnAnsStop = el('#ansstop'); const gate = el('#gate'), authForm = el('#authForm'), authMsg = el('#authMsg'); const authline = el('#authline'); el('#qq').textContent = CFG.QQ_GROUP; el('#home').href = CFG.HOMEPAGE; function log(msg) { const t = new Date().toTimeString().slice(0, 8); logBox.textContent += `[${t}] ${msg}\n`; logBox.scrollTop = logBox.scrollHeight; } let actionSeq = 0; function setAction(msg, sticky) { const box = el('#actionMsg'); if (!box) return; box.textContent = msg || ''; if (msg) log(msg); const seq = ++actionSeq; if (msg && !sticky) { setTimeout(() => { if (actionSeq === seq) box.textContent = ''; }, 8000); } } let toastTimer = null; function showToast(title, sub, ms) { const t = el('#toast'); if (!t) return; el('#toastTitle').textContent = title || '已完成'; el('#toastSub').textContent = sub || ''; t.classList.add('show'); clearTimeout(toastTimer); toastTimer = setTimeout(() => t.classList.remove('show'), ms || 4500); } async function copyText(text, label) { try { if (typeof GM_setClipboard === 'function') GM_setClipboard(text, 'text'); else await navigator.clipboard.writeText(text); log((label || '内容') + '已复制'); return true; } catch (e) { log('复制失败,请手动选择'); return false; } } function setOpen(open) { panel.classList.toggle('open', open); gmSet(CFG.PANEL_KEY, open ? 1 : 0); } fab.addEventListener('click', () => setOpen(!panel.classList.contains('open'))); el('#close').addEventListener('click', () => setOpen(false)); el('#copyqq').addEventListener('click', () => copyText(CFG.QQ_GROUP, '服务咨询QQ')); el('#toast').addEventListener('click', () => { el('#toast').classList.remove('show'); clearTimeout(toastTimer); }); el('#copyinfo').addEventListener('click', () => { const txt = [ (el('#pageinfo').textContent || '').trim(), '网址:' + location.href, '版本:' + (el('.hd span') ? el('.hd span').textContent : ''), '账号:' + trialLeftText() ].join('\n'); copyText(txt, '页面信息'); }); el('#copylog').addEventListener('click', () => { const t = (logBox.textContent || '').trim(); if (!t) { log('日志为空'); return; } copyText(t, '日志'); }); el('#clearlog').addEventListener('click', () => { logBox.textContent = ''; log('日志已清空'); }); function updateAuthline() { authline.textContent = trialLeftText(); if (!isAuthed() && AUTH.trialUsed && !expiredLogged) { expiredLogged = true; log('体验已结束:注册后继续(明天还有 1 个免费学号)'); renderQuota(); } if (trialActive()) expiredLogged = false; } setInterval(updateAuthline, 1000); let gateResolve = null; function openGate() { gate.classList.add('open'); if (gateResolve) gateResolve(false); return new Promise((r) => { gateResolve = r; }); } function closeGate(ok) { gate.classList.remove('open'); if (gateResolve) { gateResolve(ok); gateResolve = null; } } async function ensureAuth() { if (isAuthed()) return true; return openGate(); } el('#startTrial').addEventListener('click', () => { startTrial(); }); el('#showAuth').addEventListener('click', () => { authForm.style.display = 'block'; authMsg.textContent = ''; }); el('#gateLater').addEventListener('click', () => closeGate(false)); el('#ctaTrial').addEventListener('click', () => { openGate(); startTrial(); }); el('#ctaAuth').addEventListener('click', () => { openGate(); authForm.style.display = 'block'; authMsg.textContent = ''; }); el('#doLogin').addEventListener('click', async () => { const u = el('#u').value.trim(), p = el('#p').value; if (!u || !p) { authMsg.textContent = '用户名和密码都要填'; return; } authMsg.textContent = '登录中…'; try { const j = await api('/dati/api/auth/login', { username: u, password: p, device: deviceId() }); if (!j.ok) throw new Error(j.error || ('HTTP ' + j._status)); AUTH.token = j.token; AUTH.user = j.name || u; AUTH.expires = j.expires || ''; saveAuth(); log('登录成功:' + AUTH.user); closeGate(true); loadUsage(); } catch (e) { authMsg.textContent = '登录失败:' + e.message; } }); el('#doReg').addEventListener('click', async () => { const u = el('#u').value.trim(), p = el('#p').value; if (!u || !p) { authMsg.textContent = '用户名和密码都要填'; return; } authMsg.textContent = '注册中…'; try { // 注册来源由服务器记录,客户只填账号密码 const r1 = await api('/dati/api/auth/register', { username: u, password: p, device: deviceId(), channel: 'script' }); if (!r1.ok) throw new Error(r1.error || ('HTTP ' + r1._status)); if (r1.pending) { authMsg.textContent = '注册已提交,等待开通;详情请查看面板底部「服务咨询」。'; return; } const j = await api('/dati/api/auth/login', { username: u, password: p, device: deviceId() }); if (!j.ok) throw new Error('注册成功但登录失败:' + (j.error || '')); AUTH.token = j.token; AUTH.user = j.name || u; AUTH.expires = j.expires || ''; saveAuth(); log('注册并登录成功:' + AUTH.user); closeGate(true); loadUsage(); } catch (e) { authMsg.textContent = '注册失败:' + e.message; } }); async function bindStudent(quiet) { if (!isAuthed()) return false; // 不让客户手动绑定:从页面/学校门户自动识别当前登录学号 const sno = await resolveStudent(true); if (!sno) { if (!quiet) log('没识别到学号:请确认已在国开登录;识别不到可咨询服务咨询QQ'); return false; } try { const j = await api('/dati/api/student/bind', { token: token(), device: deviceId(), student_no: sno }); if (!j.ok) { log(j.error || '学号绑定失败'); if (j.today) { USAGE = { kind: 'user', today: j.today, students: j.students || j.today, credits: j.credits || 0, free_used: true, tomorrow_free: 1 }; renderQuota(); } return false; } studentCache.no = sno; gmSet('gk_student', sno); log('学号已识别:' + maskStudent(sno)); if (j.charged) log('新学号:扣 1 点,60 天内有效'); else if (j.reason === 'existing') log('该学号 60 天内已登记,不再扣点'); else if (j.reason === 'free') log('今日免费学号:60 天内有效'); await loadUsage(); return true; } catch (e) { log('学号绑定失败:' + e.message); return false; } } const state = { running: false, examRunning: false, szRunning: false, szPaused: false, szBusy: false, szLastMode: 'sz', szLastCourse: '', ansRunning: false, ansBusy: false, ansStop: false }; function refreshPageInfo() { let s = ''; const course = guessCourse(); if (course) s += '课程:' + course + '\n'; s += '学号:' + (studentCache.no ? maskStudent(studentCache.no) + '(自动识别)' : '识别中…'); el('#pageinfo').textContent = s; } async function lmsJson(path, opts) { const o = Object.assign({ credentials: 'include' }, opts || {}); o.headers = Object.assign({ 'Accept': 'application/json, text/plain, */*', 'X-Requested-With': 'XMLHttpRequest' }, o.headers || {}); const ctl = new AbortController(); const timer = setTimeout(() => ctl.abort(), 30000); o.signal = ctl.signal; let r; try { r = await fetch(path, o); } catch (e) { throw new Error(e && e.name === 'AbortError' ? '平台请求超时' : (e.message || '平台请求失败')); } finally { clearTimeout(timer); } const text = await r.text(); let j = {}; if (text) { try { j = JSON.parse(text); } catch (e) { throw new Error('平台返回非 JSON(可能被拦截)'); } } if (!r.ok) throw new Error('平台 HTTP ' + r.status + ((j && (j.message || j.error)) ? ':' + (j.message || j.error) : '')); j = j || {}; try { j._status = r.status; } catch (e) {} return j; } const lmsGet = (path) => lmsJson(path); const lmsPost = (path, body) => lmsJson(path, { method: 'POST', headers: { 'Content-Type': 'application/json;charset=UTF-8' }, body: JSON.stringify(body || {}) }); // ======== 思政课自动刷 ======== const SZ_WORDS = ['思政', '形势与政策', '马克思主义', '毛泽东', '习近平', '思想道德', '中国近现代史', '新时代', '政治理论', '党史', '中国特色社会主义', '思想道德与法治', '四史']; function isSzCourse(c) { const name = String(c.name || c.course_name || c.display_name || ''); return SZ_WORDS.some((w) => name.indexOf(w) >= 0); } function onLmsPage() { return location.protocol === 'file:' || /(^|\.)lms\.ouchn\.cn$/i.test(location.hostname); } async function szCourses(mode, keyword) { const cond = encodeURIComponent(JSON.stringify({ status: [], keyword: '' })); const fields = encodeURIComponent('id,name,display_name,start_date,end_date,is_closed,is_started,completeness'); let j; try { j = await lmsGet('/api/my-courses?conditions=' + cond + '&fields=' + fields + '&page=1&page_size=100'); } catch (e) { j = await lmsGet('/api/my-courses?conditions=' + cond + '&page=1&page_size=100'); } const list = (j.courses || []).map((c) => ({ id: c.id, name: c.name || c.display_name || c.course_name || '', raw: c })).filter((c) => c.id && !(c.raw.is_closed === true || String(c.raw.is_closed || '') === 'true')); if (mode === 'one') { const k = String(keyword || '').replace(/\s+/g, ''); if (!k) return []; return list.filter((c) => c.name.replace(/\s+/g, '').indexOf(k) >= 0); } if (mode === 'all') return list; return list.filter(isSzCourse); } async function allCoursesPaged() { const cond = encodeURIComponent(JSON.stringify({ status: [], keyword: '' })); const fields = encodeURIComponent('id,name,display_name,start_date,end_date,is_closed,is_started,completeness'); const seen = new Set(), out = []; for (let page = 1; page <= 20; page++) { let j; try { j = await lmsGet('/api/my-courses?conditions=' + cond + '&fields=' + fields + '&page=' + page + '&page_size=100'); } catch (e) { break; } const arr = j.courses || []; let added = 0; for (const c of arr) { if (!c.id || seen.has(String(c.id))) continue; seen.add(String(c.id)); if (c.is_closed === true || String(c.is_closed || '') === 'true') continue; out.push({ id: c.id, name: c.name || c.display_name || c.course_name || '', raw: c }); added++; } if (arr.length < 100 || added === 0) break; } return out; } async function szVideosFor(course) { const mods = await lmsGet('/api/courses/' + course.id + '/modules'); const ids = (mods.modules || []).map((m) => m.id).filter(Boolean); if (!ids.length) return []; const acts = await lmsGet('/api/course/' + course.id + '/all-activities?module_ids=' + encodeURIComponent(JSON.stringify(ids)) + '&activity_types=learning_activities,exams,classrooms'); return (acts.learning_activities || []).filter((a) => String(a.type || '') === 'online_video'); } async function szFullSet(course) { const full = new Set(); try { const rd = await lmsGet('/api/course/' + course.id + '/activity-reads-for-user'); for (const r of (rd.activity_reads || [])) { if (String(r.completeness || '') === 'full') full.add(String(r.activity_id)); } } catch (e) {} return full; } async function szDetail(aid) { try { return await lmsGet('/api/activities/' + aid); } catch (e) { return await lmsGet('/api/course/activities/' + aid); } } async function szBrushActivity(course, act, full) { if (full.has(String(act.id))) { log('跳过已完成:' + (act.title || act.id)); return 0; } const detail = await szDetail(act.id); const ups = (detail.uploads || []).filter((u) => u.type === 'video' || (u.videos || []).length); if (!ups.length) { log('没有视频文件:' + (act.title || act.id)); return 0; } let watched = 0; for (const up of ups) { let dur = 0; for (const v of (up.videos || [])) { const d = parseFloat(v.duration || 0); if (isFinite(d) && d > dur) dur = d; } if (!(dur > 0)) { log('视频时长未加载/转码中:' + (act.title || act.id)); continue; } log(`刷 ${course.name} · ${(act.title || act.id).slice(0, 24)}(${Math.round(dur)} 秒)`); for (let t = 0; t < dur - 0.5; t += CFG.CHUNK) { if (!state.szRunning) return watched; const t1 = Math.min(t + CFG.CHUNK, dur); try { const r = await lmsPost('/api/course/activities-read/' + act.id, { start: Math.floor(t), end: Math.floor(t1) }); watched += t1 - t; if (t === 0 || t1 >= dur - 0.5) { log(`上报 ${Math.floor(t)}-${Math.floor(t1)} 秒 → HTTP ${r._status || 200}`); } } catch (e) { const msg = e && e.message ? e.message : String(e); if (msg.indexOf('418') >= 0) { log('平台已是完成态,跳过'); break; } log('上报失败:' + msg); } await sleep(250); } } return watched; } async function runSz(mode, keyword) { const label = mode === 'all' ? '全部课程' : (mode === 'one' ? ('单科「' + keyword + '」') : '思政课'); setAction('正在读取账号里的' + label + '…', true); const courses = await szCourses(mode, keyword); if (!courses.length) { setAction(mode === 'one' ? ('没找到课程:' + keyword) : ('没识别到' + label), true); return; } log('识别到 ' + courses.length + ' 门' + label + ':' + courses.map((c) => c.name).join('、').slice(0, 160)); let videoDone = 0; for (let ci = 0; ci < courses.length; ci++) { if (!state.szRunning) break; const c = courses[ci]; setAction(`刷课 ${ci + 1}/${courses.length}:${c.name}`, true); let videos = []; try { videos = await szVideosFor(c); } catch (e) { log('课程读取失败:' + c.name + '(' + e.message + ')'); continue; } if (!videos.length) { log('没有视频:' + c.name); continue; } const full = await szFullSet(c); log(`课程 ${c.name}:${videos.length} 个视频活动`); for (let vi = 0; vi < videos.length; vi++) { if (!state.szRunning) break; const a = videos[vi]; setAction(`刷课 ${ci + 1}/${courses.length} · 视频 ${vi + 1}/${videos.length}`); try { const n = await szBrushActivity(c, a, full); if (n > 0) videoDone++; } catch (e) { log('视频处理失败:' + (a.title || a.id) + '(' + e.message + ')'); } } } if (state.szRunning) { const doneSub = videoDone ? (videoDone + ' 个视频活动') : '没有需要补刷的视频'; log(label + '处理结束:共完成 ' + videoDone + ' 个视频活动'); setAction(label + '处理结束:' + doneSub, false); showToast('已完成', label + ' · ' + doneSub); } } // ======== 自动答全部课程(形考 / 终考 / 大作业)======== const EXAM_AB = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; const COMPOUND_TYPES = ['matching', 'cloze', 'analysis', 'compound', 'group', 'material']; function stripHtml(html) { try { const d = document.createElement('div'); d.innerHTML = html || ''; return (d.textContent || '').replace(/\s+/g, ' ').trim(); } catch (e) { return String(html || '').replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim(); } } function escapeHtml(s) { return String(s || '').replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); } function xmlEsc(s) { return String(s || '').replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); } function _u16(n) { return new Uint8Array([n & 255, (n >>> 8) & 255]); } function _u32(n) { return new Uint8Array([n & 255, (n >>> 8) & 255, (n >>> 16) & 255, (n >>> 24) & 255]); } function _cat(parts) { let len = 0; for (const p of parts) len += p.length; const out = new Uint8Array(len); let off = 0; for (const p of parts) { out.set(p, off); off += p.length; } return out; } function crc32(buf) { let c = 0xffffffff; for (let i = 0; i < buf.length; i++) { c ^= buf[i]; for (let k = 0; k < 8; k++) c = (c >>> 1) ^ ((c & 1) ? 0xedb88320 : 0); } return (c ^ 0xffffffff) >>> 0; } function zipStore(files) { const enc = new TextEncoder(); const parts = [], central = []; let offset = 0; for (const f of files) { const name = enc.encode(f.name); const data = f.data; const crc = crc32(data); const lh = _cat([_u32(0x04034b50), _u16(20), _u16(0x0800), _u16(0), _u16(0), _u16(0), _u32(crc), _u32(data.length), _u32(data.length), _u16(name.length), _u16(0), name]); parts.push(lh, data); central.push({ name, data, crc, offset }); offset += lh.length + data.length; } const cdStart = offset; const cdParts = []; for (const c of central) { const ch = _cat([_u32(0x02014b50), _u16(20), _u16(20), _u16(0x0800), _u16(0), _u16(0), _u16(0), _u32(c.crc), _u32(c.data.length), _u32(c.data.length), _u16(c.name.length), _u16(0), _u16(0), _u16(0), _u16(0), _u32(0), _u32(c.offset), c.name]); cdParts.push(ch); offset += ch.length; } const cdLen = offset - cdStart; const eocd = _cat([_u32(0x06054b50), _u16(0), _u16(0), _u16(central.length), _u16(central.length), _u32(cdLen), _u32(cdStart), _u16(0)]); return _cat(parts.concat(cdParts, [eocd])); } function makeDocx(title, text) { const enc = new TextEncoder(); const paras = [title].concat(String(text || '').split(/\n+/).map((s) => s.trim()).filter(Boolean)); const body = paras.map((t) => '' + xmlEsc(t) + '').join(''); const doc = '' + '' + '' + body + '' + '' + ''; const types = '' + '' + '' + '' + '' + '' + ''; const rels = '' + '' + '' + ''; const docRels = '' + '' + '' + ''; const styles = '' + '' + '' + ''; return zipStore([ { name: '[Content_Types].xml', data: enc.encode(types) }, { name: '_rels/.rels', data: enc.encode(rels) }, { name: 'word/document.xml', data: enc.encode(doc) }, { name: 'word/styles.xml', data: enc.encode(styles) }, { name: 'word/_rels/document.xml.rels', data: enc.encode(docRels) } ]); } const DOCX_MIME = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; async function uploadDocx(course, title, text) { const bytes = makeDocx(title, text); let name = (course.name + '-' + title).replace(/[\\/:*?"<>|]+/g, '_').slice(0, 60) || '大作业'; name += '.docx'; const j = await lmsPost('/api/uploads', { name: name, size: bytes.length, parent_type: null, parent_id: 0, is_scorm: false, is_wmpkg: false, embed_material_type: '', use_storage: true }); if (!j.id || !j.upload_url) throw new Error('平台未返回上传地址'); const r = await fetch(j.upload_url, { method: 'PUT', credentials: 'omit', headers: { 'Content-Type': DOCX_MIME }, body: bytes }); if (!r.ok) throw new Error('文件直传 HTTP ' + r.status); await lmsPost('/internal-api/upload/callback/' + j.id, { file_key: j.id }); return j.id; } function normText(s) { return String(s || '').toLowerCase().replace(/[^0-9a-z\u4e00-\u9fff]+/g, ''); } function optLabel(o) { return stripHtml((o && o.content) || '').replace(/^[A-Da-d]\s*[.、))::]\s*/, ''); } function isSingleType(t) { return ['single_selection', 'true_or_false', 'true_false', 'single_choice', 'judge', '单选', '判断'].includes(String(t || '')); } function isSubjectiveType(t) { return ['fill_in_blank', 'short_answer', 'analysis', 'essay', 'completion', '填空', '简答', '论述', '名词解释'].includes(String(t || '')); } function mapAnswer(opts, answer, single) { if (!opts || !opts.length || !answer) return []; const sorted = opts.slice().sort((a, b) => (a.sort || 0) - (b.sort || 0)); const a0 = String(answer).trim(); if (!a0) return []; const segs = a0.split(/\/&\/|\|/).map((s) => s.trim()).filter(Boolean); if (segs.length > 1) { const out = []; for (const s of segs) { for (const id of mapAnswer(sorted, s, false)) if (!out.includes(id)) out.push(id); } return single ? out.slice(0, 1) : out; } if (/^[A-Za-z](?:[\s,,、/&;.]*[A-Za-z])*$/.test(a0)) { const letters = (a0.toUpperCase().match(/[A-Z]/g) || []); const sep = /[,,、/&;.]/.test(a0); if (!(single && letters.length > 1 && !sep) && letters.every((L) => EXAM_AB.indexOf(L) < sorted.length)) { const ids = letters.map((L) => sorted[EXAM_AB.indexOf(L)].id).filter(Boolean); return single ? ids.slice(0, 1) : ids; } } if (sorted.length === 2) { const t = a0.replace(/\s+/g, ''); const wrong = /错误|错|不对|×|✗|false/i.test(t); const right = !wrong && /正确|对|是|√|✓|true/i.test(t); if (wrong || right) { for (const o of sorted) { const c = optLabel(o); if (wrong ? /错误|错|否|×|✗|false/i.test(c) : /正确|对|是|√|✓|true/i.test(c)) { return [o.id]; } } } } const raw = stripHtml(a0); const clean = raw.replace(/^[A-Da-d]\s*[.、))::]\s*/, ''); const na = normText(clean); if (na) { for (const o of sorted) if (normText(optLabel(o)) === na) return [o.id]; if (na.length >= 2) { for (const o of sorted) { const no = normText(optLabel(o)); if (no && (no.includes(na) || na.includes(no))) return [o.id]; } } } return []; } async function imgKey(html) { try { if (!html || String(html).toLowerCase().indexOf(']*>/gi; let m; while ((m = re.exec(html))) { const sm = /src\s*=\s*["']([^"']+)["']/i.exec(m[0]); if (sm) srcs.push(sm[1]); } if (!srcs.length) return ''; srcs.sort(); const buf = await crypto.subtle.digest('SHA-1', new TextEncoder().encode(srcs.join('|'))); const hex = Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, '0')).join(''); return 'img:' + hex.slice(0, 16); } catch (e) { return ''; } } function leavesOf(subject, pool) { const ty = String((subject && subject.type) || ''); const subs = (subject && subject.sub_subjects) || []; const cur = ((subject && subject.options) || []).length ? subject.options : (pool || []); if (COMPOUND_TYPES.includes(ty) && subs.length) { let out = []; for (const c of subs) out = out.concat(leavesOf(c, cur)); return out; } return [{ s: subject, opts: cur }]; } async function askOne(qtext, qtype, options, courseName) { const j = await api('/dati/api/answer', { token: token(), device: deviceId(), course: courseName || guessCourse(), question: qtext, qtype: qtype, options: options || [] }, 60000); if (!j.ok) { const e = new Error(j.error || ('答案服务 HTTP ' + j._status)); e.budget = j._status === 429; throw e; } return j; } async function aiDoc(prompt) { const j = await api('/dati/api/ai/chat', { token: token(), device: deviceId(), task: 'doc', prompt: String(prompt || '').slice(0, 12000), max_tokens: 1600 }, 120000); if (!j.ok) { const e = new Error(j.error || ('写作业服务 HTTP ' + j._status)); e.budget = j._status === 429; throw e; } return String(j.content || '').trim(); } function fillParts(answer, subject) { let parts = String(answer || '').split(/\/&\/|[;;\n]/).map((s) => s.trim()).filter(Boolean); let n = parseInt(subject.answer_number, 10); if (!n) n = ((subject.description || '').match(/__blank__/g) || []).length || parts.length || 1; while (parts.length < n) parts.push(''); return parts.slice(0, n); } async function courseAssessments(course) { const mods = await lmsGet('/api/courses/' + course.id + '/modules'); const mids = (mods.modules || []).map((m) => m.id).filter(Boolean); let acts = { exams: [], learning_activities: [] }; if (mids.length) { try { acts = await lmsGet('/api/course/' + course.id + '/all-activities?module_ids=' + encodeURIComponent(JSON.stringify(mids)) + '&activity_types=learning_activities,exams,classrooms'); } catch (e) { for (const mid of mids) { try { const one = await lmsGet('/api/course/' + course.id + '/all-activities?module_ids=' + encodeURIComponent(JSON.stringify([mid])) + '&activity_types=learning_activities,exams,classrooms'); acts.exams = (acts.exams || []).concat(one.exams || []); acts.learning_activities = (acts.learning_activities || []).concat(one.learning_activities || []); } catch (e2) {} } } } const seenE = new Set(), seenH = new Set(); const exams = [], hws = []; for (const e of (acts.exams || [])) { if (!e || !e.id || seenE.has(String(e.id))) continue; seenE.add(String(e.id)); const ename = e.name || e.title || ('考试 ' + e.id); if (e.is_closed) { log('跳过已截止:' + ename); continue; } const sp = parseFloat(e.score_percentage || 0); if (!(sp > 0) && !e.is_formative && !e.is_final) { log('跳过不计分练习:' + ename); continue; } if ((Array.isArray(e.exam_submissions) && e.exam_submissions.length) || (e.exam_submissions && !Array.isArray(e.exam_submissions))) { log('跳过已交:' + ename); continue; } exams.push(e); } for (const a of (acts.learning_activities || [])) { if (!a || !a.id || seenH.has(String(a.id))) continue; const d = a.data || {}; if (!d.homework_type && String(a.type || '') !== 'homework') continue; seenH.add(String(a.id)); const t = String(a.title || a.name || ''); if (a.is_closed || a.submission_closed || (a.data && a.data.submission_closed)) { log('跳过已截止:' + (t || ('作业 ' + a.id))); continue; } const sp = parseFloat(d.score_percentage || a.score_percentage || 0); if (!(sp > 0) && !/大作业|形考|作业/.test(t)) continue; if (/学习行为|学习心得/.test(t)) { log('跳过学习行为/心得:' + t); continue; } hws.push(a); } return { exams, hws }; } async function answerExam(course, exam) { const eid = exam.id; let ename = exam.name || exam.title || ''; if (!ename) { try { const ej = await lmsGet('/api/exams/' + eid); ename = ej.name || ej.title || ej.display_name || ''; } catch (e) {} } ename = ename || ('考试 ' + eid); if (state.ansStop) return { status: 'stopped' }; try { const old = await lmsGet('/api/exams/' + eid + '/submissions'); if (old && (old.submissions || []).length) { log('跳过已交:' + ename + '(最好 ' + Math.max.apply(null, (old.submissions || []).map((x) => parseFloat(x.score || 0))) + ' 分)'); return { status: 'skip' }; } } catch (e) {} let qual = {}; try { qual = await lmsGet('/api/exam/' + eid + '/check-exam-qualification?no-intercept=true&check_status=start'); } catch (e) { log('资格检查失败(继续取卷):' + ename + '(' + e.message + ')'); } if (qual && qual.is_closed) { log('跳过已截止:' + ename + (qual.message ? '(' + qual.message + ')' : '')); return { status: 'skip' }; } try { await lmsPost('/api/course/activities-read/exam/' + eid, {}); } catch (e) {} let dist; try { dist = await lmsGet('/api/exams/' + eid + '/distribute'); } catch (e) { log('取卷失败:' + ename + '(' + e.message + ';可能要先在页面点「开始答题」)'); return { status: 'fail' }; } const data = (dist && dist.subjects) ? dist : ((dist && dist.data) || {}); const trees = data.subjects || []; const inst = data.exam_paper_instance_id || (dist && dist.exam_paper_instance_id); if (!trees.length || !inst) { log('平台未下发题目:' + ename); return { status: 'skip' }; } let leaves = []; for (const t of trees) leaves = leaves.concat(leavesOf(t, null)); leaves = leaves.filter((x) => String((x.s && x.s.type) || '') !== 'text'); const total = leaves.length; log('开始答:' + ename + '(' + total + ' 题)'); const payload = []; let hit = 0, skipped = 0; for (let i = 0; i < leaves.length; i++) { if (state.ansStop) { log('已停止答题,本卷未交:' + ename); return { status: 'stopped' }; } const s = leaves[i].s || {}; const opts = (leaves[i].opts || []).slice().sort((a, b) => (a.sort || 0) - (b.sort || 0)); const ty = String(s.type || ''); setAction('答题 ' + course.name + ' · ' + ename + ' · ' + (i + 1) + '/' + total, true); let qtext = stripHtml(s.description || ''); if (qtext.length < 4) { const k = await imgKey(s.description || ''); if (k) qtext = k; } if (!qtext) { skipped++; continue; } if (opts.length) { try { const j = await askOne(qtext, ty, opts.map((o) => stripHtml(o.content || '').slice(0, 200)).slice(0, 20), course.name); const ids = mapAnswer(opts, j.answer, isSingleType(ty)); if (ids.length) { payload.push({ subject_id: s.id, subject_updated_at: s.last_updated_at, answer_option_ids: ids }); hit++; log('[' + (i + 1) + '/' + total + '] 选择题已答(' + (j.source === 'bank' ? '题库' : 'AI') + ')'); } else { skipped++; log('[' + (i + 1) + '/' + total + '] 选择题未匹配:' + qtext.slice(0, 26)); } } catch (e) { if (e.budget) { state.ansStop = true; log('答题额度已用完,停止本次答题:' + e.message); return { status: 'stopped' }; } skipped++; log('[' + (i + 1) + '/' + total + '] 取答失败:' + e.message); } } else if (isSubjectiveType(ty)) { try { const j = await askOne(qtext, ty, [], course.name); if (j.answer) { if (ty === 'fill_in_blank' || ty === '填空') { const parts = fillParts(j.answer, s); payload.push({ subject_id: s.id, subject_updated_at: s.last_updated_at, answers: parts.map((content, idx) => ({ sort: idx, content: content })) }); } else { payload.push({ subject_id: s.id, subject_updated_at: s.last_updated_at, answer: j.answer, attachments: [] }); } hit++; log('[' + (i + 1) + '/' + total + '] ' + ty + ' 已答(' + (j.source === 'bank' ? '题库' : 'AI') + ')'); } else { skipped++; log('[' + (i + 1) + '/' + total + '] ' + ty + ' 没有答案,留人工'); } } catch (e) { if (e.budget) { state.ansStop = true; log('答题额度已用完,停止本次答题:' + e.message); return { status: 'stopped' }; } skipped++; log('[' + (i + 1) + '/' + total + '] ' + ty + ' 取答失败:' + e.message); } } else { skipped++; log('[' + (i + 1) + '/' + total + '] ' + (ty || '未知题型') + ' 留人工'); } await sleep(120); } const kept = payload.filter((x) => { const n = parseInt(x.subject_id, 10); return Number.isFinite(n) && n >= 1; }); payload.length = 0; payload.push.apply(payload, kept); log('作答结束:命中 ' + hit + '/' + total + ',跳过 ' + skipped); if (!payload.length) { log('没有可提交的作答,未交卷:' + ename); return { status: 'skip' }; } if (state.ansStop) { log('已停止答题,本卷未交:' + ename); return { status: 'stopped' }; } const st = await lmsPost('/api/exams/' + eid + '/submissions/storage', { exam_paper_instance_id: inst, exam_submission_id: null, subjects: payload }); const sid = st && (st.exam_submission_id || st.id || st.submission_id); if (state.ansStop) { log('已停止答题,本卷已暂存未交:' + ename); return { status: 'stopped' }; } const answered = payload.filter((x) => (x.answer_option_ids && x.answer_option_ids.length) || x.answer || (x.answers && x.answers.some((a) => a.content))).length; const sub = await lmsPost('/api/exams/' + eid + '/submissions', { exam_paper_instance_id: inst, exam_submission_id: sid || null, subjects: payload, progress: { answered_num: answered, total_subjects: payload.length }, reason: 'user' }); let score = sub && (sub.exam_score !== undefined ? sub.exam_score : sub.score); if (score === undefined) { try { const ls = await lmsGet('/api/exams/' + eid + '/submissions'); const arr = (ls && ls.submissions) || []; if (arr.length) score = arr[arr.length - 1].score; } catch (e) {} } log('交卷完成:' + ename + (score !== undefined ? ',得分 ' + score : '')); return { status: 'done', score: score }; } async function answerHomework(course, hw) { const aid = hw.id; let detail = {}; try { detail = await lmsGet('/api/activities/' + aid); } catch (e) { log('作业详情读取失败:' + aid + '(' + e.message + ')'); return { status: 'fail' }; } const data = detail.data || {}; const title = detail.title || detail.name || hw.title || hw.name || ('作业 ' + aid); if (detail.user_submit_count > 0) { log('跳过已交:' + title); return { status: 'skip' }; } if (detail.accepting_submission === false) { log('跳过已停止收件:' + title); return { status: 'skip' }; } if (detail.is_closed || detail.submission_closed || data.submission_closed) { log('跳过已截止:' + title); return { status: 'skip' }; } if (state.ansStop) { log('已停止,本作业未交:' + title); return { status: 'stopped' }; } const lim = parseInt(detail.submit_times || 0, 10); const used = parseInt(detail.user_submit_count || 0, 10); if (lim && !detail.non_submit_times && used >= lim) { log('跳过提交次数已用完:' + title); return { status: 'skip' }; } const hwType = String(data.homework_type || ''); if (!hwType) { log('跳过非作业:' + title); return { status: 'skip' }; } const desc = stripHtml(data.description || ''); if (/无需提交|不需要提交|不用提交|线下完成|课堂完成|面授|专题讨论|辅导教师评分|教师评分|录音|口语|听说/.test(title + ' ' + desc)) { log('跳过(题目要求人工/线下/录音):' + title); return { status: 'skip' }; } const needFile = /upload|file|attachment|附件|文档/i.test(hwType) || /上传附件|上传文档|以附件形式|附件提交/.test(desc); const prompt = '你是国开课程作业助手。请直接写出完整作业正文,分点分段、条理清楚,不少于 600 字;' + '不要复述题目,不要写“答案:”。\n课程:' + course.name + '\n作业标题:' + title + (desc ? '\n作业要求:' + desc.slice(0, 1200) : ''); let ans = '', src = '', aiAns = ''; try { const j = await askOne(title, 'essay', [], course.name); if (j.answer && j.source === 'bank') { ans = String(j.answer).trim(); src = 'bank'; } else if (j.answer) { aiAns = String(j.answer).trim(); src = 'ai'; } } catch (e) { if (e.budget) { state.ansStop = true; log('答题额度已用完,停止本次答题:' + e.message); return { status: 'stopped' }; } log('作业题库取答失败:' + title + '(' + e.message + ')'); } if (!ans) { try { ans = await aiDoc(prompt); src = 'doc'; } catch (e) { if (e.budget) { state.ansStop = true; log('答题额度已用完,停止本次答题:' + e.message); return { status: 'stopped' }; } if (aiAns) { ans = aiAns; src = 'ai'; } else { log('大作业生成失败:' + title + '(' + e.message + ')'); return { status: 'fail' }; } } } if (!ans) { log('大作业没有生成内容:' + title); return { status: 'skip' }; } if (state.ansStop) { log('已停止,本作业未交:' + title); return { status: 'stopped' }; } const body = ans.split(/\n+/).map((s) => s.trim()).filter(Boolean) .map((s) => '

' + escapeHtml(s) + '

').join(''); let uploads = []; if (needFile) { try { uploads = [await uploadDocx(course, title, ans)]; } catch (e) { log('大作业附件上传失败:' + title + '(' + e.message + ')'); return { status: 'fail' }; } } const r = await lmsPost('/api/course/activities/' + aid + '/submissions', { comment: needFile ? '

详见附件。

' : body, uploads: uploads, slides: [], is_draft: false, is_return: false, mode: 'normal' }); log('大作业已提交' + (needFile ? '(附件)' : '') + ':' + title + '(' + (src === 'bank' ? '题库' : 'AI') + ',' + ans.length + ' 字)' + (r && r.message ? ' · ' + r.message : '')); try { await lmsPost('/api/course/activities-read/' + aid, {}); } catch (e) {} return { status: 'done' }; } async function runAnswerAll() { const courses = await allCoursesPaged(); if (!courses.length) { setAction('没识别到课程,请确认已登录学习网', true); log('自动答题:没有识别到课程'); return; } log('识别到 ' + courses.length + ' 门课程,开始自动答形考/终考/大作业'); let examDone = 0, examSkip = 0, hwDone = 0, hwSkip = 0, failed = 0; for (let ci = 0; ci < courses.length; ci++) { if (state.ansStop) break; const c = courses[ci]; setAction('答题 ' + (ci + 1) + '/' + courses.length + ':' + c.name, true); let pack; try { pack = await courseAssessments(c); } catch (e) { log('课程读取失败:' + c.name + '(' + e.message + ')'); failed++; continue; } if (!pack.exams.length && !pack.hws.length) { log('没有待答的形考/终考/大作业:' + c.name); continue; } log('课程 ' + c.name + ':考试 ' + pack.exams.length + ' 份,大作业 ' + pack.hws.length + ' 个'); for (const ex of pack.exams) { if (state.ansStop) break; let r; try { r = await answerExam(c, ex); } catch (e) { log('考试处理失败:' + (ex.name || ex.id) + '(' + e.message + ')'); r = { status: 'fail' }; } if (r.status === 'done') examDone++; else if (r.status === 'skip') examSkip++; else if (r.status === 'fail') failed++; } for (const hw of pack.hws) { if (state.ansStop) break; let r; try { r = await answerHomework(c, hw); } catch (e) { log('大作业处理失败:' + (hw.title || hw.name || hw.id) + '(' + e.message + ')'); r = { status: 'fail' }; } if (r.status === 'done') hwDone++; else if (r.status === 'skip') hwSkip++; else if (r.status === 'fail') failed++; } } const summary = examDone + ' 份考试 · ' + hwDone + ' 个大作业' + ((examSkip + hwSkip) ? (' · 跳过 ' + (examSkip + hwSkip)) : '') + (failed ? (' · 失败 ' + failed) : ''); if (state.ansStop) { log('答题已停止:' + summary); setAction('答题已停止:' + summary, false); return; } log('答题结束:' + summary); setAction('已完成:' + summary, false); showToast('已完成', summary || '没有待答任务'); } async function startAnswerAll() { if (state.ansBusy || state.szBusy) return; if (!onLmsPage()) { setAction('请先打开学习网课程页(lms.ouchn.cn)', true); return; } gmSet('gk_answer_autostart', 0); setAction('正在检查登录和学号…', true); if (!(await ensureAuth())) { setAction('请先选择免费体验或注册', true); return; } if (!(await bindStudent(false))) { setAction('学号没识别到,请看日志', true); return; } state.ansBusy = true; state.ansRunning = true; state.ansStop = false; btnSz.disabled = true; btnAns.disabled = true; btnOne.disabled = true; btnSzPause.disabled = true; btnAnsStop.style.display = ''; btnAnsStop.disabled = false; try { await runAnswerAll(); } catch (e) { setAction('答题失败:' + (e && e.message ? e.message : e), true); log('答题失败:' + (e && e.message ? e.message : e)); } finally { state.ansBusy = false; state.ansRunning = false; btnSz.disabled = false; btnAns.disabled = false; btnOne.disabled = false; btnAnsStop.style.display = 'none'; btnAnsStop.disabled = true; if (!state.szRunning) { btnSzPause.disabled = true; btnSzPause.textContent = '暂停'; } } } async function ansEntry() { if (onLmsPage()) { startAnswerAll(); return; } if (!/(^|\.)menhu\.pt\.ouchn\.cn$/i.test(location.hostname)) { setAction('请先打开学习网课程页(lms.ouchn.cn)', true); return; } setAction('正在检查登录…', true); if (!(await ensureAuth())) { setAction('请先选择免费体验或注册', true); return; } const href = findCourseLink('all', ''); if (!href) { setAction('没找到「去学习」链接,请先点一门课进入学习网', true); return; } gmSet('gk_answer_autostart', { ts: Date.now() }); setAction('正在打开课程页面,进入后会自动答题…', true); location.href = href; } function findCourseLink(mode, keyword) { try { const links = Array.from(document.querySelectorAll('a')).filter((a) => /去学习/.test((a.innerText || '').trim())); if (!links.length) return ''; const key = String(keyword || '').replace(/\s+/g, ''); for (const a of links) { let p = a; for (let i = 0; i < 6 && p; i++, p = p.parentElement) { const t = (p.innerText || '').replace(/\s+/g, ''); if (mode === 'one' && key && t.indexOf(key) >= 0) return a.href || ''; if (mode === 'sz' && SZ_WORDS.some((w) => t.indexOf(w) >= 0)) return a.href || ''; } } // 单科/全部没匹配到具体课名也行:先进 LMS,再按课程名筛 return links[0].href || ''; } catch (e) { return ''; } } async function szEntry(mode, keyword) { mode = mode || 'sz'; if (onLmsPage()) { startSz(mode, keyword); return; } if (!/(^|\.)menhu\.pt\.ouchn\.cn$/i.test(location.hostname)) { setAction('请先打开学习网课程页(lms.ouchn.cn)', true); return; } setAction('正在检查登录…', true); if (!(await ensureAuth())) { setAction('请先选择免费体验或注册', true); return; } const href = findCourseLink(mode, keyword); if (!href) { setAction('没找到「去学习」链接,请先点一门课进入学习网', true); return; } gmSet('gk_sz_autostart', { ts: Date.now(), mode: mode, course: keyword || '' }); setAction('正在打开课程页面,进入后会自动开始…', true); location.href = href; } async function startSz(mode, keyword) { if (state.szBusy || state.ansBusy) return; mode = mode || 'sz'; if (!onLmsPage()) { setAction('请先打开学习网课程页(lms.ouchn.cn)', true); return; } gmSet('gk_sz_autostart', 0); state.szLastMode = mode; state.szLastCourse = keyword || ''; setAction('正在检查登录和学号…', true); if (!(await ensureAuth())) { setAction('请先选择免费体验或注册', true); return; } if (!(await bindStudent(false))) { setAction('学号没识别到,请看日志', true); return; } state.szBusy = true; state.szRunning = true; state.szPaused = false; btnSz.disabled = true; btnAns.disabled = true; btnOne.disabled = true; btnAnsStop.style.display = 'none'; btnSzPause.disabled = false; btnSzPause.textContent = '暂停'; try { await runSz(mode, keyword); } catch (e) { setAction('刷课失败:' + (e && e.message ? e.message : e), true); log('刷课失败:' + (e && e.message ? e.message : e)); } finally { state.szBusy = false; state.szRunning = false; btnSz.disabled = false; btnAns.disabled = false; btnOne.disabled = false; if (state.szPaused) { btnSzPause.disabled = false; btnSzPause.textContent = '继续'; } else { btnSzPause.disabled = true; btnSzPause.textContent = '暂停'; } } } btnSz.addEventListener('click', () => szEntry('sz', '')); btnAns.addEventListener('click', () => ansEntry()); btnOne.addEventListener('click', () => { const name = courseInput.value.trim(); if (!name) { setAction('先填写课程名称,再点「刷这一科」', false); return; } szEntry('one', name); }); btnSzPause.addEventListener('click', () => { if (state.szRunning) { state.szPaused = true; state.szRunning = false; btnSzPause.disabled = true; btnSzPause.textContent = '暂停中…'; setAction('已暂停,等当前请求结束后可继续', true); log('已暂停'); return; } if (state.szPaused && !state.szBusy) { state.szPaused = false; szEntry(state.szLastMode || 'sz', state.szLastCourse || ''); } }); btnAnsStop.addEventListener('click', () => { if (!state.ansRunning) return; state.ansStop = true; btnAnsStop.disabled = true; setAction('正在停止答题,当前这道题答完就停…', true); log('已请求停止答题'); }); el('#redeem').addEventListener('click', async () => { if (!AUTH.token) { log('先注册登录再兑换'); return; } const code = el('#code').value.trim(); if (!code) { log('先填卡密'); return; } try { const j = await api('/dati/api/redeem', { token: AUTH.token, device: deviceId(), code: code }); if (!j.ok) throw new Error(j.error || ('HTTP ' + j._status)); log('兑换成功:+' + j.added + ' 点,当前 ' + j.credits + ' 点'); el('#code').value = ''; loadUsage(); } catch (e) { log('兑换失败:' + e.message); } }); if (typeof GM_registerMenuCommand === 'function') { GM_registerMenuCommand('打开国开助手面板', () => setOpen(true)); } setOpen(!!gmGet(CFG.PANEL_KEY, 0)); updateAuthline(); refreshPageInfo(); renderQuota(); log('脚本已加载(v0.6.7)'); resolveStudent().then((no) => { if (no) { log('学号已识别:' + maskStudent(no) + '(无需手动绑定)'); refreshPageInfo(); } }); if (isAuthed()) loadUsage(); const _szRaw = gmGet('gk_sz_autostart', 0); const _szAuto = (_szRaw && typeof _szRaw === 'object') ? _szRaw : (Number(_szRaw) > 0 ? { ts: Number(_szRaw), mode: 'sz', course: '' } : null); if (_szAuto && Date.now() - Number(_szAuto.ts || 0) < 120000 && onLmsPage() && isAuthed() && !/\/content(\?|$)/.test(location.pathname)) { gmSet('gk_sz_autostart', 0); setOpen(true); log('检测到从门户跳转:准备自动刷课'); setTimeout(() => { startSz(_szAuto.mode || 'sz', _szAuto.course || ''); }, 1600); } const _ansRaw = gmGet('gk_answer_autostart', 0); const _ansAuto = (_ansRaw && typeof _ansRaw === 'object' && Date.now() - Number(_ansRaw.ts || 0) < 120000) ? _ansRaw : null; if (_ansAuto && onLmsPage() && isAuthed()) { gmSet('gk_answer_autostart', 0); setOpen(true); log('检测到从门户跳转:准备自动答题'); setTimeout(() => { startAnswerAll(); }, 1600); } setInterval(refreshPageInfo, 5000); })();