vjudge: add YBTBAS support

This commit is contained in:
undefined committed 2023-04-11 14:44:29 +08:00
1 parent c859983e07
commit e76e1c9beb
4 files changed
+100 -12

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@hydrooj/vjudge", "name": "@hydrooj/vjudge",
"version": "1.8.6", "version": "1.8.7",
"description": "Submit problems to remote oj", "description": "Submit problems to remote oj",
"main": "src/model.ts", "main": "src/model.ts",
"repository": "https://github.com/hydro-dev/Hydro.git", "repository": "https://github.com/hydro-dev/Hydro.git",
+4
View File
@@ -53,4 +53,8 @@ export class BasicFetcher {
if (typeof cookie === 'string') this.cookie = [cookie]; if (typeof cookie === 'string') this.cookie = [cookie];
else this.cookie = cookie; else this.cookie = cookie;
} }
setEndpoint(endpoint: string) {
this.defaultEndpoint = endpoint;
}
} }
+83 -10
View File
@@ -19,6 +19,7 @@ interface HustOJRemoteConfig {
usernameField?: string; usernameField?: string;
passwordField?: string; passwordField?: string;
extra?: Record<string, string> | (() => Promise<Record<string, string>>); extra?: Record<string, string> | (() => Promise<Record<string, string>>);
check?: string;
}; };
// NOTE: CAPTCHAS ARE NOT SUPPORTED // NOTE: CAPTCHAS ARE NOT SUPPORTED
submit?: { submit?: {
@@ -29,6 +30,7 @@ interface HustOJRemoteConfig {
extra?: Record<string, string>; extra?: Record<string, string>;
tooFrequent?: string; tooFrequent?: string;
rid?: RegExp; rid?: RegExp;
noRefetch?: boolean;
}; };
monit?: { monit?: {
endpoint?: string; endpoint?: string;
@@ -36,6 +38,7 @@ interface HustOJRemoteConfig {
ceInfo?: { ceInfo?: {
endpoint?: string; endpoint?: string;
matcher?: RegExp; matcher?: RegExp;
selector?: string;
}, },
server?: string; server?: string;
} }
@@ -67,6 +70,7 @@ const defaultConfig: HustOJRemoteConfig = {
usernameField: 'username', usernameField: 'username',
passwordField: 'password', passwordField: 'password',
extra: {}, extra: {},
check: '',
}, },
submit: { submit: {
endpoint: '/submit.php', endpoint: '/submit.php',
@@ -84,7 +88,7 @@ function isProcessing(t: string) {
return [STATUS.STATUS_WAITING, STATUS.STATUS_COMPILING, STATUS.STATUS_JUDGING].includes(VERDICT[t]); return [STATUS.STATUS_WAITING, STATUS.STATUS_COMPILING, STATUS.STATUS_JUDGING].includes(VERDICT[t]);
} }
// TODO // TODO
async function parseCaptcha(image: Buffer) { // eslint-disable-line async function _parseCaptcha(image: Buffer) { // eslint-disable-line
return ''; return '';
} }
@@ -108,6 +112,7 @@ export class HUSTOJ extends BasicFetcher implements IBasicProvider {
}); });
this.config = config; this.config = config;
this.updateConfig(); this.updateConfig();
if (this.config.server) this.setEndpoint(this.config.server);
} }
updateConfig() { } updateConfig() { }
@@ -120,25 +125,40 @@ export class HUSTOJ extends BasicFetcher implements IBasicProvider {
return []; return [];
} }
get loggedIn() {
if (!this.config.login.check) return !!this.cookie?.length;
return this.get('/').then(({ text }) => !!text.includes(this.config.login.check));
}
async login(username: string, password: string) { async login(username: string, password: string) {
this.cookie = []; this.cookie = [];
const c = this.config.login; const c = this.config.login;
const ex = typeof c.extra === 'function' ? await c.extra() : c.extra;
const res = await this.post(c.endpoint).send({ const res = await this.post(c.endpoint).send({
[c.usernameField]: username, [c.usernameField]: username,
[c.passwordField]: password, [c.passwordField]: password,
...(typeof c.extra === 'function' ? await c.extra() : c.extra), ...ex,
}); });
console.log(res.text);
this.state.username = username; this.state.username = username;
this.cookie = res.headers['set-cookie'].join('\n'); if (res.headers['set-cookie']) this.cookie = res.headers['set-cookie'];
this.save({ cookie: this.cookie });
} }
async ensureLogin() { async ensureLogin() {
if (!this.account.handle || !this.account.password) return false; if (!this.account.handle || !this.account.password) return false;
if (await this.loggedIn) return true;
await this.login(this.account.handle, this.account.password); await this.login(this.account.handle, this.account.password);
return true; if (!await this.loggedIn) {
// assume rate limit triggered or incorrect captcha
await this.login(this.account.handle, this.account.password);
}
return this.loggedIn;
} }
async submitProblem(id: string, lang: string, code: string, info, next) { async submitProblem(id: string, lang: string, code: string) {
await this.ensureLogin();
if (id.startsWith('P')) id = id.substring(1);
const res = await this.post(this.config.submit.endpoint).send({ const res = await this.post(this.config.submit.endpoint).send({
[this.config.submit.idField]: id, [this.config.submit.idField]: id,
[this.config.submit.langField]: lang.split('.')[1], [this.config.submit.langField]: lang.split('.')[1],
@@ -147,10 +167,12 @@ export class HUSTOJ extends BasicFetcher implements IBasicProvider {
}); });
// if (res.text.includes(this.config.submit.tooFrequent)) throw new TooFrequentError(); // if (res.text.includes(this.config.submit.tooFrequent)) throw new TooFrequentError();
console.log(res.text); console.log(res.text);
if (this.config.submit.noRefetch) {
return this.config.submit.rid.exec(res.text)[1];
}
const url = this.config.monit.endpoint.replace('{uid}', this.state.username).replace('{pid}', id); const url = this.config.monit.endpoint.replace('{uid}', this.state.username).replace('{pid}', id);
this.state.pid = id; this.state.pid = id;
const r = await this.get(url); const r = await this.get(url);
next({ message: `ID: ${this.config.submit.rid.exec(r.text)[1]}` });
return this.config.submit.rid.exec(r.text)[1]; return this.config.submit.rid.exec(r.text)[1];
} }
@@ -240,9 +262,16 @@ export class YBT extends HUSTOJ {
} }
if (VERDICT[staText[4]] === STATUS.STATUS_COMPILE_ERROR) { if (VERDICT[staText[4]] === STATUS.STATUS_COMPILE_ERROR) {
const ceInfoUrl = this.config.ceInfo.endpoint.replace('{rid}', rid); const ceInfoUrl = this.config.ceInfo.endpoint.replace('{rid}', rid);
let compilerText = 'Cannot get compiler text';
if (this.config.ceInfo.matcher) {
const resp = await this.get(ceInfoUrl); const resp = await this.get(ceInfoUrl);
const compilerText = decodeURIComponent(this.config.ceInfo.matcher.exec(resp.text)[1] const match = this.config.ceInfo.matcher.exec(resp.text);
.replace('\n', '').replace('<br/>', '\n').replace('\n\n', '\n')); if (match) compilerText = decodeURIComponent(match[1]).replace(/\n/g, '').replace(/<br\/>/g, '\n').replace(/\n\n/g, '\n');
} else if (this.config.ceInfo.selector) {
const { document } = await this.html(ceInfoUrl);
const match = document.querySelector(this.config.ceInfo.selector)?.innerHTML;
if (match) compilerText = decodeURIComponent(match).replace(/\n/g, '').replace(/<br\/>/g, '\n').replace(/\n\n/g, '\n');
}
end({ end({
status: STATUS.STATUS_COMPILE_ERROR, status: STATUS.STATUS_COMPILE_ERROR,
score: 0, score: 0,
@@ -294,10 +323,52 @@ export class YBT extends HUSTOJ {
} }
} }
export class YBTBAS extends HUSTOJ { export class YBTBAS extends YBT {
static langs = {
ybtbas: {
display: 'YBTBAS',
hidden: true,
remote: 'ybtbas',
},
'ybtbas.7': {
display: 'G++14',
monaco: 'cpp',
highlight: 'cpp',
},
'ybtbas.1': {
display: 'G++',
monaco: 'cpp',
highlight: 'cpp',
},
'ybtbas.2': {
display: 'GCC',
monaco: 'cpp',
highlight: 'cpp',
},
'ybtbas.3': {
display: 'Java',
monaco: 'java',
highlight: 'java',
},
'ybtbas.4': {
display: 'Pascal',
monaco: 'pas',
highlight: 'pascal',
},
'ybtbas.5': {
display: 'Python',
monaco: 'python',
highlight: 'python',
},
};
updateConfig() { updateConfig() {
this.config.login.check = '退出登录';
this.config.login.extra = async () => { this.config.login.extra = async () => {
const captcha = await this.get('/login_xx.php').responseType('arraybuffer'); const captcha = await this.get('/login_xx.php').responseType('arraybuffer');
if (captcha.headers['set-cookie']) this.cookie = captcha.headers['set-cookie'];
if (!global.parseCaptcha) await sleep(30000);
const parseCaptcha = global.parseCaptcha || _parseCaptcha;
return { return {
login: '登录', login: '登录',
auth: (await parseCaptcha(captcha.body)).toLowerCase(), auth: (await parseCaptcha(captcha.body)).toLowerCase(),
@@ -310,10 +381,12 @@ export class YBTBAS extends HUSTOJ {
codeField: 'source', codeField: 'source',
extra: { submit: '提交', user_id: this.account.handle }, extra: { submit: '提交', user_id: this.account.handle },
tooFrequent: '提交频繁啦!', tooFrequent: '提交频繁啦!',
rid: /runidx=([0-9]+)/gmi,
noRefetch: true,
}; };
this.config.ceInfo = { this.config.ceInfo = {
endpoint: '/show_ce_info.php?runid={rid}', endpoint: '/show_ce_info.php?runid={rid}',
matcher: /<td class="ceinfo">(.*?)<\/td>/gmi, selector: 'table[width="900px"]>tbody>tr>td>div>center>table>tbody>tr>td',
}; };
this.config.server = 'http://bas.ssoier.cn:8086/'; this.config.server = 'http://bas.ssoier.cn:8086/';
} }
+12 -1
View File
@@ -2,6 +2,7 @@ import codeforces from './codeforces';
import csgoj from './csgoj'; import csgoj from './csgoj';
import { import {
BZOJ as bzoj, HUSTOJ as hustoj, XJOI as xjoi, YBT as ybt, BZOJ as bzoj, HUSTOJ as hustoj, XJOI as xjoi, YBT as ybt,
YBTBAS as ybtbas,
} from './hustoj'; } from './hustoj';
import luogu from './luogu'; import luogu from './luogu';
import poj from './poj'; import poj from './poj';
@@ -9,6 +10,16 @@ import spoj from './spoj';
import uoj from './uoj'; import uoj from './uoj';
const vjudge: Record<string, any> = { const vjudge: Record<string, any> = {
codeforces, csgoj, 'luogu.legacy': luogu, poj, spoj, uoj, hustoj, bzoj, xjoi, ybt, codeforces,
csgoj,
'luogu.legacy': luogu,
poj,
spoj,
uoj,
hustoj,
bzoj,
xjoi,
ybt,
ybtbas,
}; };
export default vjudge; export default vjudge;