S2OJ/remote_judger/src/vjudge.ts

161 lines
3.9 KiB
TypeScript
Raw Normal View History

2023-01-20 08:35:02 +00:00
import type { BasicProvider, IBasicProvider, RemoteAccount } from './interface';
import * as Time from './utils/time';
import Logger from './utils/logger';
import htmlspecialchars from './utils/htmlspecialchars';
const logger = new Logger('vjudge');
class AccountService {
api: IBasicProvider;
constructor(
public Provider: BasicProvider,
public account: RemoteAccount,
private request: any
) {
this.api = new Provider(account);
this.main().catch(e =>
logger.error(`Error occured in ${account.type}/${account.handle}`, e)
);
}
async judge(
id: number,
problem_id: string,
language: string,
code: string,
judge_time: string
) {
const next = async payload => {
return await this.request('/submit', {
'update-status': true,
fetch_new: false,
id,
2023-01-22 09:36:18 +00:00
status:
payload.status ||
(payload.test_id ? `Judging Test #${payload.test_id}` : 'Judging'),
2023-01-20 08:35:02 +00:00
});
};
const end = async payload => {
if (payload.error) {
return await this.request('/submit', {
submit: true,
fetch_new: false,
id,
result: JSON.stringify({
status: 'Judged',
score: 0,
error: payload.status,
details:
'<div>' +
`<info-block>ID = ${payload.id || 'None'}</info-block>` +
`<error>${htmlspecialchars(payload.message)}</error>` +
'</div>',
}),
judge_time,
});
}
return await this.request('/submit', {
submit: true,
fetch_new: false,
id,
result: JSON.stringify({
status: 'Judged',
score: payload.score,
time: payload.time,
memory: payload.memory,
details:
'<div>' +
`<info-block>ID = ${payload.id || 'None'}</info-block>` +
`<info-block>VERDICT = ${payload.status}</info-block>` +
'</div>',
}),
judge_time,
});
};
try {
const rid = await this.api.submitProblem(
problem_id,
language,
code,
id,
next,
end
);
if (!rid) return;
2023-01-22 08:19:17 +00:00
await this.api.waitForSubmission(problem_id, rid, next, end);
2023-01-20 08:35:02 +00:00
} catch (e) {
logger.error(e);
await end({ error: true, message: e.message });
}
}
async login() {
const login = await this.api.ensureLogin();
if (login === true) {
logger.info(`${this.account.type}/${this.account.handle}: logged in`);
return true;
}
logger.warn(
`${this.account.type}/${this.account.handle}: login fail`,
login || ''
);
return false;
}
async main() {
const res = await this.login();
if (!res) return;
setInterval(() => this.login(), Time.hour);
}
}
class VJudge {
private providers: Record<string, AccountService> = {};
constructor(private request: any) {}
async addProvider(type: string) {
if (this.providers[type]) throw new Error(`duplicate provider ${type}`);
const provider = await import(`./providers/${type}`);
const account = provider.getAccountInfoFromEnv();
if (!account) throw new Error(`no account info for ${type}`);
this.providers[type] = new AccountService(
provider.default,
account,
this.request
);
}
async judge(
id: number,
type: string,
problem_id: string,
language: string,
code: string,
judge_time: string
) {
if (!this.providers[type]) throw new Error(`no provider ${type}`);
this.providers[type].judge(id, problem_id, language, code, judge_time);
}
}
export async function apply(request: any) {
const vjudge = new VJudge(request);
await vjudge.addProvider('codeforces');
2023-01-22 08:19:17 +00:00
await vjudge.addProvider('atcoder');
2023-01-22 09:36:18 +00:00
await vjudge.addProvider('uoj');
2023-01-24 08:45:22 +00:00
await vjudge.addProvider('loj');
2023-01-20 08:35:02 +00:00
return vjudge;
}