-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
165 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
node_modules | ||
package-lock.json | ||
config.json | ||
*.js | ||
data |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { readFileSync, writeFileSync } from 'node:fs' | ||
import { ensureDirSync } from 'fs-extra' | ||
import LuoguAccountService from './service' | ||
import yamljs from 'yamljs' | ||
|
||
const config = JSON.parse(readFileSync('config.json').toString()) | ||
|
||
const luogu = new LuoguAccountService( | ||
config.uid, | ||
`_uid=${config.uid}; __client_id=${config.client_id}`, | ||
) | ||
|
||
async function main() { | ||
if (!await luogu.isLoggedIn()) return console.error('Logged in failed') | ||
console.log('Logged in') | ||
for (const pid of config.pids) { | ||
await new Promise((resolve) => setTimeout(resolve, 10000)) | ||
console.log(`Getting problem ${pid}`) | ||
ensureDirSync(`data/${pid}/testdata`) | ||
ensureDirSync(`data/${pid}/additional_file`) | ||
const problem = await luogu.getProblem(pid) | ||
writeFileSync(`data/${pid}/problem_zh.md`, problem.content) | ||
writeFileSync(`data/${pid}/problem.yaml`, yamljs.stringify({ | ||
pid, | ||
owner: 1, | ||
title: problem.title, | ||
tag: [], | ||
nSubmit: 0, | ||
nAccept: 0, | ||
})) | ||
const testdataZip = await luogu.getTestdata(pid) | ||
testdataZip.extractAllTo(`data/${pid}/testdata/`, true) | ||
const rids = await luogu.getRecords(pid) | ||
for (const rid of rids) { | ||
const record = await luogu.getRecord(rid) | ||
if (!record) continue | ||
writeFileSync(`data/${pid}/additional_file/${rid}-${record.score}.cpp`, record.code) | ||
} | ||
} | ||
} | ||
|
||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{ | ||
"name": "luogu-problem-export", | ||
"version": "1.0.0", | ||
"description": "", | ||
"main": "index.ts", | ||
"scripts": { | ||
"start": "tsc && node index.js" | ||
}, | ||
"author": "Milmon <[email protected]>", | ||
"license": "GPL-3.0", | ||
"dependencies": { | ||
"@types/node": "^20.11.16", | ||
"adm-zip": "^0.5.10", | ||
"fs-extra": "^11.2.0", | ||
"superagent": "^8.1.2", | ||
"yamljs": "^0.3.0" | ||
}, | ||
"devDependencies": { | ||
"@types/adm-zip": "^0.5.5", | ||
"@types/fs-extra": "^11.0.4", | ||
"@types/superagent": "^8.1.3", | ||
"@types/yamljs": "^0.2.34", | ||
"typescript": "^5.3.3" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import superagent from 'superagent' | ||
import AdmZip from 'adm-zip' | ||
|
||
const UA = [ | ||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', | ||
'AppleWebKit/537.36 (KHTML, like Gecko)', | ||
'Chrome/118.0.0.0 Safari/537.36 Edg/118.0.2088.76', | ||
].join(' ') | ||
|
||
export default class LuoguAccountService { | ||
constructor( | ||
private userId: number, | ||
private cookie: string, | ||
private endPoint = 'https://www.luogu.com.cn', | ||
) { } | ||
|
||
get(url: string) { | ||
return superagent | ||
.get(this.endPoint + url) | ||
.set('Cookie', this.cookie) | ||
.set('User-Agent', UA) | ||
} | ||
post(url: string) { | ||
return superagent | ||
.post(this.endPoint + url) | ||
.set('Cookie', this.cookie) | ||
.set('User-Agent', UA) | ||
} | ||
|
||
async isLoggedIn(): Promise<boolean> { | ||
const { body } = await this.get('/problem/list').query({ _contentOnly: true }) | ||
return body.currentUser.uid === this.userId | ||
} | ||
|
||
async getProblem(pid: string) { | ||
const { body: { currentData: { problem } } } = await this | ||
.get(`/problem/${pid}`).query({ _contentOnly: true }) | ||
let content = '' | ||
if (problem.background) content += `## 题目背景\n\n${problem.background}\n\n` | ||
if (problem.description) content += `## 题目描述\n\n${problem.description}\n\n` | ||
if (problem.inputFormat) content += `## 输入格式\n\n${problem.inputFormat}\n\n` | ||
if (problem.outputFormat) content += `## 输出格式\n\n${problem.outputFormat}\n\n` | ||
let id = 0 | ||
for (const [input, output] of problem.samples) { | ||
id++ | ||
content += `\`\`\`input${id}\n${input}\n\`\`\`\n\n` | ||
content += `\`\`\`output${id}\n${output}\n\`\`\`\n\n` | ||
} | ||
if (problem.hint) content += `## 提示\n\n${problem.hint}\n\n` | ||
return { | ||
title: problem.title, | ||
content: content.trim(), | ||
// limits: | ||
} | ||
} | ||
|
||
async getTestdata(pid: string) { | ||
const { body } = await this.get(`/fe/api/problem/generateDataDownloadLink/${pid}`) | ||
return new AdmZip(body) | ||
} | ||
|
||
async getRecords(pid: string) { | ||
let rids: number[] = [] | ||
for (let page = 1; ; page++) { | ||
const { body: { currentData: { records: { result: records } } } } = await this | ||
.get('/record/list').query({ pid, page, _contentOnly: true }) | ||
const ids = records.map((record: any) => record.id) | ||
if (ids.length === 0) return rids | ||
rids = rids.concat(ids) | ||
} | ||
} | ||
|
||
async getRecord(rid: number) { | ||
const { body: { currentData } } = await this | ||
.get(`/record/${rid}`).query({ _contentOnly: true }) | ||
if (currentData.errorMessage) return null | ||
else return { | ||
code: currentData.record.sourceCode, | ||
score: currentData.record.score || 0, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"compilerOptions": { | ||
"target": "ES6", | ||
"module": "commonjs", | ||
"esModuleInterop": true, | ||
"forceConsistentCasingInFileNames": true, | ||
"strict": true, | ||
"skipLibCheck": true | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,8 +6,5 @@ | |
"forceConsistentCasingInFileNames": true, | ||
"strict": true, | ||
"skipLibCheck": true | ||
}, | ||
"ts-node": { | ||
"esm": true | ||
} | ||
} | ||
} |