diff --git a/remote_judger/src/daemon.ts b/remote_judger/src/daemon.ts index 0e88968..bfa52c5 100644 --- a/remote_judger/src/daemon.ts +++ b/remote_judger/src/daemon.ts @@ -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: `No details.`, + details: `${htmlspecialchars(err.message)}`, }), judge_time, }); diff --git a/remote_judger/src/providers/atcoder.ts b/remote_judger/src/providers/atcoder.ts index f96bde7..757de3a 100644 --- a/remote_judger/src/providers/atcoder.ts +++ b/remote_judger/src/providers/atcoder.ts @@ -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; diff --git a/remote_judger/src/providers/codeforces.ts b/remote_judger/src/providers/codeforces.ts index 1131c4b..76eb301 100644 --- a/remote_judger/src/providers/codeforces.ts +++ b/remote_judger/src/providers/codeforces.ts @@ -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; diff --git a/remote_judger/src/providers/loj.ts b/remote_judger/src/providers/loj.ts index cd79213..793b319 100644 --- a/remote_judger/src/providers/loj.ts +++ b/remote_judger/src/providers/loj.ts @@ -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}`; diff --git a/remote_judger/src/providers/uoj.ts b/remote_judger/src/providers/uoj.ts index eafe22b..a7ae179 100644 --- a/remote_judger/src/providers/uoj.ts +++ b/remote_judger/src/providers/uoj.ts @@ -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; diff --git a/remote_judger/src/vjudge.ts b/remote_judger/src/vjudge.ts index 20aab01..dda6303 100644 --- a/remote_judger/src/vjudge.ts +++ b/remote_judger/src/vjudge.ts @@ -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 = {}; + private providers: Record = {}; + + 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,75 +147,51 @@ class AccountService { }); }; - try { - const rid = await this.api.submitProblem( + 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, - id, next, end ); + } else if (config.remote_submit_type == 'my') { + if (!this.p_imports[type]) throw new Error(`No provider ${type}`); - if (!rid) return; + try { + const provider = this.p_imports[type].constructFromAccountData( + JSON.parse(config.remote_account_data) + ); - await this.api.waitForSubmission(problem_id, rid, next, end); - } catch (e) { - logger.error(e); - await end({ error: true, status: 'Judgment Failed', message: e.message }); + const rid = await provider.submitProblem( + problem_id, + language, + code, + id, + next, + end + ); + + if (!rid) return; + + await provider.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 || '' + 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 = {}; - - 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); } }