返回首頁 

Greasy Fork is available in English.

云邮教学空间助手

作业显示相关课程,office文档预览切换到 view.officeapps.live.com,重命名下载文件名

ของเมื่อวันที่ 30-11-2023 ดู เวอร์ชันล่าสุด

// ==UserScript==// @name         云邮教学空间助手// @namespace    http://tampermonkey.net/// @version      0.12// @description  作业显示相关课程,office文档预览切换到 view.officeapps.live.com,重命名下载文件名// @author       YouXam// @match        https://ucloud.bupt.edu.cn/uclass/course.html*// @match        https://ucloud.bupt.edu.cn/uclass/*// @match        https://ucloud.bupt.edu.cn/office/*// @icon         https://www.google.com/s2/favicons?sz=64&domain=ucloud.bupt.edu.cn// @grant        none// @license      MIT// ==/UserScript==window.loadJs = function loadJs(src) {return new Promise((ret, rej) => {fetch(src).then(res => res.text()).then(res => {let s = document.createElement("script")s.language = "javascript"s.type = "text/javascript"s.text = resdocument.getElementsByTagName('HEAD')[0].appendChild(s)ret(0)}).catch(e => rej(e))})}function loadCss() {function addGlobalStyle(css) {var head, style;head = document.getElementsByTagName('head')[0];if (!head) { return; }style = document.createElement('style');style.type = 'text/css';style.innerHTML = css;head.appendChild(style);}addGlobalStyle(`#nprogress{pointer-events:none}#nprogress .bar{background:#ffbe00;position:fixed;z-index:1031;top:0;left:0;width:100%;height:5px}#nprogress .peg,.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}#nprogress .peg{display:block;right:0;width:100px;height:100%;box-shadow:0 0 10px #ffbe00,0 0 5px #ffbe00;opacity:1;-webkit-transform:rotate(3deg) translate(0,-4px);-ms-transform:rotate(3deg) translate(0,-4px);transform:rotate(3deg) translate(0,-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#ffbe00;border-left-color:#ffbe00;border-radius:50%;-webkit-animation:.4s linear infinite nprogress-spinner;animation:.4s linear infinite nprogress-spinner}.nprogress-custom-parent{overflow:hidden;position:relative}@-webkit-keyframes nprogress-spinner{0%{-webkit-transform:rotate(0)}100%{-webkit-transform:rotate(360deg)}}@keyframes nprogress-spinner{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}`)}function getToken() {const cookieMap = new Map();document.cookie.split("; ").forEach((cookie) => {const [key, value] = cookie.split("=");cookieMap.set(key, value);})const token = cookieMap.get("iClass-token");const userid = cookieMap.get('iClass-uuid');return [userid, token]}async function downloadFile(url, filename) {await jsp;NProgress.configure({ trickle: false, speed: 0 });try {const response = await fetch(url);if (!response.ok) {throw new Error(`HTTP error! status: ${response.status}`);}const contentLength = response.headers.get('content-length');if (!contentLength) {throw new Error('Content-Length response header unavailable');}const total = parseInt(contentLength, 10);let loaded = 0;const reader = response.body.getReader();const chunks = [];while (true) {const { done, value } = await reader.read();if (done) break;chunks.push(value);loaded += value.length;NProgress.set(loaded / total)}NProgress.done();const blob = new Blob(chunks);const downloadUrl = window.URL.createObjectURL(blob);const a = document.createElement('a');a.href = downloadUrl;a.download = filename;document.body.appendChild(a);a.click();window.URL.revokeObjectURL(downloadUrl);} catch (error) {console.error('Download failed:', error);}}async function searchTask(siteId, keyword, token) {const res = await fetch("https://apiucloud.bupt.edu.cn/ykt-site/work/student/list", {"headers": {"authorization": "Basic cG9ydGFsOnBvcnRhbF9zZWNyZXQ=","blade-auth": token,'content-type': 'application/json;charset=UTF-8'},"body": JSON.stringify({siteId,keyword,"current": 1,"size": 5,}),"method": "POST"});const json = await res.json();return json}async function searchCourse(userId, id, keyword, token) {const res = await fetch("https://apiucloud.bupt.edu.cn/ykt-site/site/list/student/current?size=999999&current=1&userId=" + userId + "&siteRoleCode=2", {"headers": {"authorization": "Basic cG9ydGFsOnBvcnRhbF9zZWNyZXQ=","blade-auth": token,},"body": null,"method": "GET"});const json = await res.json();const list = json.data.records.map(x => ({id: x.id,name: x.siteName,teachers: x.teachers.map(y => y.name).join(', '),}))async function searchWithLimit(list, id, keyword, token, limit = 5) {for (let i = 0; i < list.length; i += limit) {const batch = list.slice(i, i + limit);const jobs = batch.map(x => searchTask(x.id, keyword, token));const ress = await Promise.all(jobs);for (let j = 0; j < ress.length; j++) {const res = ress[j];if (res.data.records.length > 0) {for (const item of res.data.records) {if (item.id == id) {return batch[j];}}}}}return null;}return await searchWithLimit(list, id, keyword, token);}async function getTasks(siteId, token) {const res = await fetch("https://apiucloud.bupt.edu.cn/ykt-site/work/student/list", {"headers": {"authorization": "Basic cG9ydGFsOnBvcnRhbF9zZWNyZXQ=","blade-auth": token,'content-type': 'application/json;charset=UTF-8'},"body": JSON.stringify({siteId,current: 1,size: 9999,}),"method": "POST"});const json = await res.json();return json}async function searchCourses(nids) {const r###lt = {}let ids = []for (let id of nids) {const r = get(id)if (r) r###lt[id] = r;else ids.push(id)}if (ids.length == 0) return r###ltconst [userid, token] = getToken()const res = await fetch("https://apiucloud.bupt.edu.cn/ykt-site/site/list/student/current?size=999999&current=1&userId=" + userid + "&siteRoleCode=2", {"headers": {"authorization": "Basic cG9ydGFsOnBvcnRhbF9zZWNyZXQ=","blade-auth": token,},"body": null,"method": "GET"});const json = await res.json();const list = json.data.records.map((x) => ({id: x.id,name: x.siteName,teachers: x.teachers.map((y) => y.name).join(', '),}))const hashMap = new Map();let count = ids.lengthfor (let i = 0; i < ids.length; i++) {hashMap.set(ids[i], i);}async function searchWithLimit(list, limit = 5) {for (let i = 0; i < list.length; i += limit) {const batch = list.slice(i, i + limit);const jobs = batch.map((x) => getTasks(x.id, token));const ress = await Promise.all(jobs);for (let j = 0; j < ress.length; j++) {const res = ress[j];if (res.data.records.length > 0) {for (const item of res.data.records) {if (hashMap.has(item.id)) {r###lt[item.id] = batch[j];set(item.id, batch[j])if (--count == 0) {return r###lt;}}}}}}return r###lt;}return await searchWithLimit(list);}async function getUndoneList() {const [userid, token] = getToken()const res = await fetch("https://apiucloud.bupt.edu.cn/ykt-site/site/student/undone?userId=" + userid, {"headers": {"authorization": "Basic cG9ydGFsOnBvcnRhbF9zZWNyZXQ=","blade-auth": token,},"method": "GET"});const json = await res.json();return json;}async function getDetail(id) {const [userid, token] = getToken()const res = await fetch("https://apiucloud.bupt.edu.cn/ykt-site/work/detail?assignmentId=" + id, {"headers": {"authorization": "Basic cG9ydGFsOnBvcnRhbF9zZWNyZXQ=","blade-auth": token,},"body": null,"method": "GET"})const json = await res.json();return json;}async function getSiteResource(id) {const [userid, token] = getToken()const res = await fetch("https://apiucloud.bupt.edu.cn/ykt-site/site-resource/tree/student?siteId=" + id + "&userId=" + userid, {"headers": {"authorization": "Basic cG9ydGFsOnBvcnRhbF9zZWNyZXQ=","blade-auth": token,},"body": null,"method": "POST"})const json = await res.json();const r###lt = []json.data.forEach(x => {x.attachmentVOs.forEach(y => r###lt.push(y.resource))})return r###lt}function $x(xpath, context = document) {const iterator = document.evaluate(xpath, context, null, XPathR###lt.ANY_TYPE, null);const r###lts = [];let item;while (item = iterator.iterateNext()) {r###lts.push(item);}return r###lts;}function set(k, v) {const h = JSON.parse(localStorage.getItem('zzxw') || '{}')h[k] = vlocalStorage.setItem('zzxw', JSON.stringify(h))}function get(k) {const h = JSON.parse(localStorage.getItem('zzxw') || '{}')return h[k];}function insert(x) {if ($x('/html/body/div[1]/div/div[2]/div[2]/div/div/div[2]/div/div[2]/div[1]/div/div/div[1]/div/p').length > 2) returnconst d = $x('/html/body/div[1]/div/div[2]/div[2]/div/div/div[2]/div/div[2]/div[1]/div/div/div[1]/div/p[1]');if (!d.length) {setTimeout(() => insert(x), 50);return}const p = document.createElement('p')const t = document.createTextNode(x.name + "(" + x.teachers + ")")p.appendChild(t)d[0].after(p)}function sleep(n) {return new Promise(res => setTimeout(res, n))}async function wait(func) {let r = func()if (r instanceof Promise) r = await rif (r) return r;await sleep(50)return await wait(func)}async function waitChange(func, value) {const r = valuewhile (1) {let t = func()if (t instanceof Promise) t = await tif (t != r) return t;await sleep(50);}}let onlinePreview = nullasync function getPreviewURL(storageId) {const res = await fetch("https://apiucloud.bupt.edu.cn/blade-source/resource/preview-url?resourceId=" + storageId)const json = await res.json();onlinePreview = json.data.onlinePreviewreturn json.data.previewUrl;}let setClicked = false;let gpage = -1;let glist = null;let jspasync function main() {'use strict';if (location.href.startsWith("https://ucloud.bupt.edu.cn/uclass/course.html#/student/assignmentDetails_fullpage")) {const q = new URLSearchParams(location.href)const id = q.get('assignmentId')const r = get(id)const [userid, token] = getToken()if (r) {insert(r)} else {const title = q.get('assignmentTitle')if (!id || !title) return;searchCourse(userid, id, title, token).then(x => {insert(x)set(id, x)})}const detail = (await getDetail(id)).dataconst filenames = detail.assignmentResource.map(x => x.resourceName)const urls = await Promise.all(detail.assignmentResource.map(x => {return getPreviewURL(x.resourceId)}))await wait(() => $x('//*[@id="assignment-info"]/div[2]/div[2]/div[2]/div').length > 0)$x('//*[@id="assignment-info"]/div[2]/div[2]/div[2]/div').forEach((x, index) => {const i = document.createElement('i')i.title="预览"i.classList.add("by-icon-eye-grey")i.addEventListener("click", () => {const url = urls[index]if (url.endsWith(".doc") || url.endsWith(".docx") || url.endsWith(".ppt") || url.endsWith(".pptx"))window.open("https://view.officeapps.live.com/op/view.aspx?src=" + encodeURIComponent(url))else if (url.endsWith(".pdf"))window.open(url)else if (onlinePreview !== null)window.open(onlinePreview + encodeURIComponent(url))})x.children[3].remove();x.children[2].insertAdjacentElement("afterend", i)const i2 = document.createElement('i')i2.title="下载"i2.classList.add("by-icon-yundown-grey")i2.addEventListener("click", () => {downloadFile(urls[index], filenames[index])})x.children[2].remove();x.children[1].insertAdjacentElement("afterend", i2)})} else if (location.href.startsWith("https://ucloud.bupt.edu.cn/uclass/#/student/homePage")) {async function getPage() {const pageText = await wait(() => document.querySelector("#layout-container > div.main-content > div.router-container > div > div.teacher-home-page > div.home-left-container.home-inline-block > div.in-progress-section.home-card > div.in-progress-header > div > div:nth-child(2) > div > div.banner-indicator.home-inline-block"))return parseInt(pageText.innerHTML.trim().split('/')[0])}const list = glist || (await getUndoneList()).data.undoneList;let page = 1glist = listif (list.length > 6) {page = await getPage();gpage = pageif (!setClicked) {setClicked = true;async function cmain() {await waitChange(getPage, gpage)main();}(await wait(() => document.querySelector("#layout-container > div.main-content > div.router-container > div > div.teacher-home-page > div.home-left-container.home-inline-block > div.in-progress-section.home-card > div.in-progress-header > div > div:nth-child(2) > div > div:nth-child(2) > span"))).addEventListener('click', cmain);(await wait(() => document.querySelector("#layout-container > div.main-content > div.router-container > div > div.teacher-home-page > div.home-left-container.home-inline-block > div.in-progress-section.home-card > div.in-progress-header > div > div:nth-child(2) > div > div:nth-child(3) > span"))).addEventListener('click', cmain);}}const tlist = list.slice((page - 1) * 6, page * 6)const ids = tlist.map(x => x.activityId)const infos = await searchCourses(ids)const texts = tlist.map(x => infos[x.activityId].name + "(" + infos[x.activityId].teachers + ")")const titles = tlist.map(x => x.activityName)await wait(() => $x('//*[@id="layout-container"]/div[2]/div[2]/div/div[2]/div[1]/div[3]/div[2]/div/div').map(x => x.children[0].innerText).every((e, i)  => e == titles[i]))const nodes = $x('//*[@id="layout-container"]/div[2]/div[2]/div/div[2]/div[1]/div[3]/div[2]/div/div')for (let i = 0; i < nodes.length; i++) {if (nodes[i].children[1].children.length == 0) {const p = document.createElement('div')const t = document.createTextNode(texts[i])p.appendChild(t)nodes[i].children[1].insertAdjacentElement('afterbegin',p)} else {nodes[i].children[1].children[0].innerHTML = texts[i]}}} else if (location.href.startsWith("https://ucloud.bupt.edu.cn/uclass/course.html#/student/courseHomePage")) {const site = JSON.parse(localStorage.getItem("site"))const id = site.idconst resources = await getSiteResource(id)$x('//div[@class="el-collapse-item__content"]/div[@class="chapter-item-resource"]/div/div[2]').forEach((x, index) => {const i = document.createElement('i')i.title="下载"i.classList.add("by-icon-download")i.classList.add("btn-icon")i.classList.add("visible")i.setAttribute(Array.from(x.attributes).filter(x => x.localName.startsWith('data-v'))[0].localName,'')i.addEventListener("click", async (e) => {e.stopPropagation();downloadFile(await getPreviewURL(resources[index].id), resources[index].name)}, false)if (x.children.length) x.children[0].remove();x.insertAdjacentElement('afterbegin',i)})}}(function () {loadCss()jsp = loadJs('https://unpkg.com/[email protected]/nprogress.js')if (location.href.startsWith("https://ucloud.bupt.edu.cn/office/")) {const url = new URLSearchParams(location.search).get("furl")if (url.endsWith(".doc") || url.endsWith(".docx") || url.endsWith(".ppt") || url.endsWith(".pptx"))location.href = "https://view.officeapps.live.com/op/view.aspx?src=" + encodeURIComponent(url)else if (url.endsWith(".pdf"))location.href = urlreturn}main()let hash = location.hash;setInterval(() => {if (location.hash != hash) {hash = location.hash;main();}}, 50)})();