Commit ab8a3741 authored by nanahira's avatar nanahira

subenv

parent 6d1321ad
Pipeline #29358 passed with stages
in 2 minutes and 1 second
...@@ -112,3 +112,6 @@ dist ...@@ -112,3 +112,6 @@ dist
Dockerfile Dockerfile
.gitlab-ci.yml .gitlab-ci.yml
/config.yaml /config.yaml
/ygopro-*
/pack.db
...@@ -107,4 +107,5 @@ dist ...@@ -107,4 +107,5 @@ dist
/output /output
/config.yaml /config.yaml
/ygopro-database /ygopro-*
/pack.db
...@@ -6,53 +6,47 @@ variables: ...@@ -6,53 +6,47 @@ variables:
GIT_DEPTH: "1" GIT_DEPTH: "1"
POST_DEPTH: 10 POST_DEPTH: 10
zh-CN: .task:
stage: generate stage: generate
tags: tags:
- linux - linux
image: node:buster
variables: variables:
LOCALE: zh-CN LOCALE: zh-CN
NPM_SCRIPT: start
CARD_SYMBOL: '简体中文卡'
FILE_SYMBOL: 'cn'
script: ./ci-scripts/generate.sh script: ./ci-scripts/generate.sh
artifacts: artifacts:
paths: paths:
- output - output
zh-CN:
extends: .task
en-US: en-US:
stage: generate extends: .task
tags:
- linux
image: node:buster
variables: variables:
LOCALE: en-US LOCALE: en-US
script: ./ci-scripts/generate.sh
artifacts:
paths:
- output
ja-JP: ja-JP:
stage: generate extends: .task
tags:
- linux
image: node:buster
variables: variables:
LOCALE: ja-JP LOCALE: ja-JP
script: ./ci-scripts/generate.sh
artifacts:
paths:
- output
ko-KR: ko-KR:
stage: generate extends: .task
tags:
- linux
image: node:buster
variables: variables:
LOCALE: ko-KR LOCALE: ko-KR
script: ./ci-scripts/generate.sh
artifacts: env408:
paths: extends: .task
- output variables:
LOCALE: zh-CN
NPM_SCRIPT: start:subenv
DATE: '2013-01-01'
CARD_SYMBOL: '408环境'
FILE_SYMBOL: 'env408'
upload_to_minio: upload_to_minio:
stage: deploy stage: deploy
...@@ -61,6 +55,7 @@ upload_to_minio: ...@@ -61,6 +55,7 @@ upload_to_minio:
- en-US - en-US
- ja-JP - ja-JP
- ko-KR - ko-KR
- env408
tags: tags:
- linux - linux
script: script:
......
...@@ -8,13 +8,14 @@ export OUTPUT_PATH="./output/$LOCALE" ...@@ -8,13 +8,14 @@ export OUTPUT_PATH="./output/$LOCALE"
apt update ; apt -y install build-essential python3 sqlite3 git tar apt update ; apt -y install build-essential python3 sqlite3 git tar
git clone --depth=1 https://code.mycard.moe/mycard/ygopro-database git clone --depth=1 https://code.mycard.moe/mycard/ygopro-database
wget -O pack.db https://cdn02.moecube.com:444/ygopro-card-list/pack.db
cp -rf ./ygopro-database/locales/ja-JP/cards.cdb "$JP_DATABASE_PATH" cp -rf ./ygopro-database/locales/ja-JP/cards.cdb "$JP_DATABASE_PATH"
npm ci npm ci
npm run build npm run build
npm start npm run "$NPM_SCRIPT"
cd "$OUTPUT_PATH" cd "$OUTPUT_PATH"
tar zcvf "../cn-data-$LOCALE.tar.gz" deck expansions tar zcvf "../$FILE_SYMBOL-data-$LOCALE.tar.gz" deck expansions
cd .. cd ..
This diff is collapsed.
...@@ -5,7 +5,8 @@ ...@@ -5,7 +5,8 @@
"main": "run.ts", "main": "run.ts",
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"start": "node build/run.js | bunyan" "start": "node build/run.js | bunyan",
"start:subenv": "node build/subenv.js | bunyan"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
...@@ -21,14 +22,17 @@ ...@@ -21,14 +22,17 @@
"@types/bunyan": "^1.8.6", "@types/bunyan": "^1.8.6",
"@types/node": "^14.11.5", "@types/node": "^14.11.5",
"@types/sqlite3": "^3.1.6", "@types/sqlite3": "^3.1.6",
"@types/underscore": "^1.10.24",
"axios": "^0.20.0", "axios": "^0.20.0",
"bunyan": "^1.8.14", "bunyan": "^1.8.14",
"jinja-js": "^0.1.8", "jinja-js": "^0.1.8",
"lodash": "^4.17.21",
"moment": "^2.30.1",
"sqlite": "^4.0.15", "sqlite": "^4.0.15",
"sqlite3": "^5.0.0", "sqlite3": "^5.0.0",
"typescript": "^4.0.3",
"underscore": "^1.11.0",
"yaml": "^1.10.0" "yaml": "^1.10.0"
},
"devDependencies": {
"@types/lodash": "^4.17.7",
"typescript": "^5.5.4"
} }
} }
import { DBReader } from "./src/dbreader"; import { DBReader } from "./src/dbreader";
import { NWFetcher } from "./src/nwfetcher"; import { NWFetcher } from "./src/nwfetcher";
import _ from "underscore"; import _ from "lodash";
import { CNOCGFetcher } from "./src/cnocg"; import { CNOCGFetcher } from "./src/cnocg";
import { ExtraCards } from "./src/ExtraCards"; import { ExtraCards } from "./src/ExtraCards";
import { DirectFetcher } from "./src/direct"; import { DirectFetcher } from "./src/direct";
...@@ -16,7 +16,6 @@ async function main() { ...@@ -16,7 +16,6 @@ async function main() {
//const missingExtraCards = ExtraCards.filter(c => !cards.some(cc => c.code === cc.code)); //const missingExtraCards = ExtraCards.filter(c => !cards.some(cc => c.code === cc.code));
//await dbreader.run(cards.concat(missingExtraCards)); //await dbreader.run(cards.concat(missingExtraCards));
const cards = await DirectFetcher.fetchOnce(); const cards = await DirectFetcher.fetchOnce();
//console.log(cards);
await dbreader.run(cards); await dbreader.run(cards);
process.exit(); process.exit();
} }
......
import { Config, loadConfig } from "./config"; import { Config, loadConfig } from "./config";
import bunyan from "bunyan"; import bunyan from "bunyan";
import _ from "underscore"; import _ from "lodash";
export default abstract class Base { export default abstract class Base {
config: Config; config: Config;
......
...@@ -5,7 +5,11 @@ export interface Config { ...@@ -5,7 +5,11 @@ export interface Config {
postDepth: number; postDepth: number;
jpDatabasePath: string; jpDatabasePath: string;
cnDatabasePath: string; cnDatabasePath: string;
cardListDatabasePath: string;
outputPath: string; outputPath: string;
descSymbol: string;
fileSymbol: string;
// cardListCondition?: string;
} }
export async function loadConfig(): Promise<Config> { export async function loadConfig(): Promise<Config> {
...@@ -14,6 +18,10 @@ export async function loadConfig(): Promise<Config> { ...@@ -14,6 +18,10 @@ export async function loadConfig(): Promise<Config> {
postDepth: process.env.POST_DEPTH ? parseInt(process.env.POST_DEPTH) : 5, postDepth: process.env.POST_DEPTH ? parseInt(process.env.POST_DEPTH) : 5,
jpDatabasePath: process.env.JP_DATABASE_PATH || "./ygopro-database/locales/ja-JP/cards.cdb", jpDatabasePath: process.env.JP_DATABASE_PATH || "./ygopro-database/locales/ja-JP/cards.cdb",
cnDatabasePath: process.env.SOURCE_TARGET_PATH || "./ygopro-database/locales/zh-CN/cards.cdb", cnDatabasePath: process.env.SOURCE_TARGET_PATH || "./ygopro-database/locales/zh-CN/cards.cdb",
outputPath: process.env.OUTPUT_PATH || "./output" cardListDatabasePath: process.env.CARD_LIST_DATABASE_PATH || "./pack.db",
outputPath: process.env.OUTPUT_PATH || "./output",
descSymbol: process.env.CARD_SYMBOL || "简体中文卡",
fileSymbol: process.env.FILE_SYMBOL || "cn",
// cardListCondition: process.env.CARD_LIST_CONDITION,
} }
} }
import { open, Database } from "sqlite"; import { open, Database } from "sqlite";
import sqlite3 from "sqlite3"; import sqlite3 from "sqlite3";
import _ from "underscore"; import _ from "lodash";
import Base from "./base"; import Base from "./base";
import { promises as fs } from "fs"; import { promises as fs } from "fs";
import { generateBanlistFromCode } from "./utility"; import { Banlist, generateBanlistFromCode, mergeBanlists } from "./utility";
import { CardPool, DeckGenerator } from "./deck"; import { CardPool, DeckGenerator } from "./deck";
import moment from "moment";
const textsFields = ["id", "name", "desc"] const textsFields = ["id", "name", "desc"]
for (let i = 1; i <= 16; ++i) { for (let i = 1; i <= 16; ++i) {
...@@ -66,7 +67,7 @@ export class Card { ...@@ -66,7 +67,7 @@ export class Card {
} }
async getRelatedCards(db: Database) { async getRelatedCards(db: Database) {
const code = this.code; const code = this.code;
const moreCodes: number[] = (await db.all('SELECT id FROM datas WHERE id > ? AND id <= ?', [code, code + 10])).map(m => m.id); const moreCodes: number[] = (await db.all('SELECT id FROM datas WHERE id > ? AND id <= ?', [code, code + 20])).map(m => m.id);
const cards = await Promise.all(moreCodes.map(code => this.createCardFromRelatedCode(code, db))); const cards = await Promise.all(moreCodes.map(code => this.createCardFromRelatedCode(code, db)));
return cards; return cards;
} }
...@@ -114,7 +115,7 @@ export class DBReader extends Base { ...@@ -114,7 +115,7 @@ export class DBReader extends Base {
jpdb: Database; jpdb: Database;
cndb: Database; cndb: Database;
outputdb: Database; outputdb: Database;
private async openDatabase(path: string) { async openDatabase(path: string) {
return await open({ return await open({
filename: path, filename: path,
driver: sqlite3.Database driver: sqlite3.Database
...@@ -134,9 +135,9 @@ export class DBReader extends Base { ...@@ -134,9 +135,9 @@ export class DBReader extends Base {
} }
} }
private async openOutputDatabase() { private async openOutputDatabase() {
const fullPath = `${this.config.outputPath}/expansions/cn.cdb`; const fullPath = `${this.config.outputPath}/expansions/${this.config.fileSymbol}.cdb`;
const createDirectoryPaths = [ const createDirectoryPaths = [
`${this.config.outputPath}/deck/cn`, `${this.config.outputPath}/deck/${this.config.fileSymbol}`,
`${this.config.outputPath}/expansions` `${this.config.outputPath}/expansions`
]; ];
for (let createDirectoryPath of createDirectoryPaths) { for (let createDirectoryPath of createDirectoryPaths) {
...@@ -191,15 +192,18 @@ export class DBReader extends Base { ...@@ -191,15 +192,18 @@ export class DBReader extends Base {
const otherCodes: number[] = (await this.cndb.all(sql)).map(m => m.id); const otherCodes: number[] = (await this.cndb.all(sql)).map(m => m.id);
return otherCodes; return otherCodes;
} }
async generateBanlist(codes: number[]) { async generateBanlist(codes: number[], extraBanlists: Banlist[] = []) {
const otherCodes = await this.getOtherCardCodes(codes); const otherCodes = await this.getOtherCardCodes(codes);
const banlistString = await generateBanlistFromCode([ const banlistString = await generateBanlistFromCode([
mergeBanlists([
...extraBanlists,
{ {
name: "cn", name: moment().format('YYYY.MM') + ' ' + this.config.descSymbol,
list: [ list: [
otherCodes otherCodes
] ]
} }
]),
]); ]);
await fs.writeFile(`${this.config.outputPath}/expansions/lflist.conf`, banlistString); await fs.writeFile(`${this.config.outputPath}/expansions/lflist.conf`, banlistString);
} }
...@@ -219,13 +223,15 @@ export class DBReader extends Base { ...@@ -219,13 +223,15 @@ export class DBReader extends Base {
const cardPool = await this.categorizeCards(cards); const cardPool = await this.categorizeCards(cards);
const deckGenerator = new DeckGenerator(cardPool); const deckGenerator = new DeckGenerator(cardPool);
const deckTexts = deckGenerator.getDeckTexts(); const deckTexts = deckGenerator.getDeckTexts();
await Promise.all(_.range(deckTexts.length).map(i => fs.writeFile(`${this.config.outputPath}/deck/cn/cn_${i}.ydk`, deckTexts[i]))); await Promise.all(_.range(deckTexts.length).map(i => fs.writeFile(`${this.config.outputPath}/deck/${this.config.fileSymbol}/${this.config.fileSymbol}_${i}.ydk`, deckTexts[i])));
} }
async run(cards: Card[]) { async run(cards: Card[], options: {
extraBanlists?: Banlist[]
} = {}) {
await Promise.all(cards.map(card => card.loadData(this.cndb))); await Promise.all(cards.map(card => card.loadData(this.cndb)));
const extendedCards = _.flatten(await Promise.all(cards.map(card => card.getRelatedCards(this.cndb))), true); const extendedCards = (await Promise.all(cards.map(card => card.getRelatedCards(this.cndb)))).flat();
const allCards = cards.concat(extendedCards); const allCards = _.uniqBy(cards.concat(extendedCards), (s) => s.code);
const queries = _.flatten(allCards.map(card => card.getSQLQueries()), true); const queries = allCards.flatMap(card => card.getSQLQueries());
await this.openOutputDatabase(); await this.openOutputDatabase();
await this.outputdb.run("BEGIN TRANSACTION;"); await this.outputdb.run("BEGIN TRANSACTION;");
for (let query of queries) { for (let query of queries) {
...@@ -235,7 +241,7 @@ export class DBReader extends Base { ...@@ -235,7 +241,7 @@ export class DBReader extends Base {
await this.outputdb.run("COMMIT;"); await this.outputdb.run("COMMIT;");
const allCodes = allCards.map(card => card.code); const allCodes = allCards.map(card => card.code);
this.log.info(`Database created.`); this.log.info(`Database created.`);
await this.generateBanlist(allCodes); await this.generateBanlist(allCodes, options.extraBanlists || []);
this.log.info(`LFList created.`); this.log.info(`LFList created.`);
await this.generatePatch(allCodes); await this.generatePatch(allCodes);
this.log.info(`Patch created.`); this.log.info(`Patch created.`);
......
import _ from "underscore"; import _ from "lodash";
import { promises as fs } from "fs"; import { promises as fs } from "fs";
export interface CardPool { export interface CardPool {
......
...@@ -22,13 +22,13 @@ export class DirectFetcher extends Base { ...@@ -22,13 +22,13 @@ export class DirectFetcher extends Base {
const codeDifference = code - c; const codeDifference = code - c;
// 新加入的卡号比某一卡号大,并且 10 以内,则新的卡是那张卡的关联卡,不入。 // 新加入的卡号比某一卡号大,并且 10 以内,则新的卡是那张卡的关联卡,不入。
if (codeDifference > 0 && codeDifference <= 10) { if (codeDifference > 0 && codeDifference <= 20) {
this.log.debug(`Skipped forward-related dup: ${code} ${c}`); this.log.debug(`Skipped forward-related dup: ${code} ${c}`);
return; return;
} }
// 新加入的卡号比某一卡号小,并且 10 以内,则把旧的卡删掉。- // 新加入的卡号比某一卡号小,并且 10 以内,则把旧的卡删掉。-
if (codeDifference < 0 && codeDifference >= -10) { if (codeDifference < 0 && codeDifference >= -20) {
this.log.debug(`Would remove backword-related dup: ${code} ${c}`); this.log.debug(`Would remove backword-related dup: ${code} ${c}`);
posToRemove.push(i); posToRemove.push(i);
} }
......
import axios from "axios"; import axios from "axios";
import _ from "underscore"; import _ from "lodash";
import Base from "./base"; import Base from "./base";
import { DBReader } from "./dbreader"; import { DBReader } from "./dbreader";
...@@ -60,7 +60,7 @@ export class NWFetcher extends Base { ...@@ -60,7 +60,7 @@ export class NWFetcher extends Base {
async fetch(): Promise<string[]> { async fetch(): Promise<string[]> {
this.log.debug(`Started fetching...`); this.log.debug(`Started fetching...`);
const posts = await this.fetchPosts(); const posts = await this.fetchPosts();
const strings = _.flatten(await Promise.all(posts.map(m => this.getAllStrings(m))), true); const strings = (await Promise.all(posts.map(m => this.getAllStrings(m)))).flat();
this.log.debug(`Done.`); this.log.debug(`Done.`);
return _.uniq(strings); return _.uniq(strings);
} }
......
import _ from "lodash";
import Base from "./base";
import { Card, DBReader } from "./dbreader";
export class PackDbFetcher extends Base {
static async fetchOnce(dbreader: DBReader, cardListCondition?: string) {
const fetcher = new PackDbFetcher({ name: "Pack DB Fetcher", level: 'info' });
await fetcher.init();
return await fetcher.fetch(dbreader, cardListCondition);
}
async fetch(dbreader: DBReader, cardListCondition?: string) {
const packDb = await dbreader.openDatabase(this.config.cardListDatabasePath);
const packDbData = await packDb.all<{ id: number, pack_id: string, date: string }[]>("SELECT id FROM pack" + (cardListCondition ? ` WHERE ${cardListCondition}` : ""));
return packDbData.map(m => new Card(m.id));
}
}
import _ from "underscore"; import _ from "lodash";
import jinja from "jinja-js"; import jinja from "jinja-js";
import { promises as fs } from "fs"; import { promises as fs } from "fs";
interface Banlist { export interface Banlist {
name: string name: string
list: number[][] list: number[][]
} }
...@@ -11,3 +11,55 @@ export async function generateBanlistFromCode(banlists: Banlist[]) { ...@@ -11,3 +11,55 @@ export async function generateBanlistFromCode(banlists: Banlist[]) {
const template = await fs.readFile("./templates/lflist.conf.j2", "utf-8"); const template = await fs.readFile("./templates/lflist.conf.j2", "utf-8");
return jinja.render(template, { banlists }); return jinja.render(template, { banlists });
} }
export async function readLFList(content: string): Promise<Banlist[]> {
const lines = content.split("\n");
const ret: Banlist[] = [];
let name = "";
let list: number[][] = [];
const nextList = (nextName: string) => {
if (list.length > 0) {
ret.push({ name, list });
}
list = [];
name = nextName;
}
for (let line of lines) {
if (line.startsWith("#")) {
continue;
}
if(line.startsWith("!")) {
nextList(line.slice(1));
continue;
}
// xxxxxxxx 0
// use regex to match
const match = line.match(/^(\d{4,})\s(\d)/);
if(!match) {
continue;
}
const code = parseInt(match[1]);
const status = parseInt(match[2]);
list[status] ??= [];
list[status].push(code);
}
nextList('');
return ret;
}
export function mergeBanlists(lists: Banlist[]): Banlist {
const ret: Banlist = { name: lists[0].name, list: [] };
const existingCodes = new Set<number>();
for (let status = 0; status < 3; ++status) {
for (let list of lists) {
ret.list[status] ??= [];
for(let code of list.list[status] ?? []) {
if (!existingCodes.has(code)) {
ret.list[status].push(code);
existingCodes.add(code);
}
}
}
}
return ret;
}
import { DBReader } from "./src/dbreader";
import _ from "lodash";
import { PackDbFetcher } from "./src/packdbfetcher";
import axios from "axios";
import { readLFList } from "./src/utility";
import moment from "moment";
const DATE = process.env.DATE;
async function getBanlist() {
const lflistReq = await axios.get<string>("https://cdn02.moecube.com:444/ygopro-database/zh-CN/lflist.conf", {
responseType: 'document'
});
const lflists = (await readLFList(lflistReq.data)).filter(l => !l.name.includes('TCG')).map(l => ({
...l,
date: moment(l.name.split(' ')[0], 'YYYY.MM')
})).filter(l => l.date.isBefore(DATE));
return _.maxBy(lflists, l => l.date.unix());
}
async function main() {
const dbreader = new DBReader({
name: "Database",
//level: Logger.DEBUG
});
await dbreader.init();
const cards = await PackDbFetcher.fetchOnce(dbreader, `date <= '${DATE}'`);
console.log(cards.length);
await dbreader.run(cards, {
extraBanlists: [await getBanlist()],
});
process.exit();
}
main();
...@@ -2,7 +2,7 @@ import { open, Database } from "sqlite"; ...@@ -2,7 +2,7 @@ import { open, Database } from "sqlite";
import sqlite3 from "sqlite3"; import sqlite3 from "sqlite3";
import { promises as fs } from "fs"; import { promises as fs } from "fs";
import { DeckGenerator } from "../src/deck"; import { DeckGenerator } from "../src/deck";
import _ from "underscore"; import _ from "lodash";
async function openDatabase(path: string) { async function openDatabase(path: string) {
return await open({ return await open({
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment