// ==UserScript==
// @name 绵阳人社继续教育 - 自动学习
// @namespace https://rsjapp.mianyang.cn/
// @version 1.1
// @description 登录后自动完成视频学习
// @description 脚本定制qq1915455753
// @author Marvis
// @match https://rsjapp.mianyang.cn/jxjy/pc/zxxx_*
// @grant GM_addStyle
// @require https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js
// ==/UserScript==
(function () {
'use strict';
const AES_KEY = CryptoJS.enc.Utf8.parse('123456ABCDEABCDE');
const ENCODE_KEY = '6e3f7bb708ff1ba0564e8264d8c7bedb4031af2580b8b8eac94088c055b51d8db74a6dcef6df7740cb6aaa9dd442d5a77111b21173b377b0467982ad30a180e631670b4aa8f56d69df9c6b58be747e29e7f6ccbb843a27be9833505105e218741792a1eed34cefec23539825a7f3a0b3a4aaa8a98cd44a6ca5a92474d631fb14';
const API_BASE = '/jxjy/pc/lcService/getData';
// ==================== AES ====================
function aesEncrypt(v) {
if (v == null) v = 'undefined';
const e = CryptoJS.AES.encrypt(
CryptoJS.enc.Utf8.parse(encodeURIComponent(String(v))),
AES_KEY, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }
);
return e.ciphertext.toString(CryptoJS.enc.Base64);
}
function aesDecrypt(b64) {
if (!b64) return null;
try {
while (b64.length % 4) b64 += '=';
const d = CryptoJS.AES.decrypt(b64, AES_KEY, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 });
const t = d.toString(CryptoJS.enc.Utf8);
const s = t.indexOf('{'), e = t.lastIndexOf('}');
return s >= 0 && e > s ? JSON.parse(t.slice(s, e + 1)) : t;
} catch (e) { return null; }
}
function buildData(p) {
return Object.entries(p).map(([k, v]) => k + '=' + encodeURIComponent(aesEncrypt(v)))
.concat('encodeKey=' + encodeURIComponent(ENCODE_KEY)).join('&');
}
async function apiPost(url, params) {
const r = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'X-Requested-With': 'XMLHttpRequest' },
body: buildData(params),
credentials: 'include',
});
const body = (await r.text()).trim().replace(/^"|"$/g, '');
return aesDecrypt(body);
}
// ==================== UI ====================
GM_addStyle(`
#mys-panel{position:fixed;top:10px;right:10px;z-index:99999;width:300px;
background:#fff;border-radius:8px;box-shadow:0 4px 20px rgba(0,0,0,.25);
font:13px "Microsoft YaHei",sans-serif;}
#mys-panel .hd{background:#2c3e50;color:#fff;padding:10px 14px;border-radius:8px 8px 0 0;
font-size:14px;font-weight:bold;cursor:move;}
#mys-panel .bd{padding:10px 14px 14px;}
#mys-panel .btn{display:block;width:100%;padding:8px;border:none;border-radius:4px;
font-size:14px;font-weight:bold;color:#fff;cursor:pointer;background:#27ae60;}
#mys-panel .btn:disabled{background:#95a5a6;cursor:not-allowed;}
#mys-panel .bar-wrap{margin-top:8px;height:8px;background:#ecf0f1;border-radius:4px;overflow:hidden;}
#mys-panel .bar-inner{height:100%;background:#3498db;width:0;transition:width .3s;}
#mys-panel .info{margin-top:6px;font-size:12px;color:#888;}
#mys-panel .log{margin-top:8px;max-height:160px;overflow-y:auto;background:#1e1e1e;
color:#d4d4d4;padding:8px;border-radius:4px;font:11px Consolas,monospace;white-space:pre-wrap;}
#mys-toggle{position:fixed;top:10px;right:10px;z-index:99998;background:#2c3e50;
color:#fff;border:none;padding:6px 12px;border-radius:4px;cursor:pointer;display:none;}
`);
const panel = document.createElement('div');
panel.id = 'mys-panel';
panel.innerHTML = `
绵阳人社 · 自动学习 _
`;
document.body.appendChild(panel);
const toggle = document.createElement('button');
toggle.id = 'mys-toggle';
toggle.textContent = '显示面板';
toggle.onclick = () => { panel.querySelector('.bd').style.display = ''; toggle.style.display = 'none'; };
document.body.appendChild(toggle);
document.getElementById('mys-min').onclick = () => {
panel.querySelector('.bd').style.display = 'none';
toggle.style.display = '';
};
// ==================== 日志 & 进度 ====================
const $ = id => document.getElementById(id);
function log(msg) { const el = $('mys-log'); el.textContent += msg + '\n'; el.scrollTop = el.scrollHeight; }
function setProgress(cur, total) {
$('mys-bar').style.width = total ? Math.round(cur / total * 100) + '%' : '0%';
$('mys-info').textContent = `进度: ${cur}/${total}`;
}
const sleep = ms => new Promise(r => setTimeout(r, ms));
// ==================== 主流程 ====================
async function run() {
const btn = $('mys-start');
btn.disabled = true;
btn.textContent = '运行中...';
$('mys-log').textContent = '';
// 从页面获取课程ID和用户ID
const adz280 = location.href.match(/adz280=([a-f0-9]+)/)?.[1];
// 自动获取 aac001:先无参请求 myd003,从响应中提取
let aac001 = null;
try {
const r0 = await apiPost(API_BASE + '/myd003.do', { adz280 });
// 尝试从响应中提取用户ID(可能在 resultData 或 data 字段中)
const data = r0?.resultData?.data?.data;
if (data) {
aac001 = data.aac001 || data.userId || data.user_id || data.uid;
}
// 如果目录中有 aac001 字段也可以提取
if (!aac001 && data?.directory?.[0]) {
aac001 = data.directory[0].aac001;
}
} catch (e) {}
// 如果自动获取不到,弹窗让用户输入
if (!aac001) {
aac001 = prompt('请输入用户ID (aac001),可从页面源码中查找:');
if (!aac001) { log('未输入用户ID'); btn.disabled = false; btn.textContent = '开始学习'; return; }
}
log(`用户ID: ${aac001}`);
log('='.repeat(50));
log('获取视频列表...');
const r = await apiPost(API_BASE + '/myd003.do', { adz280, aac001 });
if (!r?.resultData) {
log('获取失败,请确认已登录');
btn.disabled = false;
btn.textContent = '开始学习';
return;
}
const dir = r.resultData.data.data.directory;
const total = dir.length;
let done = dir.filter(v => v.videoOver === '1').length;
log(`共 ${total} 个视频,已完成 ${done},待完成 ${total - done}`);
let processed = 0;
const remaining = total - done;
while (processed < remaining) {
const r2 = await apiPost(API_BASE + '/myd003.do', { adz280, aac001 });
const dir2 = r2.resultData.data.data.directory;
let v = null, idx = 0;
for (let j = 0; j < dir2.length; j++) {
if (dir2[j].videoOver !== '1') { v = dir2[j]; idx = j; break; }
}
if (!v) { log('所有视频已完成!'); break; }
log(`[${idx + 1}/${total}] ${v.adz125}`);
setProgress(processed, remaining);
// 获取时长
const r4 = await apiPost(API_BASE + '/myd004.do', { adz290: v.adz290, aac001, adz127: 'undefined' });
let dur = 3600, fresh = true;
if (r4?.resultData) {
const d = r4.resultData.data.data?.old_time;
if (d && parseFloat(d) > 0) { dur = parseFloat(d); fresh = false; }
}
log(` 时长: ${Math.round(dur)}s (${fresh ? '新视频' : '有进度'})`);
// 上报进度(新视频分6次递增)
if (fresh) {
for (let s = 1; s <= 6; s++) {
await apiPost(API_BASE + '/myd007.do', { adz290: v.adz290, aac001, adz341: String(Math.round(dur * s / 6)) });
await sleep(1000);
}
} else {
await apiPost(API_BASE + '/myd007.do', { adz290: v.adz290, aac001, adz341: String(Math.round(dur)) });
}
await sleep(2000);
// 标记完成
const r5 = await apiPost(API_BASE + '/myd005.do', { adz290: v.adz290, aac001 });
const ok = r5?.resultData?.data?.code === '1';
log(ok ? ' 标记完成: OK' : ` 标记完成: 失败 (${r5?.resultData?.data?.msg || ''})`);
processed++;
await sleep(5000);
}
setProgress(processed, remaining);
log('\n全部完成!');
btn.disabled = false;
btn.textContent = '开始学习';
}
$('mys-start').addEventListener('click', run);
})();