// ==UserScript==
// @name luogu 插件集合
// @namespace http://tampermonkey.net/
// @version 0.0.3
// @description 非常好的 luogu 插件集合
// @author konyakest
// @license MIT
// @match https://www.luogu.com.cn/problem/*
// @match https://www.luogu.com.cn/paste/*
// @match https://www.luogu.com.cn/training/*
// @match https://www.luogu.com.cn/
// @match https://api.loj.ac/
// @icon https://fecdn.luogu.com.cn/luogu/logo.png
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @grant GM_xmlhttpRequest
// ==/UserScript==
/*
- 浏览记录
- 显示代码长度
- 简要题面
- 首页暂存内容
- 卷题情况
- 测试用例
- 显示今日AC
*/
const PASTEID = undefined/*请自行设置,如"eyb488k7"*/;
const TRAINING_ID = undefined/*请自行设置,如100,**必须是团队作业题单,且您必须有题单的编辑权限***/;
function URLmatch(pat){
return Boolean(window.location.href.match(pat));
}
async function 显示代码长度(){
const ENABLE_CACHE = true;
/*
ENABLE_CACHE:是否使用缓存(缓存即将获取的结果存下来,下一次直接使用)
根据代码长度判定颜色的方案可以自行修改 makeColoredTextBySize 中的内容,我相信这部分是可以直接看懂的(*´▽`)ノノ
*/
function getMid(lst){
// console.log(lst);
return (function(x){
if(x<1024){
return x+" B";
}
return (x/1024).toFixed(2)+" K";
})(lst[Math.floor(lst.length/2)]);
}
function makeColoredText(color,text){
return `
<a data-v-0640126c="" data-v-beeebc6e=""
colorscheme="default" class="color-default" data-v-b5709dda="">
<span data-v-71731098="" data-v-beeebc6e="" class="lfe-caption"
style="background: ${color}; color: rgb(255, 255, 255);" data-v-0640126c="">${text}
</span>
</a>
`
}
let colors = {
red: "rgb(254,76,97)",
orange: "rgb(243,156,17)",
yellow: "rgb(255,193,22)",
green: "rgb(82,196,26)",
blue: "rgb(52,152,219)",
purple: "rgb(157,61,207)",
black: "rgb(14,29,105)"
};
function makeColoredTextBySize(size,text){
size = size.split(' ');
size = Number(size[1]==="K"?size[0]*1024:size[0]);
const K = 1024;
if(size <= 1*K) return makeColoredText(colors.yellow,text);
if(size <= 2*K) return makeColoredText(colors.green,text);
if(size <= 3*K) return makeColoredText(colors.blue,text);
if(size <= 4*K) return makeColoredText(colors.purple,text);
return makeColoredText(colors.black,text);
}
async function sampleRecord(pid){
if(ENABLE_CACHE){
if(GM_getValue(pid)&&(GM_getValue(pid)!=="NaN K")){
return GM_getValue(pid);
}
}
let lst = [];
try{
let promiselst = [];
for(let i=1;i<=5;i++){
promiselst.push(
fetch(
`https://www.luogu.com.cn/record/list?pid=${pid}&status=12&_contentOnly=1&page=${i}`
)
.then(x=>x.json())
.then(x=>x.currentData.records.result)
);
}
await Promise.all(promiselst).then(function(x){
x.forEach(x=>x.forEach(x=>lst.push(x.sourceCodeLength)));
lst.sort((a,b)=>a-b);
});
}catch(e){};
// console.log(lst);
lst.sort((a,b)=>a-b);
let res = getMid(lst);
if(ENABLE_CACHE){
GM_setValue(pid,res);
}
return res;
}
if(window.location.href.split('/')[3] === "problem"){
let field = document.querySelector(".color-inverse > div:nth-child(1)");
let clone = field.cloneNode(true);
field.parentNode.appendChild(clone);
clone.children[0].innerHTML = "平均码长";
clone.children[1].innerHTML = "正在获取中";
let size = await sampleRecord(window.location.href.split('/')[4],clone.children[1]);
clone.children[1].innerHTML = makeColoredTextBySize(size,size);
}
else if(window.location.href.match('#').length){
let all = document.querySelector(".row-wrap");
//all.childNodes.forEach(async function(x){
for(let i=0;i<all.childNodes.length;i++){
let x = all.childNodes[i];
if(!x.innerHTML){
continue;
}
let element = x.children[1];
let a = document.createElement('a');
let size = await sampleRecord(element.title);
a.innerHTML = makeColoredTextBySize(size,size);
element.appendChild(a);
};
}
}
async function 浏览记录(){
const MAX_COUNT = 20;
const DELAY = 20;//停留超过 20s 会被记录
var has_built;
function mySetTimeOut(func,tim){
let a;
a = setInterval(()=>{func();clearInterval(a);},tim);
}
function store(problem,title,link){
let all = (GM_getValue("all") || []).slice(-MAX_COUNT);
if(!all.some(x=>x.problem === problem)){
all.push({problem:problem,title:title,link:link});
}
GM_setValue("all",all);
}
async function inPaste(){
let value = GM_getValue("doit");
if(!value){
return;
}
GM_deleteValue("doit");
let data = "";
GM_getValue("all").forEach(x => {
data += `- [${x.title}](${x.link})`
data += "\n\n"
});
await fetch(`https://www.luogu.com.cn/paste/edit/${PASTEID}`, {
"credentials": "include",
"headers": {
"Accept": "application/json, text/plain, */*",
"Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
"X-Requested-With": "XMLHttpRequest",
"X-CSRF-TOKEN": document.querySelector("meta[name=csrf-token]").content,
"Content-Type": "application/json",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin"
},
"body": JSON.stringify({"data":data}),
"method": "POST",
"mode": "cors"
});
window.location.reload();
}
function buildstatisticsbutton() {
if (has_built) return;
let tmp = document.querySelector(".operation > span:nth-child(3)");
let tmp2 = tmp.cloneNode(true);
tmp2.childNodes[0].childNodes[0].innerText = "历史记录";
tmp2.onclick = async function() {
GM_setValue("doit",true);
window.open(`https://www.luogu.com.cn/paste/${PASTEID}`);
};
tmp.parentNode.appendChild(tmp2);
has_built = true;
}
window.addEventListener('load',function(){
if(window.location.href.split('/')[3] === "paste"){
inPaste();
return;
}
let title = document.querySelector(".lfe-h1 > span:nth-child(1)")?
document.querySelector(".lfe-h1 > span:nth-child(1)").title :
document.querySelector(".lfe-h1").innerText;
let problem = window.location.href.split('/')[4];
mySetTimeOut(function(){store(problem,title,window.location.href);console.log("store!");},1000*DELAY);
})
window.addEventListener('load', buildstatisticsbutton);
setTimeout(buildstatisticsbutton, 500);
}
async function 简要题面(){
const keywords = [
"题目大意","题意","题意简述","问题描述","题面","Description"
];
async function getSolutions(){
let problem = window.location.href.split('/')[4];
let url = "https://www.luogu.com.cn/problem/solution/" + problem;
let text = await fetch(url).then(x=>x.text());
let data = text.split(`JSON.parse(decodeURIComponent("`)[1].split(`"`)[0];
let res = [];
JSON.parse(decodeURIComponent(data)).currentData.solutions.result.forEach(x => {
res.push(x.content.split("\n"));
});
return res;
}
function getProblemDescr(text){
let res = [];
let pre;
try{
text.forEach(function(x){
if(x.split(" ")[0] === pre){
throw "parse end!";
}
if(keywords.some(kwd=>x.match(kwd))&&!keywords.some(kwd=>x.split(" ")[0].match(kwd))){
pre=x.split(" ")[0];
}
if(pre){
res.push(x);
}
});
}catch(e){
if(e === "parse end!"){
let str = "";
res.forEach(x=>str+=`> ${x}\n`);
return str;
}
throw e;
}
return "";
}
async function my_marked(code){
if(code === "") return "";
let tmp;
await GM_xmlhttpRequest({
method:'post',
url:'http://www.nfls.com.cn:10611/api/markdown',
data:"s="+encodeURIComponent(code),
headers:{ "Content-Type": "application/x-www-form-urlencoded" },
onload:res=>tmp=res.responseText
});
async function dfs(dep){
await new Promise(resolve=>setTimeout(resolve,500));
if(typeof tmp !== 'undefined'){
return tmp;
}
dep === 8? tmp = '网络错误,请刷新重试': await dfs(dep+1);
}
await dfs(1);
return new DOMParser().parseFromString(tmp,"text/html").all[3].innerHTML;
}
async function addElement(){
let solutions = await getSolutions();
// window.searchSolutionKeyword=function(s){
// return !!window.__solutions.some(x=>x.some(xx=>xx.match(s)));
// };
// setTimeout(_=>console.log(window.searchSolutionKeyword,window),2000);
let tmp = document.querySelector(".operation > span:nth-child(3)");
let tmp2 = tmp.cloneNode(true);
tmp2.childNodes[0].childNodes[0].innerText = "搜索题解关键词";
tmp2.onclick = function() {
let value = prompt("请输入要找的关键词");
if(value === '' || value === null){
return;
}
if(!!solutions.some(x=>x.some(xx=>xx.match(value)))){
alert(`恭喜!题解中有关键词"${value}",快切了此题吧!`);
}
else{
alert(`题解中并没有关键词"${value}"`);
}
// await addtrainingproblem(window.location.href.split('/')[4]);
// window.open(`https://www.luogu.com.cn/training/${TRAINING_ID}#rank`);
};
tmp.parentNode.appendChild(tmp2);
let html = await getSolutions().then(function(x){
let res = "";
x.forEach(function(x){
x = getProblemDescr(x);
if(x !== ""){
res+=x+"\n\n";
}
});
return res;
});
await fetch("https://www.luogu.com.cn/paste/edit/"+PASTEID, {
"headers": {
"X-Requested-With": "XMLHttpRequest",
"X-CSRF-TOKEN": document.querySelector("meta[name=csrf-token]").content,
"Content-Type": "application/json"
},
"body": JSON.stringify({"data":html}),
"method": "POST"
});
html = await my_marked(html);
let nodes = [
document.querySelector("h2.lfe-h2:nth-child(1)"),
document.querySelector("div.marked:nth-child(2)")];
let text = nodes[0].parentNode.insertBefore(nodes[1].cloneNode(true),nodes[0]);
let title = nodes[0].parentNode.insertBefore(nodes[0].cloneNode(true),text);
title.innerText = "简要题意";
text.innerHTML = html;
let index = 0;
let show = function(){
let cnt = 0;
text.children.forEach(function(x){
x.style.display = (cnt === index?"":"none");
cnt ++;
});
};
show();
if(text.children.length === 0){
text.innerHTML = "未找到简要题意";
return;
}
let but1 = document.createElement("button");
but1.innerText = "换一个";
but1.onclick = function(){
index = (index+1)%text.children.length;
show();
};
if(text.children.length !== 1){
title.appendChild(but1);
}
let but2 = document.createElement("button");
but2.innerText = "更好的阅读体验";
but2.onclick = function(){window.open("https://www.luogu.com.cn/paste/"+PASTEID);};
title.appendChild(but2);
}
addElement();
}
async function 首页暂存内容(){
function inMain(){
let div = document.querySelector(".am-u-lg-3 > div:nth-child(3)");
let inn = document.createElement("div");
inn.innerHTML = "<h2>暂存内容</h2>";
inn.style.marginTop = "40px";
div.appendChild(inn);
let but1=document.createElement("button");
but1.innerText="编辑内容";
but1.onclick = function(){window.open(GM_getValue("pasteid"));};
let but2=document.createElement("button");
but2.innerText="删除内容";
but2.onclick = function(){GM_deleteValue("html");alert("删除成功");};
inn.appendChild(but1);
inn.appendChild(but2);
let tmp = document.createElement("div");
tmp.innerHTML = GM_getValue("html");
tmp.style.marginTop = "20px";
if(tmp.innerHTML === undefined){
tmp.innerHTML = "";
}
inn.appendChild(tmp);
}
//paste
function inPaste(){
let div = document.querySelector(".actions");
let button = div.childNodes[2].cloneNode(true);
button.innerText="保存到首页";
div.appendChild(div.childNodes[1].cloneNode(true));
div.appendChild(button);
button.onclick=function(){
GM_setValue("pasteid",window.location.href);
GM_setValue("html",document.querySelector(".marked").innerHTML);
alert("保存成功");
};
}
(function() {
'use strict';
if(window.location.href.split('/')[3] === "paste"){
setTimeout(inPaste,10);
}
else{
setTimeout(inMain,10);
}
})();
}
async function 卷题情况(){
var has_built = false;
function addtrainingproblem(proName) {
return fetch(`https://www.luogu.com.cn/api/training/editProblems/${TRAINING_ID}`, {
"headers": {
"X-Requested-With": "XMLHttpRequest",
"X-CSRF-TOKEN": document.querySelector("meta[name=csrf-token]").content,
"Content-Type": "application/json",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin"
},
"body": "{\"pids\":[\"" + proName + "\", \"P1001\"]}",
"method": "POST",
});
}
function buildstatisticsbutton() {
if (has_built) return;
let tmp = document.querySelector(".operation > span:nth-child(3)");
let tmp2 = tmp.cloneNode(true);
tmp2.childNodes[0].childNodes[0].innerText = "卷题情况";
tmp2.onclick = async function() {
await addtrainingproblem(window.location.href.split('/')[4]);
window.open(`https://www.luogu.com.cn/training/${TRAINING_ID}#rank`);
};
tmp.parentNode.appendChild(tmp2);
has_built = true;
}
window.addEventListener('load', buildstatisticsbutton);
setTimeout(buildstatisticsbutton, 500);
}
async function 测试用例(){
var has_built = false;
async function _getLojProblem(keyword){
return await fetch("https://api.loj.ac/api/problem/queryProblemSet", {
"credentials": "include",
"headers": {
"Content-Type": "application/json"
},
"body": JSON.stringify({
"keyword":keyword,"locale":"zh_CN","takeCount":1,
"skipCount":0,"keywordMatchesId":true,"titleOnly":true
}),
"method": "POST",
}).then((x)=>(x.json())).then(function(x){return x.result[0].meta.id;});
}
async function getLojProblem(keyword){
let value;
try{
value = await _getLojProblem(keyword);
}
catch(e){
console.log("error:",e);
value = await _getLojProblem(keyword.split('」')[1]);
}
return value;
}
async function getLojSubmission(problem){
return await fetch("https://api.loj.ac/api/submission/querySubmission", {
"headers": {
"Content-Type": "application/json"
},
"body": JSON.stringify({"problemDisplayId":problem,"locale":"zh_CN","takeCount":1,"status":"Accepted"}),
"method": "POST"
}).then((x)=>(x.json())).then((x)=>x.submissions[0].id);
}
function inLuogu(){
if(has_built || document.querySelector(".tags-wrap").innerText.search("各省省选") === -1){
return;
}
let tmp = document.querySelector(".operation > span:nth-child(3)");
let tmp2 = tmp.cloneNode(true);
tmp2.childNodes[0].childNodes[0].innerText = "测试用例";
tmp2.onclick = function() {
const p = document.querySelector(".lfe-h1 > span:nth-child(1)").title.split('[')[1].split(']');
GM_setValue("title",`「${p[0]}」${p[1].trim()}`);
window.open("https://api.loj.ac");
};
tmp.parentNode.appendChild(tmp2);
has_built = true;
}
async function inLoj(){
if(has_built){
return;
}
has_built = true;
try{
const value = await getLojSubmission(await getLojProblem(GM_getValue("title")));
if(isNaN(value)){
throw "Cannot get submission!";
}
window.location.replace("https://loj.ac/s/" + value);
}
catch(err){
alert("error: \n"+err);
}
finally{
GM_deleteValue("title");
}
}
function main(){
window.location.href === "https://api.loj.ac/" ? setTimeout(inLoj,500) : inLuogu();
}
window.addEventListener('load', main);
setTimeout(main, 500);
}
async function 显示今日AC(){
const today = (
(a)=>(new Date(a.getFullYear()+"/"+String(a.getMonth()+1)+"/"+String(a.getDate())))
)(new Date()).getTime()/1000;
async function dfsGetTodayAC(name,page){
let ans = await fetch(`https://www.luogu.com.cn/record/list?user=${name}&status=12&page=${page}`)
.then(x=>x.text())
.then(x=>x.split("JSON.parse(decodeURIComponent(\"")[1])
.then(x=>x.split("\"))")[0])
.then(x=>JSON.parse(decodeURIComponent(x)))
.then(x=>x.currentData.records.result)
.then(x=>x.filter(xx=>xx.submitTime>=today));
if(ans.length === 20){
let res = await dfsGetTodayAC(name,page+1);
res.forEach(x=>ans.push(x));
}
return ans;
}
console.log(today);
let cards = [];
while(document.querySelector(".row-wrap") === null || cards.length !== document.querySelector(".row-wrap").childElementCount){
cards = [];
let app = document.querySelector("#app");
app.childNodes.forEach(function(x){if(x.className === "dropdown") cards.push(x);});
if(!cards.length){
app = document.querySelector("main.wrapped");
app.childNodes.forEach(function(x){if(x.className === "dropdown") cards.push(x);});
console.log("cards",cards);
}
console.log("cards",cards);
await new Promise(resolve=>setTimeout(resolve,500));
}
cards.forEach(async function(x){
let name = document.querySelector(
`div.row:nth-child(${cards.indexOf(x)+1}) > span:nth-child(2) > `+
`span:nth-child(1) > span:nth-child(1) > span:nth-child(1)`
).innerText;
let ans = await dfsGetTodayAC(name,1);
let p = document.createElement("p");
p.innerText = "今日通过的题目:";
console.log(x);
x.children[0].children[1].appendChild(p);
ans = ans.filter((item, index) => {
const duplicateIndex = ans.findIndex(otherItem => otherItem.problem.pid === item.problem.pid);
return duplicateIndex === index;
});
ans.forEach(function(xx){
let a = document.createElement("a");
a.innerText = xx.problem.pid + " " + xx.problem.title;
a.href = "https://www.luogu.com.cn/problem/" + xx.problem.pid;
x.children[0].children[1].appendChild(a);
x.children[0].children[1].appendChild(document.createElement("p"));
})
console.log(name,ans);
});
}
if(URLmatch("https://www.luogu.com.cn/problem/*") || URLmatch("https://www.luogu.com.cn/paste/*") || URLmatch("https://www.luogu.com.cn/training/*")){
try{
浏览记录();
}catch(e){};
}
if(URLmatch("https://www.luogu.com.cn/problem/*") || (URLmatch("https://www.luogu.com.cn/training/*")&&!URLmatch("rank"))){
try{
显示代码长度();
}catch(e){};
}
if(URLmatch("https://www.luogu.com.cn/problem/*")){
try{
简要题面();
}catch(e){console.log(e);};
}
if(window.location.href === "https://www.luogu.com.cn/"||URLmatch("https://www.luogu.com.cn/paste/*")){
try{
首页暂存内容();
}catch(e){console.log(e);};
}
if(URLmatch("https://www.luogu.com.cn/problem/*")||!URLmatch("https://www.luogu.com.cn/problem/list")){
try{
卷题情况();
}catch(e){console.log(e);};
}
if(URLmatch("https://www.luogu.com.cn/problem/*")||URLmatch("https://api.loj.ac/")){
try{
测试用例();
}catch(e){console.log(e);};
}
let id = setInterval(function(){
if(window.location.href.split('/')[3] === "training" && Boolean(window.location.href.split('/')[4].match("#rank"))){
console.log("123");
try{
显示今日AC();
}catch(e){console.log(e);};
clearInterval(id);
}
},1000);
document.querySelector(".operation").children.forEach(x=>x.style.zIndex=9999);