Greasy Fork

Greasy Fork is available in English.

【U校园答案显示】 自用版(推荐使用ScriptCat脚本猫)

U校园题目答案显示;不支持单元测试

目前为 2024-11-16 提交的版本。查看 最新版本

// ==UserScript==
// @name         【U校园答案显示】 自用版(推荐使用ScriptCat脚本猫)
// @namespace    jayhyy
// @version      1.3
// @description  U校园题目答案显示;不支持单元测试
// @author       jayhyy
// @match        *://ucontent.unipus.cn/_pc_default/pc.html?*
// @connect      *://ucontent.unipus.cn/*
// @connect      unipus.cn
// @grant        GM_xmlhttpRequest
// @run-at       document-end
// @require      https://lib.baomitu.com/jquery/3.6.0/jquery.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js
// @license      MIT
// ==/UserScript==

$('head').append('<link href="https://lib.baomitu.com/layui/2.6.8/css/layui.css" rel="stylesheet" type="text/css" />');
$.getScript("https://lib.baomitu.com/layui/2.6.8/layui.js", function(data, status, jqxhr) {
    layui.use('element', function(){
        var element = layui.element;
    });
    layer.closeAll();
    show();
    showanswer();
});

// 感谢ssmjae提供的解密代码
function decryptContent(json) {
    if (json) {
        let r = json.content.slice(7)
        , o = CryptoJS.enc.Utf8.parse("1a2b3c4d" + json.k)
        , i = CryptoJS.enc.Hex.parse(r)
        , a = CryptoJS.enc.Base64.stringify(i)
        , contentJson = JSON.parse(CryptoJS.AES.decrypt(a, o, {
            mode: CryptoJS.mode.ECB,
            padding: CryptoJS.pad.ZeroPadding
        }).toString(CryptoJS.enc.Utf8));
        json = contentJson;
        console.log(json);
    }
    return json;
}

var show = ()=>{
    layer.open({
        type: 1,
        area: ['500px', '600px'],
        offset: ['bottom', 'left'] ,// 距离底部和左侧
        id: 'msgt',
        closeBtn: 1,
        title: "U校园网课助手(答案显示,支持单选、多选、填空、简答、问答)",
        shade: 0,
        maxmin: true,
        anim: 2,
        content:'<div class="layui-collapse"><div class="layui-colla-item"><h2 class="layui-colla-title">公告</h2><div class="layui-colla-content layui-show">脚本已修复</div>'+
        '</div></div>'+
        '<div id="content"><ul></ul><table class="layui-table"> <colgroup> <col width="100"> <col> <col> </colgroup> <thead> <tr>  </tr> </thead> <tbody>  </tbody> </table></div></div></div>'
    });
}

let isShow = true
var showanswer = ()=>{
    if (isShow){
        let url = location.href
        let arr = url.split("/")
        let book = arr[arr.length-7]
        let unit = arr[arr.length-2]
        let answer = []
        GM_xmlhttpRequest({
            method: 'GET',
            url: 'https://ucontentapi.unipus.cn/course/api/content/'+book+'/'+unit+'/default/',
            headers: {
                'X-ANNOTATOR-AUTH-TOKEN': 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJvcGVuX2lkIjoidHV4NkNCQVc4aGRrcnFZdzc5SEpEWDF2aTR5Z2ptcDUiLCJuYW1lIjoiIiwiZW1haWwiOiIiLCJhZG1pbmlzdHJhdG9yIjoiZmFsc2UiLCJleHAiOjE5MDI5NzAxNTcwMDAsImlzcyI6IlI0aG03RmxQOFdvS0xaMUNmTkllIiwiYXVkIjoiZWR4LnVuaXB1cy5jbiJ9.CwuQmnSmIuts3hHAMf9lT954rKHXUNkps-PfRJp0KnU'
            },
            timeout: 5000,
            onload: function(xhr) {
                if (xhr.status == 200) {
                    let el = '<tr class="layui-bg">' + '</td></tr>'
                    console.log('https://ucontentapi.unipus.cn/course/api/content/'+book+'/'+unit+'/default/')

                    console.log(xhr.responseText)
                    let obj = JSON.parse(xhr.responseText) || {};
                    let deObj = decryptContent(obj);
                    let keyList = Object.keys(deObj);
                    console.log(keyList)
                    Array.prototype.contains = function (obj) {
                        var index = this.length;
                        while (index--) {
                            if (this[index] === obj) {
                                return true;
                            }
                        }
                        return false;
                    }
                    // 选择题
                    if (keyList.contains('questions:questions')) {
                        let questionList = deObj['questions:questions'].questions;
                        let lineCount = 0; // 初始化行数计数器

                        for (let question of questionList) {
                            let result = '';
                            
                            if (question.answers) {
                                result += question.answers.join(' ');
                            }

                            // 在每一行前加上行号
                            lineCount++; // 增加行数
                            el = el + '<tr><td>第' + lineCount + '题: ' + result + question.analysis.html + '</td></tr>';
                        }

                        // 可选:在最后添加总行数统计
                        el = el + '<tr><td colspan="2">总题数: ' + lineCount + '</td></tr>';
                    }
                // 简答题
                if (keyList.contains('shortanswer:shortanswer')) {
                    let questionList = deObj['shortanswer:shortanswer'].questions;
                    let lineCount = 0; // 初始化行数计数器

                    for (let question of questionList) {
                        lineCount++; // 每处理一个题目,行数加 1
                        el = el + '<tr><td>第' + lineCount + '题: ' + deObj['shortanswer:shortanswer'].analysis.html + question.content.html + question.analysis.html + '</td></tr>';
                    }

                    // 可选:在最后添加总行数统计
                    el = el + '<tr><td colspan="2">总题数: ' + lineCount + '</td></tr>';
                }
                    // 填空题
                    if (keyList.contains('questions:scoopquestions')) {
                        let questionList = deObj['questions:scoopquestions'].questions;
                        let lineCount = 0; // 初始化行数计数器

                        for (let question of questionList) {
                            lineCount++; // 每处理一个题目,行数加 1

                            let result = '';
                            if (question.answers) {
                                result += question.answers.join(' ');
                            }

                            // 输出包含行号的 HTML 和复制按钮
                            el = el + '<tr><td>第' + lineCount + '题: ' + result + question.analysis.html + '</td>';
                            el += '<td><button class="layui-btn layui-btn-xs copy-btn" data-content="' + result + '">复制</button></td></tr>';
                        }

                        // 可选:在最后添加总行数统计
                        el = el + '<tr><td colspan="2">总题数: ' + lineCount + '</td></tr>';
                    }

                    // 为复制按钮添加事件监听
                    $(document).on('click', '.copy-btn', function() {
                        var content = $(this).data('content'); // 获取该按钮对应的内容
                        var textarea = document.createElement("textarea"); // 创建一个临时的textarea元素
                        textarea.value = content; // 将内容设置为textarea的值
                        document.body.appendChild(textarea); // 将textarea添加到DOM中
                        textarea.select(); // 选择textarea中的内容
                        document.execCommand("copy"); // 执行复制操作
                        document.body.removeChild(textarea); // 删除临时textarea

                        // 提示用户已复制
                        layer.msg('已复制: ' + content, {icon: 1});
                    });                // 短回答题
                if (keyList.contains('questions:shortanswer')) {
                    let questionList = deObj['questions:shortanswer'].questions;
                    let lineCount = 0; // 初始化行数计数器

                    for (let question of questionList) {
                        lineCount++; // 每处理一个题目,行数加 1

                        let result = '';
                        if (question.answers) {
                            result += question.answers.join(' ');
                        }

                        // 输出包含行号的 HTML
                        el = el + '<tr><td>第' + lineCount + '题: ' + result + question.analysis.html + '</td></tr>';
                    }

                    // 可选:在最后添加总行数统计
                    el = el + '<tr><td colspan="2">总题数: ' + lineCount + '</td></tr>';
                }
                    el = el + '<td>答案结束,如果没有答案,尝试刷新试试</td></td></tr>'
                    $("#content>table>tbody").append($(el));
                }
            }
        });
    }
    isShow = !isShow
}

var show = () => {
    layer.open({
        type: 1,
        area: ['500px', '600px'],
        offset: ['20%', '60%'], // 垂直居中,水平自动
        id: 'msgt',
        closeBtn: 1,
        title: "U校园网课助手(答案显示,支持单选、多选、填空、简答、问答)",
        shade: 0,
        maxmin: true,
        anim: 2,
        content: '<div class="layui-collapse"><div class="layui-colla-item"><h2 class="layui-colla-title">公告</h2><div class="layui-colla-content layui-show">脚本已修复</div>' +
            '</div></div>' +
            '<div id="content"><ul></ul><table class="layui-table"> <colgroup> <col width="100"> <col> <col> </colgroup> <thead> <tr>  </tr> </thead> <tbody>  </tbody> </table>' +
            // 添加刷新按钮
            '<button id="refresh-btn" class="layui-btn layui-btn-normal">一键刷新</button>' +
            '</div></div></div>',
        success: function(layero, index) {
            // 获取浏览器窗口的宽度和弹框的宽度
            var windowWidth = $(window).width();
            var layerWidth = $(layero).outerWidth();
            var layerHeight = $(layero).outerHeight();
            var topOffset = (window.innerHeight - layerHeight) / 2; // 垂直居中
            var rightOffset = windowWidth - layerWidth - 10; // 右侧10px边距

            // 设置弹框的位置,垂直居中,右对齐
            $(layero).css({
                'top': topOffset + 'px',
                'right': rightOffset + 'px'
            });
        }
    });

    // 为刷新按钮绑定点击事件
    $('#refresh-btn').click(function() {
        console.log('刷新页面');
        location.reload(); // 刷新页面
    });
}

$(document).ready(function() {
    // 页面加载完毕时初始化点击检查
    function autoClickConfirmButton() {
        var interval = setInterval(function() {
            var confirmButton = $("span[style*='font-size: 14px'][style*='text-transform: uppercase']");
            if (confirmButton.length > 0) {
                confirmButton.click(); // 自动点击
                console.log("已自动点击确定按钮");
                clearInterval(interval); // 点击后停止检查
            }
        }, 500); // 每500ms检查一次
    }

    // 初始时调用函数检查按钮
    autoClickConfirmButton();

    // 监听URL的变化,检测页面切换
    window.onhashchange = function() {
        console.log("页面切换 detected");
        $("#content>table>tbody").empty(); // 清空之前的内容
        showanswer(); // 重新加载答案
        autoClickConfirmButton(); // 每次页面切换后重新调用点击函数
    };
});


// 检查脚本是否有更新
function checkForUpdates() {
    GM_xmlhttpRequest({
        method: 'GET',
        url: 'https://update.greasyfork.icu/scripts/517254/%E3%80%90U%E6%A0%A1%E5%9B%AD%E7%AD%94%E6%A1%88%E6%98%BE%E7%A4%BA%E3%80%91%20%E8%87%AA%E7%94%A8%E7%89%88.meta.js', // 获取脚本元数据
        onload: function(response) {
            const meta = response.responseText;
            const remoteVersion = /@version\s+([\d.]+)/.exec(meta)[1]; // 提取远程版本号
            const currentVersion = GM_info.script.version; // 当前脚本版本

            if (remoteVersion > currentVersion) {
                alert(`检测到新版本:${remoteVersion},请更新脚本!`);
                // 提供更新提醒或自动下载更新
                if (confirm("发现新版本,是否立即更新脚本?")) {
                    window.location.href = "https://update.greasyfork.icu/scripts/517254/%E3%80%90U%E6%A0%A1%E5%9B%AD%E7%AD%94%E6%A1%88%E6%98%BE%E7%A4%BA%E3%80%91%20%E8%87%AA%E7%94%A8%E7%89%88.user.js"; // 自动跳转到更新链接
                }
            }
        },
        onerror: function() {
            console.log("无法检查更新,请稍后再试");
        }
    });
}

// 每隔一段时间检查一次更新(例如,每24小时检查一次)
setInterval(checkForUpdates, 86400000); // 24小时检查一次



window.onhashchange=()=>{
    $("#content>table>tbody").empty();
    showanswer();
}