AtcoderColorNEWS

お気に入りの人々の、色の変化を通知します。

// ==UserScript==
// @name         AtcoderColorNEWS
// @description  お気に入りの人々の、色の変化を通知します。
// @namespace    https://github.com/null-null-programming
// @version      0.1
// @author       null_null
// @license      MIT
// @match        https://atcoder.jp/*
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js
// ==/UserScript==

(async function () {
    //直近のコンテスト
    const latestContestScreenName = await getLatestContestScreenName();

    //既に通知したかどうかをlocalStorageで判定する。imformedFlagがtrueのときのみ通知する。
    if (imformedFlag(latestContestScreenName) === false) return;

    //お気に入りリストを取得
    let favList = JSON.parse(localStorage.fav);

    //レートを色に変換するリスト
    let color = ['灰', '茶', '緑', '水', '青', '黄', '橙', '赤', '自由'];

    //直近コンテストの結果一覧
    const latestContestResult = await getContestResultData(latestContestScreenName);

    //直近コンテストの結果一覧を辞書型に変換
    let contestResultDic = {};
    latestContestResult.forEach(res => contestResultDic[res.UserScreenName] = res);

    let string = ""; //通知用string
    //favlistのそれぞれの要素からstringを返すようにして、それをjoin
    string = favList.map(username => {
        //個人の結果
        const result = contestResultDic[username];

        //個人の結果がない場合だめ
        if (!result) return "";

        //Rateを取得し、色に変換する
        const preRate = getColorIndex(result.OldRating);
        const nowRate = getColorIndex(result.NewRating);

        //前の色よりも今の色のほうが高くない場合だめ
        if (preRate >= nowRate) return "";

        //繋げる文字列を返す
        return `${E(username)}さんのレートが${color[nowRate]}色に変わりました!\n`;
    }).join("");

    if (string.length > 0) {
        window.alert(string); //20秒後、またはクリックで消える
    }

    //localStorageに通知したコンテスト名を保存する。
    localStorage.setItem('keyContestName', latestContestScreenName);
})();

//通知済みかどうかを調べる
function imformedFlag(latestContestScreenName) {
    if (localStorage.getItem('keyContestName') !== latestContestScreenName) return true;
    return false;
}

//Rateを色に変換する
function getColorIndex(rate) {
    return Math.min(8, Math.floor(rate / 400));
}

//直近のコンテスト名を取得する。
async function getLatestContestScreenName() {
    let parser = new DOMParser();
    let archiveDom = parser.parseFromString((await $.get("https://atcoder.jp/contests/archive")), "text/html");
    let contestScreenName = archiveDom.querySelector("#main-container > div.row > div.col-lg-9.col-md-8 > div.panel.panel-default > div > table > tbody > tr:nth-child(1) > td:nth-child(2) > a");
    contestScreenName = contestScreenName.toString().split('/').pop();
    return contestScreenName;
}

async function getContestResultData(contestScreenName) {
    return await $.ajax(`https://atcoder.jp/contests/${contestScreenName}/results/json`);
}