// ==UserScript== // @name 文才学堂学习插件 // @namespace https://wencai.qktk.online/ // @version 0.6.6 // @description 文才学堂学习的插件,自动播放视频,自动完成作业,一建批量评论,考试一键搜集答案和一键答题 // @author zhou // @match *://*.wencaischool.net/* // @connect api.v1.kebaide.work // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @grant unsafeWindow // @contributionURL https://scriptcat.org/api/v2/resource/image/uNHP8PXXIuZkXvt1 // ==/UserScript== (async function () { 'use strict'; function n() { let i = [], e = "0123456789abcdef"; for (let a = 0; a < 36; a++)i[a] = e.substr(Math.floor(Math.random() * 16), 1); return i[14] = "4", i[19] = e.substr(+i[19] & 3 | 8, 1), i[8] = i[13] = i[18] = i[23] = "-", i.join("") };var baseUrl = 'https://api.v1.kebaide.work/api'; var TrackingSDK = function () { "use strict"; var d = Object.defineProperty; var o = (n, s, t) => s in n ? d(n, s, { enumerable: !0, configurable: !0, writable: !0, value: t }) : n[s] = t; var r = (n, s, t) => o(n, typeof s != "symbol" ? s + "" : s, t); function n() { let i = [], e = "0123456789abcdef"; for (let a = 0; a < 36; a++)i[a] = e.substr(Math.floor(Math.random() * 16), 1); return i[14] = "4", i[19] = e.substr(+i[19] & 3 | 8, 1), i[8] = i[13] = i[18] = i[23] = "-", i.join("") } const t = class t { constructor(e = {}) { r(this, "enterTime", Date.now()); r(this, "enterId"); r(this, "baseUrl", "https://store.qktk.online/api/v1/"); r(this, "params", { event_type: 0, device_id: this.getDeviceId(), user_id: "", page_url: location.href }); r(this, "onload", () => { fetch(this.baseUrl + "tracking_data/leave?" + this.qsStrginfy({ id: this.enterId, duration: Date.now() - this.enterTime }), { mode: "no-cors" }) }); this.params = { ...this.params, ...e }, this.send(), window.addEventListener("unload", this.onload), window.addEventListener("beforeunload", this.onload) } static getInstance(e) { return t.instance || (t.instance = new t(e)), t.instance } getDeviceId() { const e = GM_getValue("device_id", n()); return GM_setValue("device_id", e), e } send(e = {}) { e.session_id = n(), this.enterId = this.enterId || e.session_id, fetch(this.baseUrl + "tracking_data?" + this.qsStrginfy({ ...this.params, ...e }), { mode: "no-cors" }) } click(e) { this.send({ event_type: 1, action: e }) } error(e) { this.send({ event_type: 2, custom_attributes: JSON.stringify(e) }) } qsStrginfy(e) { return Object.keys(e).map(a => `${a}=${e[a]}`).join("&") } }; r(t, "instance"); let s = t; return s }(); function dedatas(n){return new Promise((t,e)=>{GM_xmlhttpRequest({method:"POST",url:baseUrl+"/client/deComponentURL",responseType:"json",data:JSON.stringify({str:n}),headers: {'Content-Type': 'application/json'},onload:e=>{e=e.response;e.success&&t(e.data)}})})}function getActionURL(n,o,s){return new Promise((t,e)=>{GM_xmlhttpRequest({method:"POST",url:baseUrl+"/client/getAction",responseType:"json",data:JSON.stringify({action:n,html:o,handle:s}),headers: {'Content-Type': 'application/json', 'handle': $api._gap_},onload:e=>{e=e.response;e.success&&t(e.data)}})})}function fetchPluginId(){return new Promise((t,e)=>{GM_xmlhttpRequest({url:baseUrl+"/client/getId",method:"GET",responseType:"json",onload(e){e=e.response;e?.success?t(e.data):t(Date.now().toString(16))}})})}function adv(){var e,t=window.top.document;t.querySelector("#tk_ad")||((e=document.createElement("div")).id="tk_ad",e.style.cssText=`position: absolute;top: 4px;left: 50%;transform: translateX(-50%);background: white;color: red;padding: 4px 6px;font-size: 14px;z-index:999;`,e.innerHTML='广告毕业设计和论文代写+QQ:139113163',t.body.appendChild(e))} const pluginId = GM_getValue("pluginId") || await fetchPluginId(); GM_setValue("pluginId", pluginId); var $ = window.unsafeWindow.$, TK; function sleep(t) { return new Promise(resolve => { setTimeout(() => { resolve() }, t) }) } const NOW = (pluginId) => eval(function(p,a,c,k,e,r){e=String;if('0'.replace(0,e)==0){while(c--)r[e(c)]=k[c];k=[function(e){return r[e]||e}];e=function(){return'^$'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('0x' + pluginId,[],1,''.split('|'),0,{})); function getDeviceId() { const e = GM_getValue("device_id", n()); return GM_setValue("device_id", e), e } // async function user(){let e=GM_getValue("tk_user");e||(e={device_id:getDeviceId(),now: NOW});if(Date.now()-e.t<3*60*1000)return e;const t=new URL(baseUrl+"/user/me");Object.keys(e).forEach(a=>{t.searchParams.append(a,e[a])});try{const a=await fetch(t.href).then(e=>e.json());e=a.data}catch(e){};e.t=Date.now();GM_setValue('tk_user', e);return e}; async function user() { return {device_id:getDeviceId(),now: NOW(pluginId)} } const myU = await user(); // adv() /**=========================批量评论start==================================== */ let $topciBtn function initTopic() { const $btn = $('
批量发话题
') $('.btnGroup').append($btn) $topciBtn = $btn $btn.on('click', () => { const content = $('#searchKey')?.val() || '好好学习,天天向上' autoSendTopic(5, content) TK.click(5) }) } function autoSendTopic(len = 5, content = '好好学习,天天向上') { function addTopic(content, ifAsk = '0', articleImgs = '') { $topciBtn.text(`正在发表(${len})`) var params = { 'user_id': getQueryString('userId'), 'course_id': getQueryString('courseId'), 'school_code': getQueryString('schoolCode'), 'grade_code': getQueryString('gradeCode'), 'course_code': getQueryString('courseCode'), 'content': content, 'is_ask': ifAsk, 'img_url': articleImgs, 'tk_key': NOW, 'time': 0 }, url = urlStart + '/forum_article.action?req=publishArticle', res = getData(url, params); len-- setTimeout(() => { if (len > 0) { addTopic(content, ifAsk, articleImgs) } else { alert('发表完成') location.reload() } }, 1000) } addTopic(content) } const topMatch = new URLPattern({ hostname: location.hostname, pathname: "*/coursePost/index.html" }) if (topMatch.test(location.href) && (TK = new TrackingSDK())) initTopic() /**=========================批量评论end==================================== */ if (myU.now <= Date.now()) { const pluginId = await fetchPluginId(); GM_setValue("pluginId", pluginId); myU.now = NOW(pluginId); } /**=========================批量视频start==================================== */ let $videoBtn function initVideo() { TK = new TrackingSDK() const $btn = $('
一键批量完成
') $('.lefttop').append($btn).css({ 'text-align': 'center', height: "auto" }).find('.btn').css({ display: 'inline-block', margin: '5px 10px' }); $videoBtn = $btn $btn.on('click', () => { autoPlayList() }) } let autoPlayed = false async function autoPlayList() { if (autoPlayed) return alert('程序正在执行,请稍等') TK.click(1) autoPlayed = true async function getChapterList() { var params = { 'course_id': getQueryString('course_id') } var url = urlStart + '/newApp_learn_course.action?req=getCourseScormItemList', res = getData(url, params), str = ''; if (res.code === 1000) { var resdata = await dedatas(res.data); list = resdata.listCourseLesson } } async function getPower() { var url = urlStart + '/newApp_use_energy.action?req=getUserAuthority', res = getData(url); if (res.code === 1000) { var data = await dedatas(res.data) learning_user_id = data.userId } } var i = 0, learning_user_id = 3518, list = [] async function useEnergy(siid, courseId) { var params = { 'learning_user_id': getQueryString('user_id'), 'course_id': courseId, 'type_code': 'progress', 'item_id': siid }, url = urlStart + '/newApp_use_energy.action?req=saveUseEnergyInfo', res = getData(url, params), ret = false; if (res.code == 1000) { ret = true; var data = await dedatas(res.data); $('#lastEnergy').text(data); } else { layer.msg(res.message, { time: 1000 }); } return ret; } async function saveSubmit(item) { var params = { 'user_id': getQueryString('user_id'), 'course_id': item.courseId, 'time': item.timeLen, 'item_id': item.lessonId, 'view_time': item.timeLen, 'last_view_time': item.timeLen - 1, 'video_length': item.timeLen, 'learning_user_id': learning_user_id, 'tk_key': myU.now, }, url = urlStart + '/learning.action?req=submitScormAndHistorySave', res = getData(url, params); if (res.code == 1000) { var data = await dedatas(res.data) if (data.isFinish) { console.log(item.lessonName + '完成') i++ playNext() } } } async function autoPlay(item) { var energy = true if (item.useEnergyNum == 1) { energy = await useEnergy(item.lessonId, item.courseId) } var t = setTimeout(() => { if (!energy) return false saveSubmit(item) }, 1000) } async function playNext() { var item = list[i] if (!item) return alert('已全部完成,请切换下一个课程') $videoBtn.text(`已完成(${i + 1}/${list.length})`) if (item.isFinish || item.lessonType != 'scorm_content') { console.log(`=====${item.lessonName}=====`) i++ await playNext() } else { await autoPlay(item) } } await Promise.all([getChapterList(), getPower()]) playNext() } const VMatch = new URLPattern({ hostname: location.hostname, pathname: "*/courseware/index.html" }) if (VMatch.test(location.href) && (TK = new TrackingSDK())) if (Date.now() < myU.now) initVideo() /**=========================批量视频end==================================== */ /**=========================批量作业start==================================== */ let $workBtn, worked = false, baseurl, answerObj = {}, sExamStatus, examid, workIndex = -1, workLen = 0; async function initWork() { TK = new TrackingSDK() const $btn = $('一键完成作业') $('.func').after($btn) $btn.css('margin-left', '10px') $workBtn = $btn; $btn.bind('click', async () => { if (worked) return alert('正在做题中...') worked = true $workBtn.text('正在自动做题中...') const workList = document.querySelectorAll('td[examid]') workLen = workList.length workIndex = -1 baseurl = $fn.getLearningWeb() TK.click(2) try { for (let i = 0; i < workList.length; i++) { const li = workList[i] examid = li.getAttribute('examid') const contentId = li.getAttribute('contentid') workIndex++ await autoWork(contentId) $workBtn.text(`已完成${i + 1}/${workList.length}`) await sleep(1000) } alert('已全部完成') webGrid?.doRefresh() } catch (e) { console.log(e) alert('操作失败,请联系管理员') TK.error(e) $workBtn.text('一键完成作业') } // location.reload() }) } async function autoWork(sContentId, prevId) { const chech = await fetch(`${baseurl}/course/check_pre_index.jsp?content_id=${sContentId}`).then(res => res.text()) if (chech.indexOf('ERROR:') > -1) return alert('数据错误,请手动完成作业') const sUrl = baseurl + "/exam/portal/exam_info.jsp?content_id=" + sContentId + "&type=work&is_make_up=undefined"; let html = await fetch(sUrl).then(res => res.text()) html = html.replace(/\\/g, '') let scoreId = html.match(/sScoreId\s?=\s?"([\d\w]+)"/)?.[1] sExamStatus = /sExamStatus\s?=\s?"(\w+)"/.exec(html)[1] const score = html.match(/(\d+)\(\1\/100\)/)?.[1] console.log(score, scoreId, sExamStatus) if (score > 90) return if (sExamStatus === 'reexamine') { // 查看答案 const params = $wapper.api.getURLParams($wapper.api.setQueryString("reexamine", '0', sUrl)) const answers = await viewAnswerN(scoreId, params) answerObj[sContentId] = answers // console.log(answers) if (!answers) return alert(`id=${sContentId}题目获取答案失败,请手动操作`) await doWork(sContentId) } else { if (sContentId === prevId) return // 先做一遍题 await doWork(sContentId) await sleep(1000) await autoWork(sContentId, sContentId) } } function disposeAnswer(ans = '') { ans = Array.isArray(ans) ? ans.join('|') : ans || '' if (ans.indexOf('答') === 0) return ans.substring(2) return /\[参考答案:([^\]]+)\]/ig.exec(ans)?.[1] || '' } function disposeNames(list) { const nameM = [...new Set(list.map(name => /\w{28,}/.exec(name)[0]).filter(name => name.split('_').length >= 4))] let currentName = '', nameSet = new Set() nameM.forEach(name => { const cname = strDiff(currentName, name) cname ? nameSet.add(cname) : nameSet.add(name) if (cname === name) { currentName && nameSet.delete(currentName) } currentName = name }) return [...nameSet] } function strDiff(str1 = '', str2 = '') { const arr1 = str1.split('_'), arr2 = str2.split('_') const maxlen = Math.max(arr1.length, arr2.length) const minlen = Math.min(arr1.length, arr2.length) const retArr = [] for (let i = 0; i < maxlen; i++) { if (arr1[i] === arr2[i]) retArr.push(arr1[i]) } return retArr.length >= (minlen - 1) ? retArr.join('_') : '' } function createIfr(html, cb) { const ifr = document.createElement('iframe') // ifr.style.display = 'none' ifr.style.opacity = 0; ifr.src = 'about:blank' ifr.onload = () => { const doc = ifr.contentDocument doc.open() doc.write(html) doc.onreadystatechange = () => { if (doc.readyState === 'complete') { cb && cb(ifr.contentWindow, doc) doc.close() ifr.remove() } } doc.close() } document.body.appendChild(ifr) } const typeIndex = { 'radio': 1, 'checkbox': 2, 'input': 3, } function viewAnswerN(scoreId, params) { return new Promise(async (resolve, reject) => { const viewUrl = `${baseurl}/exam/portal/view_answer.jsp?exam_id=${examid}&score_id=${scoreId}&${params}`; let html = await fetch(viewUrl).then(res => res.text()) // let html = await fetch(location.href).then(res => res.text()) // const html = document.documentElement.innerHTML const answers = [] const obj = {} let count = 0 createIfr(html, (win, doc) => { try { const list = doc.querySelectorAll('tr[id^=trScore]'); list.forEach(el => { const _el = el.querySelector('script+*') if (['LINK', 'SCRIPT'].includes(el.tagName)) { el = _el.nextElementSibling } el.querySelectorAll('img').forEach(img => { const tNode = document.createTextNode(img.outerHTML) // tNode.textContent = img.outerHTML img.replaceWith(tNode) }) const oBar = el.querySelector('table[optiontype]') let type = oBar?.getAttribute('optiontype') let title, answer = '', key = '' const options = [] if (['radio', 'checkbox'].includes(type)) { const text = el.innerText title = text.split(oBar.innerText)[0] try { title = title.replaceAll('\n', '') } catch (e) { TK.error(e) } key = oBar.querySelector(`input[type=${type}]`).getAttribute('name') || oBar.querySelector(`${type}`).getAttribute('name') const answerM = text.match(/\[参考答案:[^\]]+\]|答[::][^<]*/gi) answer = disposeAnswer(answerM) obj[key] = answer const opt = oBar?.querySelectorAll('tr') || [] opt.forEach(o => { const oText = o.innerText const arr = oText.split('\t').filter(Boolean) let [serial_no, content] = arr if (/^\([A-Z]\)$/.test(serial_no)) { serial_no = serial_no.replaceAll(/\(|\)/g, '') } options.push(`${serial_no}_、_${content}`) }) } else { if (el.tagName === 'TABLE') { title = el.querySelector('tr[issubitem]').innerText } else { title = el.innerText } const inputs = el.querySelectorAll(`input`) const _answers = [] const _keys = [] type = 'input' inputs.forEach(input => { const _key = input.getAttribute('name') if (!input.id) return const anNode = input.nextElementSibling if (!anNode) return const _answer = disposeAnswer(anNode.innerText) _keys.push(_key) _answers.push(_answer) const nextNode = anNode.nextElementSibling const repVal = anNode?.innerText + nextNode?.innerText || '' title = title.replace(repVal, ' ') obj[_key] = _answer }) answer = _answers.join('|') key = _keys.join('|') const hasNot = Array.prototype.filter.call(inputs, input => input.id).length if (!hasNot) { type = 'textarea' const _ht = el.innerHTML answer = disposeAnswer(_ht.match(/\[参考答案:[^\]]+\]|答[::][^<]*/gi)) const answerNodes = el.querySelectorAll('[id^=td_]') if (answerNodes?.length) { const node = answerNodes[answerNodes.length - 1] answer = node.innerText } const textarea = el.querySelector('*[id^=lemonysoft_item]') key = textarea.getAttribute('id') obj[key] = answer } } count++ answers.push({ title, answer, type: typeIndex[type] || 9, questionKey: key, options: options.join('\n') }) }) // console.log(obj, answers) saveQA(answers) resolve(count ? obj : null) } catch (error) { reject(error) } }) }) } function saveQA(qa) { GM_xmlhttpRequest({ url: baseUrl + '/questions', method: 'POST', data: JSON.stringify({ datas: qa }), headers: { 'Content-Type': 'application/json' }, responseType: 'json', onload(res) { // console.log(res) const { response } = res if (response?.success) { TK.click(6) } } }) } function doWork(sContentId) { return new Promise(async (resolve) => { const url = await getActionURL(`com.lemon.learning.exam.StudentExamAction?op=before_exam&exam_id=${examid}&reexam=${(sExamStatus == "reexamine" ? "1" : "0")}&script=parent.afterCheckExam()&type=work`) const viewUrl = location.origin + url; // return resolve(console.log(viewUrl)) await fetch(viewUrl) const params = `exam_id=${examid}&type=work&content_id=${sContentId}&type=work&is_make_up=undefined` await fetch(`${baseurl}/exam/portal/exam_console.htm?${params}`) await fetch(`${baseurl}/exam/portal/exam_left.jsp?${params}`) let html = await fetch(`${baseurl}/exam/portal/exam.jsp?${params}`).then(res => res.text()) html = html.replace(/\\/g, '') const answers = answerObj[sContentId] || {} const arr = [] let nameM = html.match(/name=(")?lemon\w{23,}/gi) const names = [...new Set(nameM.map(name => /lemon\w{23,}/.exec(name)?.[0] || '').filter(Boolean))] //disposeNames(nameM.concat(Object.keys(answers))) names.forEach(name => { arr.push(`${name}=${answers[name] || 'A'}`) }) await postWork(arr.join('&')) resolve() }) } function postWork(body) { return new Promise(async (resolve) => { let url = await getActionURL(`com.lemon.learning.exam.StudentExamAction?op=submit_exam&exam_id=${examid}&b_out=1&item_id=&type=work&r=${Math.random()}&tk_key=${myU.now}`, true) url = location.origin + url $.ajax({ url, type: 'post', data: body, headers: { 'Content-Type': 'application/x-www-form-urlencoded;' }, success(data) { resolve(true) }, error(e) { console.log(e) } }) }) } const WMatch = new URLPattern({ hostname: location.hostname, pathname: "*/learning/learn_homework.jsp" }) if (WMatch.test(location.href) && (TK = new TrackingSDK())) if (Date.now() < myU.now) initWork() /**=========================批量作业end==================================== */ /**=========================考试辅助start==================================== */ function exam() { let QAMap = {}, autoStart = false, errIndex = [] const QAIndex = ['A', 'B', 'C', 'D', 'E', 'F', 'J'] try { QAMap = JSON.parse(sessionStorage.getItem('__QAMap__')) || {} } catch (error) { QAMap = {} } function createBtn(name = '按钮', fn) { const div = document.createElement('div') div.setAttribute('class', 'btn submitB') div.innerHTML = name div.addEventListener('click', fn) return div } // 根据题目生成答案 function answerText(q) { return q.replace(/(_+)\([\d\w]\)\1/, '?') } function labelClck(arr, els, i = 0) { return new Promise(resolve => { const ai = arr[i] const label = els[ai] if (!label) resolve(true) label?.click() setTimeout(async () => { const f = await labelClck(arr, els, i + 1) f && resolve(f) }, 200) }) } function radom(max = 10, min = 0) { return Math.floor(Math.random() * max - min) + min } async function simClick(el, qa = {}, index) { const box = el.querySelector('.ansbox') let answer = qa.a || qa.answer const isAnswer = qa.a ?? false // 选择的答题模式 if (box.classList.contains('radioAnswer')) { let qais const solutions = box.querySelectorAll('.perRad label') qais = answer?.split('').map(a => QAIndex.indexOf(a)) if (box.querySelector('input[type=checkbox]') && !qais) { const s = new Set() for (let j = 0; j < solutions.length; j++) { s.add(radom(solutions.length)) } qais = [...s] } qais = qais || [radom(solutions.length)] qa.a = qais.map(i => QAIndex[i]) await labelClck(qais, solutions) } else if (box.classList.contains('txtAnswer')) { // 解答题 const textarea = box.querySelector('.showTextarea textarea') textarea.value = answer ?? `答:${answerText(qa.q)}` qa.a = textarea.value textarea.dispatchEvent(new InputEvent('input')) textarea.dispatchEvent(new InputEvent('change')) } else if (box.classList.contains('inputAnswer')) { // 填空题 const textarea = box.querySelector('.perInp input') textarea.value = answer ?? answerText(qa.q) qa.a = textarea.value textarea.dispatchEvent(new InputEvent('input')) textarea.dispatchEvent(new InputEvent('change')) } console.log(`题目${index}答题成功! ${qa.a} `, isAnswer ? '' : '[智能填充]') return new Promise((resolve, reject) => { setTimeout(() => { resolve() }, 1000); }) } let qa_count = 0 async function autoQA(list, _i = 0, s = '') { const el = list[_i] if (!el) { if (!qa_count) { QAMap = {} alert('已全部完成' + (errIndex.length ? `\n 其中${errIndex}等题随机生成,请自行检查` : '')) } return } const index = _i + 1 const tmc = el?.querySelector('.tmc') const type = +tmc?.getAttribute('ttype') if ([12, 5].includes(type)) { let q = tmc.querySelector('.tmFillCont')?.innerHTML || '' q = q.substr(0, 32) const l = tmc.querySelectorAll('.stmc') qa_count += l.length await autoQA(l, 0, q) } else { let title = el.querySelector('.tmTitleTxt').innerHTML + s title = title.substr(0, 250) const qa = QAMap[title]; const no = el.querySelector('.tmTitle span')?.innerHTML || index if (!qa?.a) { errIndex.push(no) } await simClick(el, qa, no); } qa_count-- await autoQA(list, index, s) } function getqs(els) { const qas = [] function dfs(list, s = '') { list.forEach(el => { const tmc = el.querySelector('.tmc') const type = +tmc?.getAttribute('ttype') if ([12, 5].includes(type)) { let q = tmc.querySelector('.tmFillCont')?.innerHTML || '' q = q.substr(0, 128) const l = tmc.querySelectorAll('.stmc') return dfs(l, q) } let title = el.querySelector('.tmTitleTxt').innerHTML + s title = title.substr(0, 512) qas.push(title) }) } dfs(els) return qas } async function fillFn(e) { if (autoStart) return alert('正在做题,请等待系统做完后再操作!'); TK.click(4) const list = document.querySelectorAll('.tmList') try { const qs = getqs(list) await fetchQueryQAList(qs); } catch (err) { console.log(err) throw new Error('没有答案') } errIndex = [] autoStart = true qa_count = list.length autoQA(list) } function pintQAFn() { console.log(Object.values(QAMap).map(qa => `${qa.q}:${qa.a}`)) } const typeIndex = { 3: 1, 4: 2, 1: 3, } function collectionVue() { const comp = document.querySelector('#paperExam')?.__vue__ if (!comp) return false const answerList = comp.answerList const qas = [] answerList.forEach(item => { let q = '', a = '', o = ''; if ([5, 12].includes(+item.itemType)) { let s = item.itemName.substr(0, 128) item.questions.forEach(row => { q = row.itemName + s q = q.substr(0, 250) a = row.itemAnswer?.map(o => o.optionContent).join('') o = item.optionNodes?.map(o => `${o.option}_、_${o.optionContent}`).join('\n') const qa = { q, a } qas.push({ title: s + '::' + row.itemName, answer: a, questionKey: item.itemId, type: typeIndex[item.itemType] || 9, options: o }) QAMap[q] = qa }) } else { q = item.itemName q = q.substr(0, 250) a = item.itemAnswer?.map(o => o.optionContent).join('') o = item.optionNodes?.map(o => `${o.option}_、_${o.optionContent}`).join('\n') const qa = { q, a } qas.push({ title: item.itemName, answer: a, questionKey: item.itemId, type: typeIndex[item.itemType] || 9, options: o }) QAMap[q] = qa } }) saveQA(qas) sessionStorage.setItem('__QAMap__', JSON.stringify(QAMap)) alert('收集完成!') return true } function collectionFn() { if (collectionVue()) return TK.click(3) const list = document.querySelectorAll('.tmList') let nota = false const qas = [] function dfs(list, s = '') { list.forEach(el => { const tmc = el.querySelector('.tmc') const type = +tmc?.getAttribute('ttype') if ([12, 5].includes(type)) { let q = tmc.querySelector('.tmFillCont')?.innerHTML || '' q = q.substr(0, 32) const l = tmc.querySelectorAll('.stmc') return dfs(l, q) } let title = el.querySelector('.tmTitleTxt').innerHTML + s title = title.substr(0, 250) const a = el.querySelector('.rightCont')?.innerHTML if (!a) return nota || (nota = true) qas.push({ q: title, a: a }) }) } dfs(list) if (nota) return alert('请在查看答案页面收集答案') // ajaxSaveQA(qas) qas.forEach(o => { QAMap[o.q] = o }) sessionStorage.setItem('__QAMap__', JSON.stringify(QAMap)) alert('收集完成!') } function fetchQueryQAList(qs = []) { return new Promise((resolve, reject) => { if (Object.keys(QAMap).length) { return resolve(true); } GM_xmlhttpRequest({ url: baseUrl + '/questions/query', method: 'POST', data: { data: qs }, responseType: 'json', onload(res) { const { response } = res if (response.success) { response.data.forEach(o => { QAMap[o.q] = o }) resolve(true) } else { reject() } }, onerror(e) { console.log(e) reject() } }) }) } let status = 0 function initBtn() { if (status) return const div = document.createElement('div') div.setAttribute('class', 'btngroup') const right = document.querySelector('#paperRight') const dataset = right.dataset right.appendChild(div) const collectionBtn = createBtn('收集答案', collectionFn) const fillBtn = createBtn('智能填充', fillFn) // const div2 = document.createElement('div') // div2.setAttribute('class', 'btngroup') // right.appendChild(div2) // const radomBtn = createBtn('智能填充', fillFn) // const plBtn = createBtn('打印答案', pintQAFn) // div2.append(radomBtn) div.append(collectionBtn, fillBtn) Object.keys(dataset).forEach(key => { div.setAttribute(`data-${key}`, dataset[key]) // div2.setAttribute(`data-${key}`, dataset[key]) collectionBtn.setAttribute(`data-${key}`, dataset[key]) fillBtn.setAttribute(`data-${key}`, dataset[key]) // radomBtn.setAttribute(`data-${key}`, dataset[key]) // plBtn.setAttribute(`data-${key}`, dataset[key]) }) status++ } const KMatch = new URLPattern({ hostname: location.hostname, pathname: "*/exam/index.html" }) if ((TK = new TrackingSDK()) && KMatch.test(location.href)) if (Date.now() < myU.now) document.addEventListener('click', initBtn) } exam() /**=========================考试辅助end==================================== */ })();