UMU 增强

增强 UMU 的体验,可根据参与人数进行搜索,可模糊搜索,支持在新窗口打开页面从而不丢失当前浏览状态。

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         UMU 增强
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  增强 UMU 的体验,可根据参与人数进行搜索,可模糊搜索,支持在新窗口打开页面从而不丢失当前浏览状态。
// @author       LinHQ
// @match        https://m.umu.cn/course/*
// @icon         https://m.umu.cn/favicon.ico
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @connect https://m.umu.cn/uapi/*
// @require https://greasyfork.org/scripts/429203-async-xmlhttprequest/code/Async%20xmlhttprequest.js?version=949226
// ==/UserScript==
'use strict'

// 搜索关键字 ask
let TYPES = new Map();
TYPES.set(1,"survey");
TYPES.set(2, "ask");
TYPES.set(3, "discussion");
TYPES.set(4, "photo");
TYPES.set(5, "GAME");
TYPES.set(6, "attendance");
TYPES.set(7, "mini_course");
TYPES.set(8, "raffle_drawing");
TYPES.set(9, "registration");
TYPES.set(10, "quiz");
TYPES.set(11, "video");
TYPES.set(12, "live");
TYPES.set(13, "article");
TYPES.set(14, "document");
TYPES.set(16, "exercise/?surl=");

let GROUPID = new URL(window.location.href).searchParams.get('groupId');

// 获取第{cid}目录下的内容
async function getChapters(cid, sessionCount) {
  let resp = await req({
    method: "POST",
    headers: {"Content-type": "application/x-www-form-urlencoded"},
    url: "https://m.umu.cn/uapi/v2/element/chapter-session",
    data: `t=${new Date().getTime()}&parent_id=${GROUPID}&chapter_id=${cid}&page=1&size=${sessionCount}&get_draft=0`
  });
  // 解析返回数据
  let li = JSON.parse(resp.responseText).data.list;
  li = li.map(l => {
    return {
      title: l.index + '.' + l.title,
      href: l.share_card_view.replace("element/share", `session/${TYPES.get(l.type)}`),
      people: parseInt(l.stat.finish_num),
      learned: l.extend.learn_status !== 0
    }
  });
  console.log(li);
  return li;
}

// 获取第{id}页
async function getPage(index) {
  let resp = await req({
    method: "POST",
    headers: {"Content-type": "application/x-www-form-urlencoded"},
    url: "https://m.umu.cn/uapi/v2/element/list",
    data: `t=${new Date().getTime()}&parent_id=${GROUPID}&page=${index}&size=15&get_draft=0`
  });
  return JSON.parse(resp.responseText).data;
}

// 获取所有列表
async function getIds() {
  let ids = [];
  let meta = await getPage(1);
  const pages = meta.page_info.total_page_num;
  for (let p = 1; p <= pages; p++) {
    let currentPage = await getPage(p);
    for (let dir of currentPage.list){
      let chapterIds = await getChapters(dir.id, parseInt(dir.session_count));
      ids = ids.concat(chapterIds);
    }
  }
  return ids;
}

// 根据人数进行过滤
function doFilter(filter, data) {
  console.log(data)
  // 【模式,值】
  let [mode, v] = filter.split(":");
  switch (mode) {
    case 's':
      return data.filter(e => e.title !== undefined && e.title.includes(v));
    case 'gt':
      return data.filter(e => e.people >= parseInt(v));
    case 'lt':
      return data.filter(e => e.people <= parseInt(v));
    case 'd':
      return data.filter(e => e.learned);
    case '!d':
      return data.filter(e => !e.learned);
    default:
      return [];
  }
}

(function () {
  GM_addStyle(`#gm_box{
  position: fixed;
  z-index: 9999;
  top: 0vh;
  left: 0;
  border: 1px solid #a0c5fd;
  border-radius: 5px;
  height: 95vh;
  width: 3.4rem;
  display: flex;
  flex-flow: column;
  }

  #gm_box input {
  margin: 10px;
  }

  #gm_box .show{
  height: 90vh;
  width: 3.4rem;
  margin: 3px auto;
  overflow: auto;
  padding: 5px;
  }

  #gm_box li {
  margin: 5px;
  border-left: 3px solid #fa541c;
  background: #e6e6e6;
  border-radius: 3px;
  padding: 3px;
  color: #114979;
  cursor: pointer;
  line-height: 1.2em;
  }

  #gm_box li.learned {
  border-left: 3px solid #52c41a;
  }

  #gm_box li.clicked {
  border-left: 3px solid #73289b;
  }

  #gm_box .state {
  text-align: center;
  align-self: center;
  font-size: 0.7em;
  }

  #gm_box .state span {
  color: #ff7875;
  }
  `);
  let container = document.createElement("div");
  container.id = "gm_box";
  container.innerHTML = `
  <input type="text" value="s:"/>
  <div class="state">Enter以开始搜索</div>
  <div class="show">
  <ol>
  </ol>
  </div>
  `;
  document.body.appendChild(container);

  let reader = document.querySelector("#gm_box input");
  let li = [];
  reader.onkeypress = async (e) => {
    let ol = document.querySelector(".show>ol");
    let state = document.querySelector("div.state");

    if (e.code === "Enter") {
      // 清除之前内容
      ol.innerHTML = "";

      // 检查缓存
      if (li.length === 0) {
        state.textContent = "正在加载中,请稍后...";
        li = await getIds();
      }

      let res = doFilter(e.target.value, li);
      // 展示结果
      state.textContent = `${res.length} 条结果`;
      for (let l of res) {
        let li = document.createElement("li");
        li.textContent = `${l.title}`;
        ol.appendChild(li);
        if (l.learned) li.classList.add("learned");

        li.onclick = () => {
          // 点击变色
          li.classList.add("clicked");
          window.open(l.href)
        };
      }
    }
  };

})();