ahc_mintoku_user_script

AHCをみんなで解く会の参加者用のユーザースクリプトです

// ==UserScript==
// @name         ahc_mintoku_user_script
// @namespace    http://sample.com/
// @version      0.9
// @description  AHCをみんなで解く会の参加者用のユーザースクリプトです
// @author       hiraku
// @match        https://kenkoooo.com/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    function extention(participants, teamsPage, rankingPage, rankingPageText, rankingPageStyle, participantStyle, hashTags, contestPage, contestName) {
        const MINTOKU_RANKING_PAGE_ID = "mintokuRankingPageLink";
        function createTweetText(contestName, participantInfo, participant, hashTags) {
            return contestName + "での成績\n" + 
            "チーム: " + participantInfo.teamRank + "位(" + participantInfo.teamName + ": " + participantInfo.teamScore + "点)\n" +
            "個人: " + participantInfo.personalRank + "位(" + participant + ": " + participantInfo.personalScore + "点)\n" +
            hashTags + "\n";
        }
        if (!document.getElementById(MINTOKU_RANKING_PAGE_ID)) {
            var ranking = Array.from(document.querySelectorAll("div.mb-2.row")).map(e => e.children[0]);
            // チーム順位表ページへのリンク挿入
            let rankingPageLink = document.createElement("a");
            rankingPageLink.innerHTML = rankingPageText;
            rankingPageLink.setAttribute('id', MINTOKU_RANKING_PAGE_ID);
            rankingPageLink.setAttribute('style', rankingPageStyle);
            rankingPageLink.setAttribute('href', rankingPage);
            ranking[0].appendChild(rankingPageLink);
        }
        // IDの横に(チーム名: discord名)を追加
        var targets = Array.from(document.getElementsByTagName('tbody')[2].getElementsByTagName('tr')).map(e => e.children[1]);
        for (let i = 0; i < targets.length; i++) {
            let participant = targets[i].firstChild.innerHTML;
            // Show Rating機能の対応
            participant = participant.match(/users\/(?<user>\S+)"/)?.groups?.user ?? participant;
            if (participant in participants) {
                let newa = document.createElement("a");
                newa.innerHTML = "(" + participants[participant].teamName + ": " + participants[participant].displayName + ")";
                newa.setAttribute('style', participantStyle);
                newa.setAttribute('href', teamsPage);
                targets[i].appendChild(newa);

                // Share 皆解会!! 機能の対応
                let shareit = targets[i].children[1]?.innerHTML;
                if (shareit != null && shareit.match("Share it!")) {
                    const url = new URL("https://twitter.com/intent/tweet");
                    url.searchParams.set("url", contestPage);
                    url.searchParams.set("text", createTweetText(contestName, participants[participant], participant, hashTags));

                    let newdiv = document.createElement("div");
                    newdiv.setAttribute('class', 'text-right');
                    let newa = document.createElement("a");
                    newa.innerHTML = "Share 皆解会!!";
                    newa.setAttribute('href', url);
                    newa.setAttribute('target', '_blank');
                    newa.setAttribute('rel', 'noopener noreferrer');
                    newa.setAttribute('class', 'btn btn-link');
                    newdiv.appendChild(newa);
                    targets[i].appendChild(newdiv);
                }
            }
        }
    }

    window.onload = function(){
        'use strict';

        // 1. new XMLHttpRequest オブジェクトを作成
        let xhr = new XMLHttpRequest();
        const gasUrl = "https://script.google.com/macros/s/AKfycbzf5VFF6aobcxrrb7jChtQF8vQIMUrwklmjxyhNOZtadVt6ljddgEep0JqOWlEsAulGmg/exec";

        // 2. 設定: URL /article/.../load に対する GET-リクエスト
        xhr.open('GET', gasUrl);

        // 3. ネットワーク経由でリクエスト送信
        xhr.send();

        xhr.onload = function() {
            if (xhr.status != 200) { // レスポンスの HTTP ステータスを解析
                alert(`Error ${xhr.status}: ${xhr.statusText}`); // e.g. 404: Not Found
            } else { // show the result
                // console.log(xhr.response); // responseText is the server
                moge(JSON.parse(xhr.response));
            }
        };

        function moge(response) {
            const locationHash = response.locationHash; // 発火する / しないの判定用
            const participants = response.participants;
            const teamsPage = response.teamsPage;
            const rankingPage = response.rankingPage;
            const rankingPageText = response.rankingPageText ? response.rankingPageText : "チーム順位表";
            const rankingPageStyle = response.rankingPageStyle ? response.rankingPageStyle : 'font-size:30px;';
            const participantStyle = response.participantStyle ? response.participantStyle : 'font-size:11px; color:gray; margin:0 4px;';
            const hashTags = response.hashTags ? response.hashTags : '#皆解会';
            const contestPage = response.contestPage;
            const contestName = response.contestName;

            var elem = "";

            // document生成完了後にsendMessageのresponseを取得したときの対応
            if (location.hash === locationHash && document.getElementsByTagName('tbody')[2]){
                elem = document.getElementById("root").firstChild.children[1].children[5];
                extention(participants, teamsPage, rankingPage, rankingPageText, rankingPageStyle, participantStyle, hashTags, contestPage, contestName);
            }

            var observer = new MutationObserver(function () {
                if (location.hash === locationHash) {
                    if (document.getElementsByTagName('tbody')[2] && elem !== document.getElementById("root").firstChild.children[1].children[5]) {
                        elem = document.getElementById("root").firstChild.children[1].children[5];
                        extention(participants, teamsPage, rankingPage, rankingPageText, rankingPageStyle, participantStyle, hashTags, contestPage, contestName);
                    }
                }
            });
            const config = {
                attributes: true,
                childList: true,
                characterData: true,
                subtree: true
            };

            observer.observe(document, config);
        }
    };
})();