UNPKG

atiro

Version:
312 lines (265 loc) 9.86 kB
const fs = require('fs'); const chalk = require('chalk'); const prompts = require('prompts'); const { exec } = require('child_process'); const readline = require('readline-sync'); const webtool = require('./webtool.js'); const config = require('./config.js'); const produced = require('./produced.js'); const { exit } = require('./utils.js'); const title = 'vjudge', key = 'vjudge'; const site = 'https://vjudge.net'; async function check(page) { await page.goto(site); const text = await page.$eval('#navbarResponsive', element => element ? element.textContent : ''); if (!text.toLowerCase().includes('login')) return true; else return false; } async function login(options) { let browser, page; try { if (options.cookies != undefined) { const file = options.cookies; if (file !== true) { const cookies = fs.readFileSync(file).toString(); webtool.setCookies(key, cookies); console.log(chalk.green('[Success]'), 'Set cookies successfully.'); } else { console.log('Enter Cookies:'); let cookies = ''; while(true) { cookies += readline.prompt(); try { webtool.setCookies(key, cookies); console.log(chalk.green('[Success]'), 'Set cookies successfully.'); break; } catch (error) { } }; } } else { console.log('Checking login status...'); browser = await webtool.open(key); page = (await browser.pages())[0]; const res = await check(page); await webtool.close(key, browser); if (res) { console.log('You have already logged in.'); return; } console.log('Please continue in the browser...'); browser = await webtool.open(key, { headless: false }); page = (await browser.pages())[0]; await page.goto(`${site}`); await page.click('a.nav-link.login'); await page.waitForNavigation({ timeout: 0 }); const text = await page.$eval('#navbarResponsive', element => element ? element.textContent : ''); await webtool.close(key, browser); if (text.toLowerCase().includes('login')) { throw 1; } console.log(chalk.green('[Success]'), 'Login successfully.'); } } catch (error) { console.log(chalk.red('[Error]'), 'Login failed.'); } exit(); } async function logout() { try { produced.unsetValue('cookies', key); console.log(chalk.green('[Success]'), 'Logout successfully.'); } catch (error) { console.log(chalk.red('[Error]'), 'Logout failed.'); } } async function getSamples(page, url) { await page.goto(url); await page.goto(await page.$eval('#frame-description', node => node.src)); const samples = await page.$$('table.vjudge_sample > tbody > tr'); const res = []; for (let i = 0; i < samples.length; i++) { res.push({ in: await samples[i].evaluate(node => node.children[0].innerText), ans: await samples[i].evaluate(node => node.children[1].innerText) }); } return res; } async function getProblem(page, contest, problem, file, data) { const samples = await getSamples(page, `${site}/problem/${problem}`) fs.writeFileSync(`${file}.cpp`, `// Atiro: vjudge ${problem}\n\n` + (fs.existsSync(`${file}.cpp`) ? fs.readFileSync(`${file}.cpp`).toString() : '')); if (samples.length == 0) { console.log(chalk.blue('[Notice]'), 'No samples detected.'); } else if (samples.length == 1) { fs.writeFileSync(`${data}.in`, samples[0].in); fs.writeFileSync(`${data}.ans`, samples[0].ans); } else if (samples.length > 1) { for (let i = 0; i < samples.length; i++) { fs.writeFileSync(`${data}${i + 1}.in`, samples[i].in); fs.writeFileSync(`${data}${i + 1}.ans`, samples[i].ans); } } } async function getContest(browser, contest) {} async function get(id, file, data) { let browser, page; try { file = (file || config.getValue('file', 'name')).replace(/\.cpp$/, ''); data = (data || file).replace(/\.in$/, ''); browser = await webtool.open(key); page = (await browser.pages())[0]; // if (!(await check(page))) { // console.log(chalk.blue('[Notice]'), 'Please login first.'); // await browser.close(); // return; // } if (id.match(/problem\/([^\s\/?#]+)/)) { const res = id.match(/problem\/([^\s\/?#]+)/); const problem = res[1]; await getProblem(page, undefined, problem, file, data); } else if (id.match(/^([^\s\/?#]+)$/)) { const res = id.match(/^([^\s\/?#]+)$/); const problem = res[1]; await getProblem(page, undefined, problem, file, data); } else { console.log(chalk.red('[Error]'), 'No problem ID recognized.'); } await webtool.close(key, browser); } catch (error) { console.log(chalk.red('[Error]'), 'Get failed.\nFor one possible reason, does it need permission to access?'); if (browser) await browser.close(); } } async function submitProblem(page, contest, problem, code) { await page.goto(`${site}/problem/${problem}`); await page.reload(); await page.click('#btn-submit'); const open_options = await page.$$('#submit-open > label'); const opens = []; let defaultOpen; for (let i = 0, j = 0; i < open_options.length; i++) { const open = await open_options[i].evaluate(node => node.textContent.trim()); opens.push({ title: open, value: await open_options[i].evaluate(node => node.children[0].id) }); if (await open_options[i].evaluate(node => node.active)) { defaultOpen = j; } j++; } const open = (await prompts({ type: 'select', name: 'open', message: 'Open:', choices: opens, initial: defaultOpen })).open; const lang_options = await page.$$('#submit-language > option'); const langs = []; let defaultLang; for (let i = 0, j = 0; i < lang_options.length; i++) { const lang = await lang_options[i].evaluate(node => node.textContent.trim()); if (lang.toLowerCase().includes('c++') || lang.toLowerCase().includes('g++')) { langs.push({ title: lang, value: await lang_options[i].evaluate(node => node.value) }); if (await lang_options[i].evaluate(node => node.selected)) { defaultLang = j; } j++; } } if (!langs.length) { throw 1; } const lang = (await prompts({ type: 'select', name: 'lang', message: 'Language:', choices: langs, initial: defaultLang })).lang; await page.click(`#${open}`); await page.select('#submit-language', lang); await page.$eval('#submit-solution', (node, code) => { node.value = code; }, code); await page.waitForFunction(() => { return !document.querySelector('div.modal-footer > #btn-submit').disabled; }, { timeout: 5000 }); await page.click('div.modal-footer > #btn-submit'); setTimeout(() => { console.log(chalk.blue('[Notice]'), 'If you are waiting too long, manually check if it has been already submitted.') }, 5000); await page.waitForSelector('#solutionModalLabel > a:first-child', { timeout: 10000 }); console.log(chalk.green('[Success]'), 'Submit successfully.'); if (process.platform == 'win32') { exec(`start ${await page.$eval('#solutionModalLabel > a:first-child', node => node.href)}`); } else if (process.platform == 'linux') { exec(`xdg-open ${await page.$eval('#solutionModalLabel > a:first-child', node => node.href)}`); } else if (process.platform == 'darwin') { exec(`open ${await page.$eval('#solutionModalLabel > a:first-child', node => node.href)}`); } else { console.log(`Submission Page: ${page.url()}`); } } async function submit(file, id) { let browser, page; try { file = (file || config.getValue('file', 'name')).replace(/\.cpp$/, ''); if (!fs.existsSync(`${file}.cpp`)) { console.log(chalk.red('[Error]'), `No file "${file}.cpp" detected.`); return; } browser = await webtool.open(key); page = (await browser.pages())[0]; // if (!(await check(page))) { // console.log(chalk.blue('[Notice]'), 'Please login first.'); // await browser.close(); // return; // } let code = fs.readFileSync(`${file}.cpp`).toString().replace(/\r\n/g, '\n').replace(/\r/g, '\n'); let contest, problem; if (code.match(/^\/\/\s*Atiro\s*:\s*vjudge\s+([^\s\/?#]+)/)) { const res = code.match(/^\/\/\s*Atiro\s*:\s*vjudge\s+([^\s\/?#]+)/); problem = res[1]; code = code.replace(/^\/\/\s*Atiro\s*:\s*vjudge\s+([^\s\/?#]+)\s*/, ''); } if (id) { if (id.match(/problem\/([^\s\/?#]+)/)) { const res = id.match(/problem\/([^\s\/?#]+)/); problem = res[1]; } else if (id.match(/^([^\s\/?#]+)$/)) { const res = id.match(/^([^\s\/?#]+)$/); problem = res[1]; } } if (!problem) { console.log(chalk.red('[Error]'), 'No problem ID recognized.'); await browser.close(); return; } await submitProblem(page, undefined, problem, code); await webtool.close(key, browser); } catch (error) { console.log(chalk.red('[Error]'), 'Submit failed.\nFor one possible reason, please check whether you have logged in.'); if (browser) await browser.close(); } finally { exit(); } } module.exports = (program) => { program .command('login').alias('i') .description(`login to ${title}`) .option('-c, --cookies [file]', 'use cookies') .action(login); program .command('logout').alias('o') .description(`logout from ${title}`) .action(logout); program .command('get').alias('g') .description(`get exactly one or a contest of problem(s) from ${title}`) .arguments('<id> [file] [data]') .action(get); program .command('submit').alias('s') .description(`submit code to ${title}`) .arguments('[file] [id]') .action(submit); }