mirror of
https://github.com/renbaoshuo/S2OJ.git
synced 2024-11-22 13:28:41 +00:00
feat(remote_judger): add loj
This commit is contained in:
parent
246c7fb615
commit
459956049f
308
remote_judger/src/providers/loj.ts
Normal file
308
remote_judger/src/providers/loj.ts
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
import superagent from 'superagent';
|
||||||
|
import proxy from 'superagent-proxy';
|
||||||
|
import sleep from '../utils/sleep';
|
||||||
|
import { IBasicProvider, RemoteAccount, USER_AGENT } from '../interface';
|
||||||
|
import Logger from '../utils/logger';
|
||||||
|
|
||||||
|
proxy(superagent);
|
||||||
|
const logger = new Logger('remote/loj');
|
||||||
|
|
||||||
|
const langs_map = {
|
||||||
|
C: {
|
||||||
|
name: 'C (gcc, c11, O2, m64)',
|
||||||
|
info: {
|
||||||
|
language: 'c',
|
||||||
|
compileAndRunOptions: {
|
||||||
|
compiler: 'gcc',
|
||||||
|
O: '2',
|
||||||
|
m: '64',
|
||||||
|
std: 'c11',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
comment: '//',
|
||||||
|
},
|
||||||
|
'C++03': {
|
||||||
|
name: 'C++ (g++, c++03, O2, m64)',
|
||||||
|
info: {
|
||||||
|
language: 'cpp',
|
||||||
|
compileAndRunOptions: {
|
||||||
|
compiler: 'g++',
|
||||||
|
std: 'c++03',
|
||||||
|
O: '2',
|
||||||
|
m: '64',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
comment: '//',
|
||||||
|
},
|
||||||
|
'C++11': {
|
||||||
|
name: 'C++ (g++, c++11, O2, m64)',
|
||||||
|
info: {
|
||||||
|
language: 'cpp',
|
||||||
|
compileAndRunOptions: {
|
||||||
|
compiler: 'g++',
|
||||||
|
std: 'c++11',
|
||||||
|
O: '2',
|
||||||
|
m: '64',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
comment: '//',
|
||||||
|
},
|
||||||
|
'C++': {
|
||||||
|
name: 'C++ (g++, c++14, O2, m64)',
|
||||||
|
info: {
|
||||||
|
language: 'cpp',
|
||||||
|
compileAndRunOptions: {
|
||||||
|
compiler: 'g++',
|
||||||
|
std: 'c++14',
|
||||||
|
O: '2',
|
||||||
|
m: '64',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
comment: '//',
|
||||||
|
},
|
||||||
|
'C++17': {
|
||||||
|
name: 'C++ (g++, c++17, O2, m64)',
|
||||||
|
info: {
|
||||||
|
language: 'cpp',
|
||||||
|
compileAndRunOptions: {
|
||||||
|
compiler: 'g++',
|
||||||
|
std: 'c++17',
|
||||||
|
O: '2',
|
||||||
|
m: '64',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
comment: '//',
|
||||||
|
},
|
||||||
|
'C++20': {
|
||||||
|
name: 'C++ (g++, c++20, O2, m64)',
|
||||||
|
info: {
|
||||||
|
language: 'cpp',
|
||||||
|
compileAndRunOptions: {
|
||||||
|
compiler: 'g++',
|
||||||
|
std: 'c++20',
|
||||||
|
O: '2',
|
||||||
|
m: '64',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
comment: '//',
|
||||||
|
},
|
||||||
|
'Python2.7': {
|
||||||
|
name: 'Python (2.7)',
|
||||||
|
info: {
|
||||||
|
language: 'python',
|
||||||
|
compileAndRunOptions: {
|
||||||
|
version: '2.7',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
comment: '#',
|
||||||
|
},
|
||||||
|
Python3: {
|
||||||
|
name: 'Python (3.10)',
|
||||||
|
info: {
|
||||||
|
language: 'python',
|
||||||
|
compileAndRunOptions: {
|
||||||
|
version: '3.10',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
comment: '#',
|
||||||
|
},
|
||||||
|
Java17: {
|
||||||
|
name: 'Java',
|
||||||
|
info: {
|
||||||
|
language: 'java',
|
||||||
|
compileAndRunOptions: {},
|
||||||
|
},
|
||||||
|
comment: '//',
|
||||||
|
},
|
||||||
|
Pascal: {
|
||||||
|
name: 'Pascal',
|
||||||
|
info: {
|
||||||
|
language: 'pascal',
|
||||||
|
compileAndRunOptions: {
|
||||||
|
O: '2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
comment: '//',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getAccountInfoFromEnv(): RemoteAccount | null {
|
||||||
|
const {
|
||||||
|
LOJ_HANDLE,
|
||||||
|
LOJ_TOKEN,
|
||||||
|
LOJ_ENDPOINT = 'https://api.loj.ac.cn/api',
|
||||||
|
LOJ_PROXY,
|
||||||
|
} = process.env;
|
||||||
|
|
||||||
|
if (!LOJ_TOKEN) return null;
|
||||||
|
|
||||||
|
const account: RemoteAccount = {
|
||||||
|
type: 'loj',
|
||||||
|
handle: LOJ_HANDLE,
|
||||||
|
password: LOJ_TOKEN,
|
||||||
|
endpoint: LOJ_ENDPOINT,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (LOJ_PROXY) account.proxy = LOJ_PROXY;
|
||||||
|
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class LibreojProvider implements IBasicProvider {
|
||||||
|
constructor(public account: RemoteAccount) {
|
||||||
|
this.account.endpoint ||= 'https://api.loj.ac.cn/api';
|
||||||
|
}
|
||||||
|
|
||||||
|
get(url: string) {
|
||||||
|
logger.debug('get', url);
|
||||||
|
if (!url.includes('//')) url = `${this.account.endpoint}${url}`;
|
||||||
|
const req = superagent
|
||||||
|
.get(url)
|
||||||
|
.auth(this.account.password, { type: 'bearer' })
|
||||||
|
.set('User-Agent', USER_AGENT);
|
||||||
|
if (this.account.proxy) return req.proxy(this.account.proxy);
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
post(url: string) {
|
||||||
|
logger.debug('post', url);
|
||||||
|
if (!url.includes('//')) url = `${this.account.endpoint}${url}`;
|
||||||
|
const req = superagent
|
||||||
|
.post(url)
|
||||||
|
.type('json')
|
||||||
|
.auth(this.account.password, { type: 'bearer' })
|
||||||
|
.set('User-Agent', USER_AGENT)
|
||||||
|
.set('x-recaptcha-token', 'skip');
|
||||||
|
if (this.account.proxy) return req.proxy(this.account.proxy);
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
get loggedIn() {
|
||||||
|
return this.get('/auth/getSessionInfo?token=' + this.account.password).then(
|
||||||
|
res =>
|
||||||
|
res.body.userMeta && res.body.userMeta.username === this.account.handle
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async ensureLogin() {
|
||||||
|
if (await this.loggedIn) return true;
|
||||||
|
logger.info('retry login');
|
||||||
|
// TODO: login
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getProblemId(displayId: number) {
|
||||||
|
const { body } = await this.post('/problem/getProblem').send({ displayId });
|
||||||
|
|
||||||
|
return body.meta.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
async submitProblem(
|
||||||
|
displayId: string,
|
||||||
|
lang: string,
|
||||||
|
code: string,
|
||||||
|
submissionId: number,
|
||||||
|
next,
|
||||||
|
end
|
||||||
|
) {
|
||||||
|
const programType = langs_map[lang] || langs_map['C++'];
|
||||||
|
const comment = programType.comment;
|
||||||
|
|
||||||
|
if (comment) {
|
||||||
|
const msg = `S2OJ Submission #${submissionId} @ ${new Date().getTime()}`;
|
||||||
|
if (typeof comment === 'string') code = `${comment} ${msg}\n${code}`;
|
||||||
|
else if (comment instanceof Array)
|
||||||
|
code = `${comment[0]} ${msg} ${comment[1]}\n${code}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = await this.getProblemId(parseInt(displayId));
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
'Submitting',
|
||||||
|
id,
|
||||||
|
`(displayId: ${displayId})`,
|
||||||
|
programType,
|
||||||
|
lang,
|
||||||
|
`(S2OJ Submission #${submissionId})`
|
||||||
|
);
|
||||||
|
|
||||||
|
const { body, error } = await this.post('/submission/submit').send({
|
||||||
|
problemId: id,
|
||||||
|
content: {
|
||||||
|
code,
|
||||||
|
...programType.info,
|
||||||
|
},
|
||||||
|
uploadInfo: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
await end({
|
||||||
|
error: true,
|
||||||
|
status: 'Judgment Failed',
|
||||||
|
message: 'Failed to submit code.',
|
||||||
|
});
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return body.submissionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
async waitForSubmission(problem_id: string, id: string, next, end) {
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (++i > 60) {
|
||||||
|
return await end({
|
||||||
|
id,
|
||||||
|
error: true,
|
||||||
|
status: 'Judgment Failed',
|
||||||
|
message: 'Failed to fetch submission details.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await sleep(2000);
|
||||||
|
const { body, error } = await this.post('/submission/getSubmissionDetail')
|
||||||
|
.send({ submissionId: String(id), locale: 'zh_CN' })
|
||||||
|
.retry(3);
|
||||||
|
|
||||||
|
if (error) continue;
|
||||||
|
|
||||||
|
if (body.progress.progressType !== 'Finished') {
|
||||||
|
await next({
|
||||||
|
status: `${body.progress.progressType}: ${body.progress.status}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body.meta.status === 'CompilationError') {
|
||||||
|
await end({
|
||||||
|
error: true,
|
||||||
|
id,
|
||||||
|
status: 'Compile Error',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
['SystemError', 'JudgementFailed', 'ConfigurationError'].includes(
|
||||||
|
body.meta.status
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
await end({
|
||||||
|
error: true,
|
||||||
|
id,
|
||||||
|
status: 'Judgment Failed',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return await end({
|
||||||
|
id,
|
||||||
|
status: body.meta.status,
|
||||||
|
score: body.meta.score,
|
||||||
|
time: body.meta.timeUsed,
|
||||||
|
memory: body.meta.memoryUsed,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -154,6 +154,7 @@ export async function apply(request: any) {
|
|||||||
await vjudge.addProvider('codeforces');
|
await vjudge.addProvider('codeforces');
|
||||||
await vjudge.addProvider('atcoder');
|
await vjudge.addProvider('atcoder');
|
||||||
await vjudge.addProvider('uoj');
|
await vjudge.addProvider('uoj');
|
||||||
|
await vjudge.addProvider('loj');
|
||||||
|
|
||||||
return vjudge;
|
return vjudge;
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ class UOJRemoteProblem {
|
|||||||
'name' => 'LibreOJ',
|
'name' => 'LibreOJ',
|
||||||
'short_name' => 'LOJ',
|
'short_name' => 'LOJ',
|
||||||
'url' => 'https://loj.ac',
|
'url' => 'https://loj.ac',
|
||||||
'languages' => ['C', 'C++03', 'C++11', 'C++', 'C++17', 'C++20', 'Python3', 'Python2.7', 'Java8', 'Java11', 'Java17', 'Pascal'],
|
'languages' => ['C', 'C++03', 'C++11', 'C++', 'C++17', 'C++20', 'Python3', 'Python2.7', 'Java17', 'Pascal'],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user