您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
automaticly fetch codeforce submissions and verify it.
// ==UserScript== // @name ucr cs141 grader helper script // @namespace http://tampermonkey.net/ // @version 2024-10-12 // @description automaticly fetch codeforce submissions and verify it. // @author BugParty // @match https://www.gradescope.com/courses/873565/questions/* // @icon https://www.google.com/s2/favicons?sz=64&domain=gradescope.com // @require https://cdnjs.cloudflare.com/ajax/libs/decimal.js/9.0.0/decimal.min.js // @home-url https://github.com/bugparty/ucr_cs141_gradescope_helper // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; /* you need to apply for codeforce api key and secret */ const apiKey = '123'; const apiSecret = '123'; console.log("inject started"); async function signCodeforcesUrl(apiKey, apiSecret, methodName, params) { // Generate a random 6-character string const rand = [...window.crypto.getRandomValues(new Uint8Array(3))].map(b => b.toString(16).padStart(2, '0')).join(''); // Current time in UNIX timestamp format const currentTime = Math.floor(Date.now() / 1000); // Adding apiKey and time to params params.apiKey = apiKey; params.time = currentTime; // Sort the parameters lexicographically by keys and values const sortedParams = Object.keys(params).sort().reduce((acc, key) => { acc.push(`${key}=${params[key]}`); return acc; }, []).join('&'); // Concatenating the string as per the documentation const stringToHash = `${rand}/${methodName}?${sortedParams}#${apiSecret}`; //console.log("api string", stringToHash); // Encoding the string to a Uint8Array const encoder = new TextEncoder(); const dataToHash = encoder.encode(stringToHash); // Hashing the string using SHA-512 const hashBuffer = await window.crypto.subtle.digest('SHA-512', dataToHash); const hashArray = Array.from(new Uint8Array(hashBuffer)); // Convert buffer to byte array const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); // Convert bytes to hex string // Forming the apiSig const apiSig = `${rand}${hashHex}`; // Creating the signed URL const signedUrl = `https://codeforces.com/api/${methodName}?${sortedParams}&apiSig=${apiSig}`; return signedUrl; } async function fetchData(url) { try { // Sending the HTTP request to the specified URL const response = await fetch(url); // Check if the response was successful if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } // Parsing the JSON response into an object const data = await response.json(); // Use the data object as needed console.log(data); return data; } catch (error) { console.error('Failed to fetch data:', error); } } function isValidInteger(str) { const invalid_val = {valid: false, val: -1}; const regex = /^\s*\d+\s*$/; if(!regex.test(str)){ return invalid_val; } const num = Number(str); return {valid: true, val: num}; } $(document).ready(function(){ const answers_id_queries = ["#question_41404944 > div:nth-child(3) > div:nth-child(4) > div > div > span", "#question_41404944 > div:nth-child(3) > div:nth-child(6) > div > div > span", "#question_41404944 > div:nth-child(3) > div:nth-child(8) > div > div > span", "#question_41404944 > div:nth-child(3) > div:nth-child(10) > div > div > span", "#question_41404944 > div:nth-child(3) > div:nth-child(12) > div > div > span" ]; const report_title_selector = '#question_41404944_text_12'; const anwers_titles = ["Lost in the Shuffle","Juice Box", "The Stairs", "Chocolate Frogs", "Wizard Chess"]; const methodName = 'contest.status'; const params = { contestId: '551192', asManager: true }; const points_html_template = '<div id="bowman-total-points" class="form--textInput form--textInput-prominent form--textInput-med form--textInput-readOnly u-preserveWhitespace"><span> %points% </span></div>'; let endOfTitle = $("#question_41404944 > div:nth-child(2)"); console.log(endOfTitle); const button_title = " Fetch Status from codeforce"; endOfTitle.before('<button type="button" id="bowman_super_btn" class="tiiBtn tiiBtn-secondary actionBar--action" tabindex="0" style="display: inline-block;"><span><i class="fa" role="img" aria-hidden="true"></i><span> Fetch Status from codeforce</span></span></button>'); $("#bowman_super_btn").click(function() { console.log("super button clicked, starting working"); $(this).prop('disabled', true); // Disable the button $(this).text('Please wait... if not resepond for 10seconds, refresh the page'); // Change button text signCodeforcesUrl(apiKey, apiSecret, methodName, params) .then(signedUrl => { console.log('Signed URL:', signedUrl) let submissions; fetchData(signedUrl) .then(data => { if (data.status === 'OK') submissions = data.result; else{ $("#bowman_super_btn").text("codeforce return an invalid response:"+ data.status) return; } const user_cf_id = $("#question_41404944 > div:nth-child(3) > div:nth-child(2) > div > div > span").text().trim() console.log("user codeforce handle is ", user_cf_id); let answers_ids = []; let total_points = new Decimal(0); for(let i=0;i<answers_id_queries.length;i++){ let element = $(answers_id_queries[i]); let the_id = element.text(); console.log("problem ", anwers_titles[i], " id: ", the_id); let num_id = isValidInteger(the_id); answers_ids.push(num_id.val); if (num_id.valid){ let found_submission = submissions.find(obj => obj.id === num_id.val); console.log(found_submission); let isSameUser = found_submission.author.members[0].handle === user_cf_id; if (!isSameUser) { element.append("❌ different user handle found: ", found_submission.author.members[0].handle); continue; } let problem_name = found_submission.problem.name; if (problem_name != anwers_titles[i]){ element.append("❌ wrong problem: ", problem_name); continue; } if (found_submission.points == undefined){ element.append("❌ no score found, verdict: ", found_submission.verdict); continue; } element.append(" ✅ point: ", found_submission.points, " handle:", found_submission.author.members[0].handle, " problem: ", problem_name); total_points = total_points.plus(found_submission.points); }else{//invalid submission element.text(the_id + " ❌ invalid submission id"); } } //insert total score before Report let points_text = "total points: " + total_points + " subtract 5 by " + (Decimal.sub(5,total_points)); let points_html = points_html_template.replace("%points%", points_text); if($('#bowman-total-points').length==0){ $(report_title_selector).before(points_html); }else{ $('#bowman-total-points > span').text(points_text); } $("#bowman_super_btn").text(button_title); $("#bowman_super_btn").prop('disabled', false); }) .catch(error => { console.log(error); $("#bowman_super_btn").text(error + ' ' + button_title); $("#bowman_super_btn").prop('disabled', false); //reset scores if($('#bowman-total-points').length!=0){ $('#bowman-total-points > span').text("no points available"); } } ); } ) .catch(error => console.error('Error signing URL:', error)); }); }); })();