mirror of
https://github.com/renbaoshuo/S2OJ.git
synced 2024-11-08 13:38:41 +00:00
feat(remote_judger/luogu): luogu open api (#48)
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
commit
95733e3905
11
remote_judger/package-lock.json
generated
11
remote_judger/package-lock.json
generated
@ -12,7 +12,6 @@
|
|||||||
"crlf-normalize": "^1.0.18",
|
"crlf-normalize": "^1.0.18",
|
||||||
"fs-extra": "^11.1.0",
|
"fs-extra": "^11.1.0",
|
||||||
"jsdom": "^21.0.0",
|
"jsdom": "^21.0.0",
|
||||||
"lodash.flattendeep": "^4.4.0",
|
|
||||||
"math-sum": "^2.0.0",
|
"math-sum": "^2.0.0",
|
||||||
"reggol": "^1.3.4",
|
"reggol": "^1.3.4",
|
||||||
"string-strip-html": "^13.1.0",
|
"string-strip-html": "^13.1.0",
|
||||||
@ -826,11 +825,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
||||||
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="
|
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="
|
||||||
},
|
},
|
||||||
"node_modules/lodash.flattendeep": {
|
|
||||||
"version": "4.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
|
|
||||||
"integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ=="
|
|
||||||
},
|
|
||||||
"node_modules/lodash.trim": {
|
"node_modules/lodash.trim": {
|
||||||
"version": "4.5.1",
|
"version": "4.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.trim/-/lodash.trim-4.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.trim/-/lodash.trim-4.5.1.tgz",
|
||||||
@ -2288,11 +2282,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
||||||
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="
|
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="
|
||||||
},
|
},
|
||||||
"lodash.flattendeep": {
|
|
||||||
"version": "4.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
|
|
||||||
"integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ=="
|
|
||||||
},
|
|
||||||
"lodash.trim": {
|
"lodash.trim": {
|
||||||
"version": "4.5.1",
|
"version": "4.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.trim/-/lodash.trim-4.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.trim/-/lodash.trim-4.5.1.tgz",
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
"crlf-normalize": "^1.0.18",
|
"crlf-normalize": "^1.0.18",
|
||||||
"fs-extra": "^11.1.0",
|
"fs-extra": "^11.1.0",
|
||||||
"jsdom": "^21.0.0",
|
"jsdom": "^21.0.0",
|
||||||
"lodash.flattendeep": "^4.4.0",
|
|
||||||
"math-sum": "^2.0.0",
|
"math-sum": "^2.0.0",
|
||||||
"reggol": "^1.3.4",
|
"reggol": "^1.3.4",
|
||||||
"string-strip-html": "^13.1.0",
|
"string-strip-html": "^13.1.0",
|
||||||
|
@ -4,7 +4,6 @@ import proxy from 'superagent-proxy';
|
|||||||
import Logger from '../utils/logger';
|
import Logger from '../utils/logger';
|
||||||
import { IBasicProvider, RemoteAccount, USER_AGENT } from '../interface';
|
import { IBasicProvider, RemoteAccount, USER_AGENT } from '../interface';
|
||||||
import sleep from '../utils/sleep';
|
import sleep from '../utils/sleep';
|
||||||
import flattenDeep from 'lodash.flattendeep';
|
|
||||||
import htmlspecialchars from '../utils/htmlspecialchars';
|
import htmlspecialchars from '../utils/htmlspecialchars';
|
||||||
|
|
||||||
proxy(superagent);
|
proxy(superagent);
|
||||||
@ -76,10 +75,58 @@ const LANGS_MAP = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const API_LANGS_MAP = {
|
||||||
|
C: {
|
||||||
|
id: 'c/99/gcc',
|
||||||
|
name: 'C',
|
||||||
|
comment: '//',
|
||||||
|
},
|
||||||
|
'C++98': {
|
||||||
|
id: 'cxx/98/gcc',
|
||||||
|
name: 'C++98',
|
||||||
|
comment: '//',
|
||||||
|
},
|
||||||
|
'C++11': {
|
||||||
|
id: 'cxx/11/gcc',
|
||||||
|
name: 'C++11',
|
||||||
|
comment: '//',
|
||||||
|
},
|
||||||
|
'C++': {
|
||||||
|
id: 'cxx/14/gcc',
|
||||||
|
name: 'C++14',
|
||||||
|
comment: '//',
|
||||||
|
},
|
||||||
|
'C++17': {
|
||||||
|
id: 'cxx/17/gcc',
|
||||||
|
name: 'C++17',
|
||||||
|
comment: '//',
|
||||||
|
},
|
||||||
|
'C++20': {
|
||||||
|
id: 'cxx/20/gcc',
|
||||||
|
name: 'C++20',
|
||||||
|
comment: '//',
|
||||||
|
},
|
||||||
|
Python3: {
|
||||||
|
id: 'python3/c',
|
||||||
|
name: 'Python 3',
|
||||||
|
comment: '#',
|
||||||
|
},
|
||||||
|
Java8: {
|
||||||
|
id: 'java/8',
|
||||||
|
name: 'Java 8',
|
||||||
|
comment: '//',
|
||||||
|
},
|
||||||
|
Pascal: {
|
||||||
|
id: 'pascal/fpc',
|
||||||
|
name: 'Pascal',
|
||||||
|
comment: '//',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
function buildLuoguTestCaseInfoBlock(test) {
|
function buildLuoguTestCaseInfoBlock(test) {
|
||||||
let res = '';
|
let res = '';
|
||||||
|
|
||||||
res += `<test num="${test.id + 1}" info="${STATUS_MAP[test.status]}" time="${
|
res += `<test num="${test.id}" info="${STATUS_MAP[test.status]}" time="${
|
||||||
test.time || -1
|
test.time || -1
|
||||||
}" memory="${test.memory || -1}" score="${test.score || ''}">`;
|
}" memory="${test.memory || -1}" score="${test.score || ''}">`;
|
||||||
res += `<res>${htmlspecialchars(test.description || '')}</res>`;
|
res += `<res>${htmlspecialchars(test.description || '')}</res>`;
|
||||||
@ -88,6 +135,28 @@ function buildLuoguTestCaseInfoBlock(test) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getAccountInfoFromEnv(): RemoteAccount | null {
|
||||||
|
const {
|
||||||
|
LUOGU_HANDLE,
|
||||||
|
LUOGU_PASSWORD,
|
||||||
|
LUOGU_ENDPOINT = 'https://open-v1.lgapi.cn',
|
||||||
|
LUOGU_PROXY,
|
||||||
|
} = process.env;
|
||||||
|
|
||||||
|
if (!LUOGU_HANDLE || !LUOGU_PASSWORD) return null;
|
||||||
|
|
||||||
|
const account: RemoteAccount = {
|
||||||
|
type: 'luogu-api',
|
||||||
|
handle: LUOGU_HANDLE,
|
||||||
|
password: LUOGU_PASSWORD,
|
||||||
|
endpoint: LUOGU_ENDPOINT,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (LUOGU_PROXY) account.proxy = LUOGU_PROXY;
|
||||||
|
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
export default class LuoguProvider implements IBasicProvider {
|
export default class LuoguProvider implements IBasicProvider {
|
||||||
constructor(public account: RemoteAccount) {
|
constructor(public account: RemoteAccount) {
|
||||||
if (account.cookie) this.cookie = account.cookie;
|
if (account.cookie) this.cookie = account.cookie;
|
||||||
@ -101,19 +170,23 @@ export default class LuoguProvider implements IBasicProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cookie: string[] = [];
|
cookie: string[] = [];
|
||||||
csrf: string;
|
csrf: string = null;
|
||||||
|
|
||||||
get(url: string) {
|
get(url: string) {
|
||||||
logger.debug('get', url, this.cookie);
|
|
||||||
|
|
||||||
if (!url.includes('//'))
|
if (!url.includes('//'))
|
||||||
url = `${this.account.endpoint || 'https://www.luogu.com.cn'}${url}`;
|
url = `${this.account.endpoint || 'https://www.luogu.com.cn'}${url}`;
|
||||||
|
|
||||||
|
logger.debug('get', url, this.cookie);
|
||||||
|
|
||||||
const req = superagent
|
const req = superagent
|
||||||
.get(url)
|
.get(url)
|
||||||
.set('Cookie', this.cookie)
|
.set('Cookie', this.cookie)
|
||||||
.set('User-Agent', USER_AGENT);
|
.set('User-Agent', USER_AGENT);
|
||||||
|
|
||||||
|
if (this.account.type == 'luogu-api') {
|
||||||
|
req.auth(this.account.handle, this.account.password);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.account.proxy) return req.proxy(this.account.proxy);
|
if (this.account.proxy) return req.proxy(this.account.proxy);
|
||||||
|
|
||||||
return req;
|
return req;
|
||||||
@ -137,11 +210,11 @@ export default class LuoguProvider implements IBasicProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
post(url: string) {
|
post(url: string) {
|
||||||
logger.debug('post', url, this.cookie);
|
|
||||||
|
|
||||||
if (!url.includes('//'))
|
if (!url.includes('//'))
|
||||||
url = `${this.account.endpoint || 'https://www.luogu.com.cn'}${url}`;
|
url = `${this.account.endpoint || 'https://www.luogu.com.cn'}${url}`;
|
||||||
|
|
||||||
|
logger.debug('post', url, this.cookie);
|
||||||
|
|
||||||
const req = superagent
|
const req = superagent
|
||||||
.post(url)
|
.post(url)
|
||||||
.set('Cookie', this.cookie)
|
.set('Cookie', this.cookie)
|
||||||
@ -150,6 +223,10 @@ export default class LuoguProvider implements IBasicProvider {
|
|||||||
.set('x-requested-with', 'XMLHttpRequest')
|
.set('x-requested-with', 'XMLHttpRequest')
|
||||||
.set('origin', 'https://www.luogu.com.cn');
|
.set('origin', 'https://www.luogu.com.cn');
|
||||||
|
|
||||||
|
if (this.account.type == 'luogu-api') {
|
||||||
|
req.auth(this.account.handle, this.account.password);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.account.proxy) return req.proxy(this.account.proxy);
|
if (this.account.proxy) return req.proxy(this.account.proxy);
|
||||||
|
|
||||||
return req;
|
return req;
|
||||||
@ -186,6 +263,8 @@ export default class LuoguProvider implements IBasicProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async ensureLogin() {
|
async ensureLogin() {
|
||||||
|
if (this.account.type == 'luogu-api') return true;
|
||||||
|
|
||||||
if (await this.loggedIn) {
|
if (await this.loggedIn) {
|
||||||
await this.getCsrfToken('/user/setting');
|
await this.getCsrfToken('/user/setting');
|
||||||
|
|
||||||
@ -227,7 +306,10 @@ export default class LuoguProvider implements IBasicProvider {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const programType = LANGS_MAP[lang] || LANGS_MAP['C++'];
|
const programType =
|
||||||
|
this.account.type == 'luogu-api'
|
||||||
|
? API_LANGS_MAP[lang] || API_LANGS_MAP['C++']
|
||||||
|
: LANGS_MAP[lang] || LANGS_MAP['C++'];
|
||||||
const comment = programType.comment;
|
const comment = programType.comment;
|
||||||
|
|
||||||
if (comment) {
|
if (comment) {
|
||||||
@ -237,6 +319,28 @@ export default class LuoguProvider implements IBasicProvider {
|
|||||||
code = `${comment[0]} ${msg} ${comment[1]}\n${code}`;
|
code = `${comment[0]} ${msg} ${comment[1]}\n${code}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.account.type == 'luogu-api') {
|
||||||
|
const result = await this.post('/judge/problem').send({
|
||||||
|
pid: id,
|
||||||
|
code,
|
||||||
|
lang: programType.id,
|
||||||
|
o2: 1,
|
||||||
|
trackId: submissionId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.status == 402) {
|
||||||
|
await end({
|
||||||
|
error: true,
|
||||||
|
id,
|
||||||
|
status: 'Judgment Failed',
|
||||||
|
message: 'Payment required.',
|
||||||
|
});
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.body.requestId;
|
||||||
|
} else {
|
||||||
const result = await this.post(`/fe/api/problem/submit/${id}`)
|
const result = await this.post(`/fe/api/problem/submit/${id}`)
|
||||||
.set('referer', `https://www.luogu.com.cn/problem/${id}`)
|
.set('referer', `https://www.luogu.com.cn/problem/${id}`)
|
||||||
.send({
|
.send({
|
||||||
@ -249,8 +353,11 @@ export default class LuoguProvider implements IBasicProvider {
|
|||||||
|
|
||||||
return result.body.rid;
|
return result.body.rid;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async ensureIsOwnSubmission(id: string) {
|
async ensureIsOwnSubmission(id: string) {
|
||||||
|
if (this.account.type == 'luogu-api') return true;
|
||||||
|
|
||||||
const { body } = await this.safeGet(`/record/${id}?_contentOnly=1`);
|
const { body } = await this.safeGet(`/record/${id}?_contentOnly=1`);
|
||||||
|
|
||||||
const current_uid = body.currentUser?.uid;
|
const current_uid = body.currentUser?.uid;
|
||||||
@ -273,24 +380,42 @@ export default class LuoguProvider implements IBasicProvider {
|
|||||||
let fail = 0;
|
let fail = 0;
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
||||||
while (count < 180 && fail < 10) {
|
while (count < 360 && fail < 10) {
|
||||||
await sleep(1000);
|
await sleep(500);
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { body } = await this.safeGet(`/record/${id}?_contentOnly=1`);
|
let result, data, body;
|
||||||
const data = body.currentData.record;
|
|
||||||
|
|
||||||
if (!data) {
|
if (this.account.type == 'luogu-api') {
|
||||||
|
result = await this.get(`/judge/result?id=${id}`);
|
||||||
|
body = result.body;
|
||||||
|
data = body.data;
|
||||||
|
} else {
|
||||||
|
result = await this.safeGet(`/record/${id}?_contentOnly=1`);
|
||||||
|
body = result.body;
|
||||||
|
data = body.currentData.record;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.status == 204) {
|
||||||
|
await next({ status: '[Luogu] Judging' });
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.status == 200 && !data) {
|
||||||
return await end({
|
return await end({
|
||||||
error: true,
|
error: true,
|
||||||
id: `R${id}`,
|
id,
|
||||||
status: 'Judgment Failed',
|
status: 'Judgment Failed',
|
||||||
message: 'Failed to fetch submission details.',
|
message: 'Failed to fetch submission details.',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.problem.pid != problem_id) {
|
if (
|
||||||
|
this.account.type != 'luogu-api' &&
|
||||||
|
data.problem.pid != problem_id
|
||||||
|
) {
|
||||||
return await end({
|
return await end({
|
||||||
id,
|
id,
|
||||||
error: true,
|
error: true,
|
||||||
@ -299,29 +424,55 @@ export default class LuoguProvider implements IBasicProvider {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.account.type == 'luogu-api') {
|
||||||
|
data = {
|
||||||
|
...data,
|
||||||
|
status: data.judge.status,
|
||||||
|
score: data.judge.score,
|
||||||
|
time: data.judge.time,
|
||||||
|
memory: data.judge.memory,
|
||||||
|
detail: {
|
||||||
|
compileResult: data.compile,
|
||||||
|
judgeResult: {
|
||||||
|
...data.judge,
|
||||||
|
subtasks: data.judge.subtasks.map(sub => ({
|
||||||
|
...sub,
|
||||||
|
testCases: sub.cases,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
data.detail.compileResult &&
|
data.detail.compileResult &&
|
||||||
data.detail.compileResult.success === false
|
data.detail.compileResult.success === false
|
||||||
) {
|
) {
|
||||||
return await end({
|
return await end({
|
||||||
error: true,
|
error: true,
|
||||||
id: `R${id}`,
|
id,
|
||||||
status: 'Compile Error',
|
status: 'Compile Error',
|
||||||
message: data.detail.compileResult.message,
|
message: data.detail.compileResult.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info('Fetched with length', JSON.stringify(body).length);
|
|
||||||
const total = flattenDeep(
|
|
||||||
Object.entries(body.currentData.testCaseGroup || {}).map(o => o[1])
|
|
||||||
).length;
|
|
||||||
|
|
||||||
if (!data.detail.judgeResult?.subtasks) continue;
|
if (!data.detail.judgeResult?.subtasks) continue;
|
||||||
|
|
||||||
|
const finishedTestCases = Object.entries(
|
||||||
|
data.detail.judgeResult.subtasks
|
||||||
|
)
|
||||||
|
.map(o => o[1])
|
||||||
|
.reduce(
|
||||||
|
(acc: number, sub: any) =>
|
||||||
|
acc +
|
||||||
|
Object.entries(sub.testCases as any[])
|
||||||
|
.map(o => o[1])
|
||||||
|
.filter(test => test.status >= 2).length,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
await next({
|
await next({
|
||||||
status: `Judging (${
|
status: `[Luogu] Judging (${finishedTestCases} judged)`,
|
||||||
data.detail.judgeResult?.finishedCaseCount || '?'
|
|
||||||
}/${total})`,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (data.status < 2) continue;
|
if (data.status < 2) continue;
|
||||||
@ -331,24 +482,31 @@ export default class LuoguProvider implements IBasicProvider {
|
|||||||
const status = STATUS_MAP[data.status];
|
const status = STATUS_MAP[data.status];
|
||||||
let details = '';
|
let details = '';
|
||||||
|
|
||||||
|
if (this.account.type != 'luogu-api') {
|
||||||
details +=
|
details +=
|
||||||
'<remote-result-container>' +
|
'<remote-result-container>' +
|
||||||
'<remote-result-table>' +
|
'<remote-result-table>' +
|
||||||
Object.entries({
|
Object.entries({
|
||||||
题目: `<a href="https://www.luogu.com.cn/problem/${
|
题目: `<a href="https://www.luogu.com.cn/problem/${
|
||||||
data.problem.pid
|
data.problem.pid
|
||||||
}">${data.problem.pid} ${htmlspecialchars(data.problem.title)}</a>`,
|
}">${data.problem.pid} ${htmlspecialchars(
|
||||||
|
data.problem.title
|
||||||
|
)}</a>`,
|
||||||
提交记录: `<a href="https://www.luogu.com.cn/record/${id}">R${id}</a>`,
|
提交记录: `<a href="https://www.luogu.com.cn/record/${id}">R${id}</a>`,
|
||||||
提交时间: new Date(data.submitTime * 1000).toLocaleString('zh-CN'),
|
提交时间: new Date(data.submitTime * 1000).toLocaleString(
|
||||||
|
'zh-CN'
|
||||||
|
),
|
||||||
账号: `<a href="https://www.luogu.com.cn/user/${data.user.uid}">${data.user.name}</a>`,
|
账号: `<a href="https://www.luogu.com.cn/user/${data.user.uid}">${data.user.name}</a>`,
|
||||||
状态: status,
|
状态: status,
|
||||||
})
|
})
|
||||||
.map(
|
.map(
|
||||||
o => `<remote-result-tr name="${o[0]}">${o[1]}</remote-result-tr>`
|
o =>
|
||||||
|
`<remote-result-tr name="${o[0]}">${o[1]}</remote-result-tr>`
|
||||||
)
|
)
|
||||||
.join('') +
|
.join('') +
|
||||||
'</remote-result-table>' +
|
'</remote-result-table>' +
|
||||||
'</remote-result-container>';
|
'</remote-result-container>';
|
||||||
|
}
|
||||||
|
|
||||||
details += '<tests>';
|
details += '<tests>';
|
||||||
|
|
||||||
@ -357,7 +515,12 @@ export default class LuoguProvider implements IBasicProvider {
|
|||||||
data.detail.judgeResult.subtasks[0].testCases
|
data.detail.judgeResult.subtasks[0].testCases
|
||||||
)
|
)
|
||||||
.map(o => o[1])
|
.map(o => o[1])
|
||||||
.map(buildLuoguTestCaseInfoBlock)
|
.map((testcase: any) =>
|
||||||
|
buildLuoguTestCaseInfoBlock({
|
||||||
|
...testcase,
|
||||||
|
id: testcase.id + (this.account.type != 'luogu-api'),
|
||||||
|
})
|
||||||
|
)
|
||||||
.join('\n');
|
.join('\n');
|
||||||
} else {
|
} else {
|
||||||
details += Object.entries(data.detail.judgeResult.subtasks)
|
details += Object.entries(data.detail.judgeResult.subtasks)
|
||||||
@ -372,7 +535,12 @@ export default class LuoguProvider implements IBasicProvider {
|
|||||||
subtask.testCases
|
subtask.testCases
|
||||||
)
|
)
|
||||||
.map(o => o[1])
|
.map(o => o[1])
|
||||||
.map(buildLuoguTestCaseInfoBlock)
|
.map((testcase: any) =>
|
||||||
|
buildLuoguTestCaseInfoBlock({
|
||||||
|
...testcase,
|
||||||
|
id: testcase.id + (this.account.type != 'luogu-api'),
|
||||||
|
})
|
||||||
|
)
|
||||||
.join('\n')}</subtask>`
|
.join('\n')}</subtask>`
|
||||||
)
|
)
|
||||||
.join('\n');
|
.join('\n');
|
||||||
@ -381,15 +549,15 @@ export default class LuoguProvider implements IBasicProvider {
|
|||||||
details += '</tests>';
|
details += '</tests>';
|
||||||
|
|
||||||
return await end({
|
return await end({
|
||||||
id: `R${id}`,
|
id,
|
||||||
status,
|
status,
|
||||||
score:
|
score: status === 'Accepted' ? 100 : Math.min(97, data.score),
|
||||||
status === 'Accepted'
|
|
||||||
? 100
|
|
||||||
: (data.score / data.problem.fullScore) * 100,
|
|
||||||
time: data.time,
|
time: data.time,
|
||||||
memory: data.memory,
|
memory: data.memory,
|
||||||
details: `<div>${details}</div>`,
|
details:
|
||||||
|
this.account.type != 'luogu-api'
|
||||||
|
? `<div>${details}</div>`
|
||||||
|
: details,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error(e);
|
logger.error(e);
|
||||||
|
@ -241,7 +241,7 @@ export async function apply(request: any) {
|
|||||||
await vjudge.addProvider('atcoder');
|
await vjudge.addProvider('atcoder');
|
||||||
await vjudge.addProvider('uoj');
|
await vjudge.addProvider('uoj');
|
||||||
await vjudge.addProvider('loj');
|
await vjudge.addProvider('loj');
|
||||||
await vjudge.importProvider('luogu');
|
await vjudge.addProvider('luogu');
|
||||||
await vjudge.addProvider('qoj');
|
await vjudge.addProvider('qoj');
|
||||||
|
|
||||||
return vjudge;
|
return vjudge;
|
||||||
|
@ -49,7 +49,7 @@ class UOJRemoteProblem {
|
|||||||
'short_name' => '洛谷',
|
'short_name' => '洛谷',
|
||||||
'url' => 'https://www.luogu.com.cn',
|
'url' => 'https://www.luogu.com.cn',
|
||||||
'languages' => ['C', 'C++98', 'C++11', 'C++', 'C++17', 'C++20', 'Python3', 'Java8', 'Pascal'],
|
'languages' => ['C', 'C++98', 'C++11', 'C++', 'C++17', 'C++20', 'Python3', 'Java8', 'Pascal'],
|
||||||
'submit_type' => ['archive'],
|
'submit_type' => ['bot', 'my', 'archive'],
|
||||||
],
|
],
|
||||||
'qoj' => [
|
'qoj' => [
|
||||||
'name' => 'Qingyu Online Judge',
|
'name' => 'Qingyu Online Judge',
|
||||||
|
Loading…
Reference in New Issue
Block a user