refactor(remote_judger): remote_submit_type

This commit is contained in:
Baoshuo Ren 2023-02-03 09:38:27 +08:00
parent 2ab8d07683
commit 7bc29d60c9
Signed by: baoshuo
GPG Key ID: 00CB9680AB29F51A
6 changed files with 130 additions and 66 deletions

View File

@ -7,6 +7,7 @@ import * as TIME from './utils/time';
import { apply } from './vjudge';
import path from 'path';
import child from 'child_process';
import htmlspecialchars from './utils/htmlspecialchars';
proxy(superagent);
@ -146,7 +147,8 @@ export default async function daemon(config: UOJConfig) {
config.remote_problem_id,
config.answer_language,
code,
judge_time
judge_time,
config
);
} catch (err) {
await request('/submit', {
@ -157,7 +159,7 @@ export default async function daemon(config: UOJConfig) {
status: 'Judged',
score: 0,
error: 'Judgment Failed',
details: `<error>No details.</error>`,
details: `<error>${htmlspecialchars(err.message)}</error>`,
}),
judge_time,
});

View File

@ -73,6 +73,10 @@ export default class AtcoderProvider implements IBasicProvider {
this.account.endpoint ||= 'https://atcoder.jp';
}
static constructFromAccountData(data) {
throw new Error('Method not implemented.');
}
cookie: string[] = ['language=en'];
csrf: string;

View File

@ -87,6 +87,10 @@ export default class CodeforcesProvider implements IBasicProvider {
this.account.endpoint ||= 'https://codeforces.com';
}
static constructFromAccountData(data) {
throw new Error('Method not implemented.');
}
cookie: string[] = [];
csrf: string;

View File

@ -156,6 +156,10 @@ export default class LibreojProvider implements IBasicProvider {
this.account.endpoint ||= 'https://api.loj.ac.cn/api';
}
static constructFromAccountData(data) {
throw new Error('Method not implemented.');
}
get(url: string) {
logger.debug('get', url);
if (!url.includes('//')) url = `${this.account.endpoint}${url}`;

View File

@ -99,6 +99,10 @@ export default class UOJProvider implements IBasicProvider {
if (account.cookie) this.cookie = account.cookie;
}
static constructFromAccountData(data) {
throw new Error('Method not implemented.');
}
cookie: string[] = [];
csrf: string;

View File

@ -8,11 +8,7 @@ const logger = new Logger('vjudge');
class AccountService {
api: IBasicProvider;
constructor(
public Provider: BasicProvider,
public account: RemoteAccount,
private request: any
) {
constructor(public Provider: BasicProvider, public account: RemoteAccount) {
this.api = new Provider(account);
this.main().catch(e =>
logger.error(`Error occured in ${account.type}/${account.handle}`, e)
@ -24,7 +20,81 @@ class AccountService {
problem_id: string,
language: string,
code: string,
judge_time: string
next,
end
) {
try {
const rid = await this.api.submitProblem(
problem_id,
language,
code,
id,
next,
end
);
if (!rid) return;
await this.api.waitForSubmission(problem_id, rid, next, end);
} catch (e) {
logger.error(e);
await end({ error: true, status: 'Judgment Failed', 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 p_imports: Record<string, any> = {};
private providers: Record<string, AccountService> = {};
constructor(private request: any) {}
async importProvider(type: string) {
if (this.p_imports[type]) throw new Error(`duplicate provider ${type}`);
const provider = await import(`./providers/${type}`);
this.p_imports[type] = provider.default;
}
async addProvider(type: string) {
if (this.p_imports[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.p_imports[type] = provider.default;
this.providers[type] = new AccountService(provider.default, account);
}
async judge(
id: number,
type: string,
problem_id: string,
language: string,
code: string,
judge_time: string,
config
) {
const next = async payload => {
return await this.request('/submit', {
@ -77,8 +147,26 @@ class AccountService {
});
};
if (!config.remote_submit_type || config.remote_submit_type == 'bot') {
if (!this.providers[type]) throw new Error(`No provider ${type}`);
await this.providers[type].judge(
id,
problem_id,
language,
code,
next,
end
);
} else if (config.remote_submit_type == 'my') {
if (!this.p_imports[type]) throw new Error(`No provider ${type}`);
try {
const rid = await this.api.submitProblem(
const provider = this.p_imports[type].constructFromAccountData(
JSON.parse(config.remote_account_data)
);
const rid = await provider.submitProblem(
problem_id,
language,
code,
@ -89,63 +177,21 @@ class AccountService {
if (!rid) return;
await this.api.waitForSubmission(problem_id, rid, next, end);
await provider.waitForSubmission(problem_id, rid, next, end);
} catch (e) {
logger.error(e);
await end({ error: true, status: 'Judgment Failed', message: e.message });
await end({
error: true,
status: 'Judgment Failed',
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 || ''
throw new Error(
'Unsupported remote submit type: ' + config.remote_submit_type
);
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}`);
await this.providers[type].judge(id, problem_id, language, code, judge_time);
}
}