GuctOJ Helper 发布页 - FReQuenter 的 cnblogs 博客

我不会弄自动更新啊,OJ更新了记得来这里再复制一下代码

GutcOJ Helper 基于油猴,不知道什么是油猴请自行百度

适配 GuctOJ 3.x 和 2.0 版本。

网址:https://oj.oimaster.top

经由 NFLSOJ Helper 改编而来。

NFLSOJ Helper 发布页:http://www.nfls.com.cn:20035/article/1197

更新日志:https://www.luogu.com.cn/paste/vlwxsykx

待完成:

  • 修改背景

历史版本:

v0.4.0

  • 【更新】更新网址,适配 GutcOJ 3.1

源码

// ==UserScript==
// @name         GutcOJ Helper
// @namespace    https://github.com/NFLSCode/nflsoj-helper
// @version      0.4.0
// @description  Use GutcOJ More Easily
// @author       FReQuenter(NFLSOJ Helper by lexiyvv & ppip & GlaceonVGC & ACrazySteve)
// @match        *://oj.oimaster.cf/*
// @match        *://yun.oimaster.ml/*
// @match        *://oj.oimaster.top/*
// @require      http://oj.oimaster.cf/cdnjs/jquery/3.3.1/jquery.min.js
// @require      http://oj.oimaster.cf/cdnjs/blueimp-md5/2.10.0/js/md5.min.js
// @grant        GM_setClipboard
// @grant        GM_info
// ==/UserScript==
/* eslint-disable no-undef */
/* eslint-disable curly */

const domain = window.location.pathname, repo = "NFLSCode/nflsoj-helper";
try {
    let username = $(".dropdown.item")[1].children[0].innerText.slice(0, -1);
    /******************** contest module ********************/
    if (document.body.innerHTML.includes("我的比赛")) $(".menu")[1].innerHTML += `<a class="item" href="/summary/?username=${username}"><i class="tasks icon"></i>总结</a>`;
    if (/contest\/\d+(?!\d|\/[a-z])/.test(domain)) document.body.innerHTML = document.body.innerHTML.replaceAll("<!--", "").replaceAll("-->", "");
} catch {
    console.info('iframe');
}
async function getDOM(href) {
    return new DOMParser().parseFromString(await $.get(href), "text/html");
}


/******************** rating module ********************/
function getEloWinProbability(ra, rb) {
    return 1.0 / (1 + Math.pow(10, (rb - ra) / 400.0));
}
function getContestantSeed(contestantIndex, allContestants) {
    let seed = 1;
    let rating = allContestants[contestantIndex].currentRating;
    for (let i = 0; i < allContestants.length; i++) if (contestantIndex != i) seed += getEloWinProbability(allContestants[i].currentRating, rating);
    return seed;
}
function sum(arr) {
    let s = 0;
    for (let ind in arr) s += arr[ind];
    return s;
}
function getRatingSeed(rating, allContestants) {
    return 1 + sum(allContestants.map(c => getEloWinProbability(c.currentRating, rating)));
}
function getAverageRank(contestant, allContestants) {
    const realRank = allContestants[contestant].rank;
    const expectedRank = getContestantSeed(contestant, allContestants);
    const average = Math.sqrt(realRank * expectedRank);
    return average;
}
function getRatingToRank(contestantIndex, allContestants) {
    let averageRank = getAverageRank(contestantIndex, allContestants);
    let left = 1;// contestant.getPrevRating() - 2 * minDelta;
    let right = 8000;// contestant.getPrevRating() + 2 * maxDelta;
    while (right - left > 1) {
        const mid = (left + right) / 2;
        const seed = getRatingSeed(mid, allContestants);
        if (seed < averageRank) right = mid;
        else left = mid;
    }
    return left;
}
function calcRating(allContestants) {
    let deltas = [];
    const numberOfContestants = allContestants.length;
    for (let i = 0; i < allContestants.length; i++) {
        const expR = getRatingToRank(i, allContestants);
        deltas[i] = ((expR - allContestants[i].currentRating) / 2);
    }
    const deltaSum = sum(deltas);
    const inc = -deltaSum / numberOfContestants - 1;
    deltas = deltas.map(d => d + inc);
    const zeroSumCount = Math.min(Math.trunc(4 * Math.round(Math.sqrt(numberOfContestants))), numberOfContestants);
    const deltaSum2 = sum(deltas.slice(0, zeroSumCount));
    const inc2 = Math.min(Math.max(-deltaSum2 / zeroSumCount, -10), 0);
    deltas = deltas.map(d => d + inc2);
    return allContestants.map((contestant, i) => {
        let n = Math.round(deltas[i]);
        return `<td>${Math.round(contestant.currentRating + n)}<span class="rating_${n >= 0 ? "up" : "down"}">(${(n < 0 ? "" : "+") + n})</span></td>`;
    });
}
async function Rating() {
    if (document.getElementsByTagName("thead")[0].rows[0].innerHTML.includes("<th>Rating(Δ)</th>")) return ;
    const hisRating = $(".center.aligned.header")[0].innerText.replaceAll("(", "\\(").replaceAll(")", "\\)") + `<\\/td>[\\s\\S]*?(<td>\\d{3,4}[^/]*?<\\/td>)`,
          curRating = /<i class="star icon"><\/i>积分 (\d+)/;
    let arr = document.getElementsByTagName("tbody")[0].rows, c = Array.from({length: arr.length}, (v, i) => i);
    c = (await $.get(arr[0].innerHTML.match(/\/user\/\d+/)[0])).match(hisRating) != null
        ? await Promise.all(c.map(async i => (await $.get(arr[i].innerHTML.match(/\/user\/\d+/)[0])).match(hisRating)[1]))
        : calcRating(await Promise.all(c.map(async i => ({
            rank: arr[i].children[0].innerText,
            currentRating: parseInt((await $.get(arr[i].innerHTML.match(/\/user\/\d+/)[0])).match(curRating)[1])
        }))));
    document.getElementsByTagName("thead")[0].rows[0].innerHTML += "<th>Rating(Δ)</th>";
    for (let i = 0; i < arr.length; ++i) arr[i].innerHTML += c[i];
}
/******************** rank module ********************/
if (/\d+\/(ranklist|repeat)/.test(domain)) {
    let head = document.getElementsByTagName("tr")[0], pos = /ranklist/.test(domain) ? head.innerHTML.indexOf("</th>") + 5 : 0;
    if (head.innerHTML.indexOf("用户名") == -1) {
        let arr = document.getElementsByTagName("tbody")[0].rows;
        head.innerHTML = head.innerHTML.slice(0, pos) + "<th>用户名</th>" + head.innerHTML.slice(pos);
        for (let i = 0; i < arr.length; ++i) {
            let pos = /ranklist/.test(domain) ? arr[i].innerHTML.indexOf("</td>") : 0;
            arr[i].innerHTML = arr[i].innerHTML.slice(0, pos) + name[i] + arr[i].innerHTML.slice(pos);
        }
    }
    if (/ranklist/.test(domain)) {
        $(".padding")[0].innerHTML =
              `<span class="ui mini right floated labeled blue icon button" id="rating" style="top:6px;"><i class="calculator icon" id=calc></i>Predict Rating</span>`
            + $(".padding")[0].innerHTML;
        rating.addEventListener("click", async () => {
            rating.childNodes[1].data = "Please Wait...";
            await Rating();
            rating.childNodes[1].data = "Done!";
        });
    }
}
/******************** dashboard ********************/
if (domain == "/") {
    let col = $(".eleven.wide.column")[0], ind = col.innerHTML.search(/<h4 class="ui top attached block header"><i class="ui signal/);
    col.innerHTML = col.innerHTML.slice(0, ind) + `
    <h4 class="ui top attached block header">
      <style="width:20px;height:20px;position:relative;top:-3px;">GutcOJ Helper 控制面板
    </h4>
    <div class="ui bottom attached segment">
      <table class="ui very basic table" style="table-layout: fixed;">
        <tr><td>
          <h4 style="display:inline;">基础版(NFLSOJ Helper)链接</h4>
          <a class="ui red button" style="position:relative;left:20px;" href="https://github.com/${repo}/">
            <i class="ui linkify icon"></i>NFLSOJ Helper 主页
          </a><a class="ui orange button" id="l2" style="position:relative;left:20px;">
            <i class="repeat icon"></i>NFLSOJ Helper 最新版
          </a>
        </td></tr>
        <tr><td>
          <h4 style="display:inline;">主要功能</h4>
          <a class="ui yellow button" id="f1" style="position:relative;left:20px;">
            <i class="code icon"></i>延长登录时间
          </a>
          <a class="ui green button" id="f2" style="position:relative;left:20px;">
            <i class="code icon"></i>查找用户
          </a>
          <a class="ui blue button" id="f3" style="position:relative;left:20px;">
            <i class="code icon"></i>Predict Rating
          </a>
          <a class="ui purple button" id="f4" style="position:relative;left:20px;">
            <i class="code icon"></i>显示隐藏的用户名
          </a>
        </td></tr>
        <tr><td>
          <h4 style="display:inline;">发布页链接</h4>
          <a class="ui button" id="g1" style="position:relative;left:20px;" href="http://www.nfls.com.cn:20035/article/1197">
            <i class="linkify icon"></i>NFLSOJ: NFLSOJ Helper 发布页
          </a>
          <a class="ui black button" id="g2" style="position:relative;left:20px;" href="https://www.luogu.com.cn/blog/frequenter5156/GutcOJ-Helper">
            <i class="linkify icon"></i>洛谷博客:GutcOJ Helper 发布页
          </a>
        </td></tr>
      </table>
    </div>` + col.innerHTML.slice(ind);
    l2.addEventListener("click", async () => {
        window.location.href = `https://github.com/${repo}/releases/download/${(await $.get(`https://api.github.com/repos/${repo}/releases/latest`)).tag_name}/nflsoj-helper.min.user.js`;
    });
    f1.addEventListener("click", () => {
        document.cookie = `${document.cookie.match(/(^| )(login=[^;]*)(;|$)/)[2]};expires=Wed, 04 Aug 2077 01:00:00 GMT`;
        alert("登录时间延长至 Wed, 04 Aug 2077 01:00:00 GMT。\n在别处登录或登出后无效!");
    });
    f2.addEventListener("click", () => {
        alert("请到首页顶端查看!");
    });
    f3.addEventListener("click", () => {
        alert("请到比赛“排行榜”界面点击“Predict Rating”查看!");
    });
    f4.addEventListener("click", () => {
        alert("请到比赛“排行榜”界面查看,仅在该界面生效。\n虽然但是,截止目前,oimaster并没有这样做过。");
    });
}

v0.3.2

  • 【修复】删除名字查找

源码

// ==UserScript==
// @name         GutcOJ Helper
// @namespace    https://github.com/NFLSCode/nflsoj-helper
// @version      0.3.2
// @description  Use GutcOJ More Easily
// @author       FReQuenter(NFLSOJ Helper by lexiyvv & ppip & GlaceonVGC & ACrazySteve)
// @match        *://oj.oimaster.cf/*
// @match        *://yun.oimaster.ml/*
// @require      http://oj.oimaster.cf/cdnjs/jquery/3.3.1/jquery.min.js
// @require      http://oj.oimaster.cf/cdnjs/blueimp-md5/2.10.0/js/md5.min.js
// @grant        GM_setClipboard
// @grant        GM_info
// ==/UserScript==
/* eslint-disable no-undef */
/* eslint-disable curly */

const domain = window.location.pathname, repo = "NFLSCode/nflsoj-helper";
try {
    let username = $(".dropdown.item")[1].children[0].innerText.slice(0, -1);
    /******************** contest module ********************/
    if (document.body.innerHTML.includes("我的比赛")) $(".menu")[1].innerHTML += `<a class="item" href="/summary/?username=${username}"><i class="tasks icon"></i>总结</a>`;
    if (/contest\/\d+(?!\d|\/[a-z])/.test(domain)) document.body.innerHTML = document.body.innerHTML.replaceAll("<!--", "").replaceAll("-->", "");
} catch {
    console.info('iframe');
}
async function getDOM(href) {
    return new DOMParser().parseFromString(await $.get(href), "text/html");
}


/******************** rating module ********************/
function getEloWinProbability(ra, rb) {
    return 1.0 / (1 + Math.pow(10, (rb - ra) / 400.0));
}
function getContestantSeed(contestantIndex, allContestants) {
    let seed = 1;
    let rating = allContestants[contestantIndex].currentRating;
    for (let i = 0; i < allContestants.length; i++) if (contestantIndex != i) seed += getEloWinProbability(allContestants[i].currentRating, rating);
    return seed;
}
function sum(arr) {
    let s = 0;
    for (let ind in arr) s += arr[ind];
    return s;
}
function getRatingSeed(rating, allContestants) {
    return 1 + sum(allContestants.map(c => getEloWinProbability(c.currentRating, rating)));
}
function getAverageRank(contestant, allContestants) {
    const realRank = allContestants[contestant].rank;
    const expectedRank = getContestantSeed(contestant, allContestants);
    const average = Math.sqrt(realRank * expectedRank);
    return average;
}
function getRatingToRank(contestantIndex, allContestants) {
    let averageRank = getAverageRank(contestantIndex, allContestants);
    let left = 1;// contestant.getPrevRating() - 2 * minDelta;
    let right = 8000;// contestant.getPrevRating() + 2 * maxDelta;
    while (right - left > 1) {
        const mid = (left + right) / 2;
        const seed = getRatingSeed(mid, allContestants);
        if (seed < averageRank) right = mid;
        else left = mid;
    }
    return left;
}
function calcRating(allContestants) {
    let deltas = [];
    const numberOfContestants = allContestants.length;
    for (let i = 0; i < allContestants.length; i++) {
        const expR = getRatingToRank(i, allContestants);
        deltas[i] = ((expR - allContestants[i].currentRating) / 2);
    }
    const deltaSum = sum(deltas);
    const inc = -deltaSum / numberOfContestants - 1;
    deltas = deltas.map(d => d + inc);
    const zeroSumCount = Math.min(Math.trunc(4 * Math.round(Math.sqrt(numberOfContestants))), numberOfContestants);
    const deltaSum2 = sum(deltas.slice(0, zeroSumCount));
    const inc2 = Math.min(Math.max(-deltaSum2 / zeroSumCount, -10), 0);
    deltas = deltas.map(d => d + inc2);
    return allContestants.map((contestant, i) => {
        let n = Math.round(deltas[i]);
        return `<td>${Math.round(contestant.currentRating + n)}<span class="rating_${n >= 0 ? "up" : "down"}">(${(n < 0 ? "" : "+") + n})</span></td>`;
    });
}
async function Rating() {
    if (document.getElementsByTagName("thead")[0].rows[0].innerHTML.includes("<th>Rating(Δ)</th>")) return ;
    const hisRating = $(".center.aligned.header")[0].innerText.replaceAll("(", "\\(").replaceAll(")", "\\)") + `<\\/td>[\\s\\S]*?(<td>\\d{3,4}[^/]*?<\\/td>)`,
          curRating = /<i class="star icon"><\/i>积分 (\d+)/;
    let arr = document.getElementsByTagName("tbody")[0].rows, c = Array.from({length: arr.length}, (v, i) => i);
    c = (await $.get(arr[0].innerHTML.match(/\/user\/\d+/)[0])).match(hisRating) != null
        ? await Promise.all(c.map(async i => (await $.get(arr[i].innerHTML.match(/\/user\/\d+/)[0])).match(hisRating)[1]))
        : calcRating(await Promise.all(c.map(async i => ({
            rank: arr[i].children[0].innerText,
            currentRating: parseInt((await $.get(arr[i].innerHTML.match(/\/user\/\d+/)[0])).match(curRating)[1])
        }))));
    document.getElementsByTagName("thead")[0].rows[0].innerHTML += "<th>Rating(Δ)</th>";
    for (let i = 0; i < arr.length; ++i) arr[i].innerHTML += c[i];
}
/******************** rank module ********************/
if (/\d+\/(ranklist|repeat)/.test(domain)) {
    let head = document.getElementsByTagName("tr")[0], pos = /ranklist/.test(domain) ? head.innerHTML.indexOf("</th>") + 5 : 0;
    if (head.innerHTML.indexOf("用户名") == -1) {
        let arr = document.getElementsByTagName("tbody")[0].rows;
        head.innerHTML = head.innerHTML.slice(0, pos) + "<th>用户名</th>" + head.innerHTML.slice(pos);
        for (let i = 0; i < arr.length; ++i) {
            let pos = /ranklist/.test(domain) ? arr[i].innerHTML.indexOf("</td>") : 0;
            arr[i].innerHTML = arr[i].innerHTML.slice(0, pos) + name[i] + arr[i].innerHTML.slice(pos);
        }
    }
    if (/ranklist/.test(domain)) {
        $(".padding")[0].innerHTML =
              `<span class="ui mini right floated labeled blue icon button" id="rating" style="top:6px;"><i class="calculator icon" id=calc></i>Predict Rating</span>`
            + $(".padding")[0].innerHTML;
        rating.addEventListener("click", async () => {
            rating.childNodes[1].data = "Please Wait...";
            await Rating();
            rating.childNodes[1].data = "Done!";
        });
    }
}
/******************** dashboard ********************/
if (domain == "/") {
    let col = $(".eleven.wide.column")[0], ind = col.innerHTML.search(/<h4 class="ui top attached block header"><i class="ui signal/);
    col.innerHTML = col.innerHTML.slice(0, ind) + `
    <h4 class="ui top attached block header">
      <style="width:20px;height:20px;position:relative;top:-3px;">GutcOJ Helper 控制面板
    </h4>
    <div class="ui bottom attached segment">
      <table class="ui very basic table" style="table-layout: fixed;">
        <tr><td>
          <h4 style="display:inline;">基础版(NFLSOJ Helper)链接</h4>
          <a class="ui red button" style="position:relative;left:20px;" href="https://github.com/${repo}/">
            <i class="ui linkify icon"></i>NFLSOJ Helper 主页
          </a><a class="ui orange button" id="l2" style="position:relative;left:20px;">
            <i class="repeat icon"></i>NFLSOJ Helper 最新版
          </a>
        </td></tr>
        <tr><td>
          <h4 style="display:inline;">主要功能</h4>
          <a class="ui yellow button" id="f1" style="position:relative;left:20px;">
            <i class="code icon"></i>延长登录时间
          </a>
          <a class="ui green button" id="f2" style="position:relative;left:20px;">
            <i class="code icon"></i>查找用户
          </a>
          <a class="ui blue button" id="f3" style="position:relative;left:20px;">
            <i class="code icon"></i>Predict Rating
          </a>
          <a class="ui purple button" id="f4" style="position:relative;left:20px;">
            <i class="code icon"></i>显示隐藏的用户名
          </a>
        </td></tr>
        <tr><td>
          <h4 style="display:inline;">发布页链接</h4>
          <a class="ui button" id="g1" style="position:relative;left:20px;" href="http://www.nfls.com.cn:20035/article/1197">
            <i class="linkify icon"></i>NFLSOJ: NFLSOJ Helper 发布页
          </a>
          <a class="ui black button" id="g2" style="position:relative;left:20px;" href="https://www.luogu.com.cn/blog/frequenter5156/GutcOJ-Helper">
            <i class="linkify icon"></i>洛谷博客:GutcOJ Helper 发布页
          </a>
        </td></tr>
      </table>
    </div>` + col.innerHTML.slice(ind);
    l2.addEventListener("click", async () => {
        window.location.href = `https://github.com/${repo}/releases/download/${(await $.get(`https://api.github.com/repos/${repo}/releases/latest`)).tag_name}/nflsoj-helper.min.user.js`;
    });
    f1.addEventListener("click", () => {
        document.cookie = `${document.cookie.match(/(^| )(login=[^;]*)(;|$)/)[2]};expires=Wed, 04 Aug 2077 01:00:00 GMT`;
        alert("登录时间延长至 Wed, 04 Aug 2077 01:00:00 GMT。\n在别处登录或登出后无效!");
    });
    f2.addEventListener("click", () => {
        alert("请到首页顶端查看!");
    });
    f3.addEventListener("click", () => {
        alert("请到比赛“排行榜”界面点击“Predict Rating”查看!");
    });
    f4.addEventListener("click", () => {
        alert("请到比赛“排行榜”界面查看,仅在该界面生效。\n虽然但是,截止目前,oimaster并没有这样做过。");
    });
}

v0.3.1

  • 【更新】修复一个图标 bug

功能:

  • Predict Rating

  • 查找用户

  • 延长登录时间

v0.3

  • 【更新】大改控制面板。

  • 【更新】发布页

功能:

  • Predict Rating

  • 查找用户

  • 延长登录时间

v0.2

  • 【更新】删除了一些多余的东西。

  • 【移除】名字颜色修改

  • 【移除】修改背景

功能:

  • Predict Rating

  • 查找用户

  • 延长登录时间

v0.1

  • 【更新】第一版。除了 url 啥都没改。

功能:

  • Predict Rating

  • 查找用户

  • 延长登录时间

  • 名字颜色修改(是 NFLSOJ 上的啦)

  • 修改背景

posted @ 2023-10-18 20:56  FReQuenter  阅读(36)  评论(1编辑  收藏  举报