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",
|
||||
"version": "1.3.1",
|
||||
"version": "1.3.2",
|
||||
"description": "Submit problems to remote oj",
|
||||
"main": "package.json",
|
||||
"repository": "https://github.com/hydro-dev/Hydro.git",
|
||||
|
||||
@@ -6,6 +6,7 @@ export interface RemoteAccount {
|
||||
password: string;
|
||||
endpoint?: string;
|
||||
proxy?: string;
|
||||
query?: string;
|
||||
}
|
||||
declare module 'hydrooj/src/interface' {
|
||||
interface Collections {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import codeforces from './codeforces';
|
||||
import luogu from './luogu';
|
||||
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