不智慧教室

Bypass CORS in private

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         不智慧教室
// @version      1.1
// @description  Bypass CORS in private
// @author       singledog
// @match        https://duaa.singledog233.top/*
// @grant        GM_xmlhttpRequest
// @connect      iclass.buaa.edu.cn
// @run-at       document-start
// @license MIT
// @namespace https://greasyfork.org/users/1226768
// ==/UserScript==

(function () {
    'use strict';

    const DEBUG = false;
    const dlog = (...args) => { if (DEBUG) console.log('[iClass-Userscript]', ...args); };
    dlog('Userscript loaded at', location.href);

    function httpRequest(method, url, { headers = {}, data = null, timeout = 8000 } = {}) {
        dlog('httpRequest:start', { method, url, headers, dataPreview: (typeof data === 'string' ? data.slice(0, 128) : data), timeout });
        return new Promise((resolve, reject) => {
            const t0 = performance.now();
            GM_xmlhttpRequest({
                method,
                url,
                headers,
                data,
                timeout,
                onload: (res) => {
                    const dt = (performance.now() - t0).toFixed(1);
                    dlog('httpRequest:onload', { url, status: res.status, timeMs: dt, length: (res.responseText || '').length });
                    try {
                        const json = JSON.parse(res.responseText || '{}');
                        dlog('httpRequest:json', json);
                        resolve(json);
                    } catch (e) {
                        dlog('httpRequest:parse-error', e);
                        resolve({ STATUS: '1', message: '响应非JSON', raw: res.responseText });
                    }
                },
                onerror: (e) => {
                    dlog('httpRequest:onerror', e);
                    reject(new Error('网络错误'));
                },
                ontimeout: () => {
                    dlog('httpRequest:timeout', { url, timeout });
                    reject(new Error('请求超时'));
                },
            });
        });
    }

    function toQuery(params) {
        const usp = new URLSearchParams();
        Object.entries(params).forEach(([k, v]) => usp.append(k, v));
        return usp.toString();
    }

    // 登录
    async function login(studentId) {
        dlog('login:start', { studentId });
        const url = `https://iclass.buaa.edu.cn:8346/app/user/login.action?` + toQuery({
            password: '',
            phone: studentId,
            userLevel: '1',
            verificationType: '2',
            verificationUrl: '',
        });
        const res = await httpRequest('GET', url);
        dlog('login:done', res);
        return res;
    }

    // 课表查询
    async function getSchedule(userId, sessionId, dateStr) {
        dlog('getSchedule:start', { userId, sessionIdPreview: (sessionId || '').slice(0, 6) + '...', dateStr });
        const url = `https://iclass.buaa.edu.cn:8346/app/course/get_stu_course_sched.action?` + toQuery({
            dateStr,
            id: userId,
        });
        const res = await httpRequest('GET', url, { headers: { sessionId } });
        dlog('getSchedule:done', res);
        return res;
    }

    // 签到
    async function sign(userId, courseSchedId) {
        dlog('sign:start', { userId, courseSchedId });
        const tsMs = Date.now();
        const url = `http://iclass.buaa.edu.cn:8081/app/course/stu_scan_sign.action?courseSchedId=${encodeURIComponent(courseSchedId)}&timestamp=${tsMs}`;
        const body = toQuery({ id: userId }); // 表单方式
        const res = await httpRequest('POST', url, {
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            data: body,
        });
        dlog('sign:done', res);
        return res;
    }

    // 使用 postMessage 监听来自页面的请求,并回传处理结果
    window.addEventListener('message', async (ev) => {
        try {
            const data = ev.data || {};
            if (!data.__ICLASS_MSG__) return;
            const { type, id, payload } = data;
            dlog('message:received', { type, id, payload });

            if (type === 'iclass:intranet:query') {
                const { studentId, dateStr } = payload || {};
                try {
                    const loginRes = await login(studentId);
                    if (loginRes.STATUS !== '0') {
                        dlog('query:login-failed', loginRes);
                        window.postMessage({ __ICLASS_MSG__: true, type: 'iclass:intranet:query:result', id, payload: { login: loginRes, schedule: { STATUS: '1', message: '登录失败' } } }, '*');
                        return;
                    }
                    const userId = loginRes.result?.id;
                    const sessionId = loginRes.result?.sessionId;
                    const scheduleRes = await getSchedule(userId, sessionId, dateStr);
                    dlog('query:success');
                    window.postMessage({ __ICLASS_MSG__: true, type: 'iclass:intranet:query:result', id, payload: { login: loginRes, schedule: scheduleRes } }, '*');
                } catch (e) {
                    dlog('query:error', e);
                    window.postMessage({ __ICLASS_MSG__: true, type: 'iclass:intranet:query:result', id, payload: { login: { STATUS: '1', message: e.message }, schedule: { STATUS: '1', message: e.message } } }, '*');
                }
            }

            if (type === 'iclass:intranet:signin') {
                const { studentId, courseSchedId } = payload || {};
                try {
                    const loginRes = await login(studentId);
                    if (loginRes.STATUS !== '0') {
                        dlog('signin:login-failed', loginRes);
                        window.postMessage({ __ICLASS_MSG__: true, type: 'iclass:intranet:signin:result', id, payload: { STATUS: '1', message: '登录失败' } }, '*');
                        return;
                    }
                    const userId = loginRes.result?.id;
                    const signRes = await sign(userId, courseSchedId);
                    dlog('signin:success');
                    window.postMessage({ __ICLASS_MSG__: true, type: 'iclass:intranet:signin:result', id, payload: signRes }, '*');
                } catch (e) {
                    dlog('signin:error', e);
                    window.postMessage({ __ICLASS_MSG__: true, type: 'iclass:intranet:signin:result', id, payload: { STATUS: '1', message: e.message } }, '*');
                }
            }
        } catch (err) {
            dlog('message:handler-error', err);
        }
    });

})();