AtCoder Duplicate Checker

重複提出をチェックします。 Check for duplicate submissions.

Per 19-02-2023. Zie de nieuwste versie.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name         AtCoder Duplicate Checker
// @namespace    https://github.com/Raclamusi
// @version      1.0.1
// @description  重複提出をチェックします。 Check for duplicate submissions.
// @author       Raclamusi
// @supportURL   https://github.com/Raclamusi/atcoder-duplicate-checker
// @match        https://atcoder.jp/contests/*/tasks/*
// @match        https://atcoder.jp/contests/*/submit*
// @grant        none
// @license      MIT
// ==/UserScript==

// AtCoder Duplicate Checker
//
// Copyright (c) 2022 Raclamusi
//
// This software is released under the MIT License, see https://github.com/Raclamusi/atcoder-duplicate-checker/blob/main/LICENSE .

(function () {
    "use strict";

    const getButtonText = lang => {
        const texts = {
            ja: "重複チェック中...",
            en: "Duplicate Checking...",
        };
        return texts[lang in texts ? lang : "ja"];
    };

    const getMessage = (lang, time) => {
        const messages = {
            ja: ["過去に同じコードを提出しています。", "本当に提出しますか?"],
            en: ["You have submitted the same code before.", "Are you sure you want to submit it?"],
        };
        const msg = messages[lang in messages ? lang : "ja"];
        return `${msg[0]} (${time})\n${msg[1]}`;
    };

    const getSubmissions = async contest => {
        const response = await fetch(`https://atcoder.jp/contests/${contest}/submissions/me`);
        const htmlText = await response.text();
        const iter = htmlText.matchAll(/<tr(?:.|\s)*?<time[^>]*>(.+?)<\/time>(?:.|\s)*?submissions\/(\d+)(?:.|\s)*?<\/tr>/g);
        return [...iter].map(([_, time, id]) => ({ time, id }));
    };

    const getSubmittedCode = async (contest, id) => {
        const response = await fetch(`https://atcoder.jp/contests/${contest}/submissions/${id}`);
        const htmlText = await response.text();
        const escapedCode = htmlText.match(/<pre id="submission-code"[^>]*>([^<]*)<\/pre>/)[1];
        const preElement = document.createElement("pre");
        preElement.innerHTML = escapedCode;
        return preElement.textContent;
    };

    const getDuplicateSubmisionTime = async code => {
        for (const { time, id } of await getSubmissions(contestScreenName)) {
            if (code === await getSubmittedCode(contestScreenName, id)) {
                return time;
            }
        }
        return null;
    };

    const submitButton = document.getElementById("submit");
    const submitButtonClickListener = e => {
        // チェックを待つため、一旦提出をキャンセル
        e.preventDefault();

        // チェック中であれば即終了
        if (submitButton.disabled) return;

        // 提出ボタンの表示を変更
        submitButton.disabled = true;
        const buttonText = submitButton.textContent;
        submitButton.textContent = getButtonText(LANG);

        (async () => {
            // 重複チェック
            const time = await getDuplicateSubmisionTime(getSourceCode());
            if (time === null || confirm(getMessage(LANG, time))) {
                // チェックが通ったら、このイベントリスナを外して本来の提出の処理をする
                submitButton.removeEventListener("click", submitButtonClickListener);
                setTimeout(() => submitButton.click());
            }

            // 提出ボタンの表示を戻す
            submitButton.disabled = false;
            submitButton.textContent = buttonText;
        })();
    };
    submitButton.addEventListener("click", submitButtonClickListener, { passive: false });
})();