mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
vjudge: draft: luogu
This commit is contained in:
1 parent
2bed36b012
commit
d48adda992
4 files changed
+181
-2
No files matched your search
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@hydrooj/vjudge",
|
"name": "@hydrooj/vjudge",
|
||||||
"version": "1.3.1",
|
"version": "1.3.2",
|
||||||
"description": "Submit problems to remote oj",
|
"description": "Submit problems to remote oj",
|
||||||
"main": "package.json",
|
"main": "package.json",
|
||||||
"repository": "https://github.com/hydro-dev/Hydro.git",
|
"repository": "https://github.com/hydro-dev/Hydro.git",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export interface RemoteAccount {
|
|||||||
password: string;
|
password: string;
|
||||||
endpoint?: string;
|
endpoint?: string;
|
||||||
proxy?: string;
|
proxy?: string;
|
||||||
|
query?: string;
|
||||||
}
|
}
|
||||||
declare module 'hydrooj/src/interface' {
|
declare module 'hydrooj/src/interface' {
|
||||||
interface Collections {
|
interface Collections {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import codeforces from './codeforces';
|
import codeforces from './codeforces';
|
||||||
|
import luogu from './luogu';
|
||||||
import spoj from './spoj';
|
import spoj from './spoj';
|
||||||
|
|
||||||
export = { codeforces, spoj };
|
export = { codeforces, luogu, spoj };
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
/* eslint-disable no-await-in-loop */
|
||||||
|
import { JSDOM } from 'jsdom';
|
||||||
|
import { flattenDeep } from 'lodash';
|
||||||
|
import * as superagent from 'superagent';
|
||||||
|
import proxy from 'superagent-proxy';
|
||||||
|
import { STATUS } from '@hydrooj/utils/lib/status';
|
||||||
|
import { sleep } from '@hydrooj/utils/lib/utils';
|
||||||
|
import { Logger } from 'hydrooj/src/logger';
|
||||||
|
import * as setting from 'hydrooj/src/model/setting';
|
||||||
|
import { IBasicProvider, RemoteAccount } from '../interface';
|
||||||
|
|
||||||
|
proxy(superagent as any);
|
||||||
|
const logger = new Logger('remote/luogu');
|
||||||
|
|
||||||
|
const STATUS_MAP = [
|
||||||
|
STATUS.STATUS_WAITING,
|
||||||
|
STATUS.STATUS_JUDGING,
|
||||||
|
STATUS.STATUS_COMPILE_ERROR,
|
||||||
|
STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED,
|
||||||
|
STATUS.STATUS_MEMORY_LIMIT_EXCEEDED,
|
||||||
|
STATUS.STATUS_TIME_LIMIT_EXCEEDED,
|
||||||
|
STATUS.STATUS_WRONG_ANSWER,
|
||||||
|
STATUS.STATUS_RUNTIME_ERROR,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
STATUS.STATUS_SYSTEM_ERROR,
|
||||||
|
STATUS.STATUS_ACCEPTED,
|
||||||
|
0,
|
||||||
|
STATUS.STATUS_WRONG_ANSWER,
|
||||||
|
];
|
||||||
|
|
||||||
|
const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36';
|
||||||
|
|
||||||
|
export default class LuoguProvider implements IBasicProvider {
|
||||||
|
constructor(public account: RemoteAccount, private save: (data: any) => Promise<void>) {
|
||||||
|
if (account.cookie) this.cookie = account.cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
cookie: string[] = [];
|
||||||
|
csrf: string;
|
||||||
|
|
||||||
|
get(url: string) {
|
||||||
|
logger.debug('get', url);
|
||||||
|
if (!url.includes('//')) url = `${this.account.endpoint || 'https://www.luogu.com.cn'}${url}`;
|
||||||
|
const req = superagent.get(url)
|
||||||
|
.set('Cookie', this.cookie)
|
||||||
|
.set('User-Agent', UA);
|
||||||
|
if (this.account.proxy) return req.proxy(this.account.proxy);
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
post(url: string) {
|
||||||
|
logger.debug('post', url, this.cookie);
|
||||||
|
if (!url.includes('//')) url = `${this.account.endpoint || 'https://www.luogu.com.cn'}${url}`;
|
||||||
|
const req = superagent.post(url)
|
||||||
|
.set('Cookie', this.cookie)
|
||||||
|
.set('x-csrf-token', this.csrf)
|
||||||
|
.set('User-Agent', UA)
|
||||||
|
.set('x-requested-with', 'XMLHttpRequest')
|
||||||
|
.set('origin', 'https://www.luogu.com.cn');
|
||||||
|
if (this.account.proxy) return req.proxy(this.account.proxy);
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCsrfToken(url: string) {
|
||||||
|
const { text: html } = await this.get(url);
|
||||||
|
const $dom = new JSDOM(html);
|
||||||
|
this.csrf = $dom.window.document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
||||||
|
logger.info('csrf-token=', this.csrf);
|
||||||
|
}
|
||||||
|
|
||||||
|
get loggedIn() {
|
||||||
|
return this.get('/user/setting?_contentOnly=1').then(({ body }) => body.currentTemplate !== 'AuthLogin');
|
||||||
|
}
|
||||||
|
|
||||||
|
async ensureLogin() {
|
||||||
|
if (await this.loggedIn) {
|
||||||
|
this.getCsrfToken('/');
|
||||||
|
setInterval(() => this.getCsrfToken('/'), 5 * 60 * 1000);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
logger.info('retry login');
|
||||||
|
// TODO login;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getProblem(id: string) {
|
||||||
|
logger.info(id);
|
||||||
|
// TODO
|
||||||
|
return {
|
||||||
|
title: id,
|
||||||
|
data: {},
|
||||||
|
files: {},
|
||||||
|
tag: [],
|
||||||
|
content: '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async listProblem() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async submitProblem(id: string, lang: string, code: string, info) {
|
||||||
|
const comment = setting.langs[lang]?.comment;
|
||||||
|
if (comment) {
|
||||||
|
const msg = `Hydro submission #${info.rid}@${new Date().toLocaleString()}`;
|
||||||
|
if (typeof comment === 'string') code = `${comment} ${msg}\n${code}`;
|
||||||
|
else if (comment instanceof Array) code = `${comment[0]} ${msg} ${comment[1]}\n${code}`;
|
||||||
|
}
|
||||||
|
lang = lang.includes('luogu.') ? lang.split('luogu.')[1] : '0';
|
||||||
|
const result = await this.post(`/fe/api/problem/submit/${id}${this.account.query || ''}`)
|
||||||
|
.set('referer', `https://www.luogu.com.cn/problem/${id}`)
|
||||||
|
// TODO O2
|
||||||
|
.send({ code, lang: +lang, enableO2: 0 });
|
||||||
|
logger.info('RecordID:', result.body.rid);
|
||||||
|
return result.body.rid;
|
||||||
|
}
|
||||||
|
|
||||||
|
async waitForSubmission(id: string, next, end) {
|
||||||
|
const done = {};
|
||||||
|
let fail = 0;
|
||||||
|
let count = 0;
|
||||||
|
while (count < 60 && fail < 5) {
|
||||||
|
await sleep(1500);
|
||||||
|
count++;
|
||||||
|
try {
|
||||||
|
const { body } = await this.get(`/record/${id}?_contentOnly=1`);
|
||||||
|
const data = body.currentData.record;
|
||||||
|
if (data.detail.compileResult && data.detail.compileResult.success === false) {
|
||||||
|
await next({ compilerText: data.detail.compileResult.message });
|
||||||
|
return await end({
|
||||||
|
status: STATUS.STATUS_COMPILE_ERROR, score: 0, time: 0, memory: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
logger.info('Fetched with length', JSON.stringify(body).length);
|
||||||
|
const total = flattenDeep(body.currentData.testCaseGroup).length;
|
||||||
|
// TODO sorted
|
||||||
|
for (const subtask of data.detail?.judgeResult?.subtasks || []) {
|
||||||
|
for (const cid of Object.keys(subtask.testCases)) {
|
||||||
|
if (done[`${subtask.id}.${cid}`]) continue;
|
||||||
|
done[`${subtask.id}.${cid}`] = true;
|
||||||
|
const testcase = subtask.testCases[cid];
|
||||||
|
await next({
|
||||||
|
status: STATUS.STATUS_JUDGING,
|
||||||
|
case: {
|
||||||
|
status: STATUS_MAP[testcase.status],
|
||||||
|
time: testcase.time,
|
||||||
|
memory: testcase.memory,
|
||||||
|
message: testcase.description,
|
||||||
|
},
|
||||||
|
progress: (Object.keys(done).length / total) * 100,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.status < 2) continue;
|
||||||
|
logger.info('RecordID:', id, 'done');
|
||||||
|
// TODO calc total status
|
||||||
|
return await end({
|
||||||
|
status: STATUS_MAP[data.status],
|
||||||
|
score: STATUS_MAP[data.status] === STATUS.STATUS_ACCEPTED ? 100 : 0,
|
||||||
|
time: data.time,
|
||||||
|
memory: data.memory,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
logger.error(e);
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return await end({
|
||||||
|
status: STATUS.STATUS_SYSTEM_ERROR,
|
||||||
|
score: 0,
|
||||||
|
time: 0,
|
||||||
|
memory: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user