Add find, refactor types
This commit is contained in:
parent
a2d9e5bcbc
commit
748cb12c2c
8 changed files with 1464 additions and 34 deletions
18
api.ts
18
api.ts
|
@ -1,6 +1,8 @@
|
||||||
import { stringify } from "querystring";
|
import { stringify } from "querystring";
|
||||||
import { get } from "request";
|
import { get } from "request";
|
||||||
|
|
||||||
|
import { Article } from "./types";
|
||||||
|
|
||||||
const URI = "https://api.cardmarket.com/ws/v2.0/output.json";
|
const URI = "https://api.cardmarket.com/ws/v2.0/output.json";
|
||||||
|
|
||||||
export default class CardMarketApi {
|
export default class CardMarketApi {
|
||||||
|
@ -48,4 +50,20 @@ export default class CardMarketApi {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getAllArticles(uid: string): Promise<Article[]> {
|
||||||
|
const perPage = 1000;
|
||||||
|
let start = 0;
|
||||||
|
let response;
|
||||||
|
let data: Article[] = [];
|
||||||
|
do {
|
||||||
|
response = await this.get(`/users/${uid}/articles`, {
|
||||||
|
start,
|
||||||
|
maxResults: perPage
|
||||||
|
});
|
||||||
|
data = data.concat(response.article as Article[]);
|
||||||
|
start += response.article.length;
|
||||||
|
} while (response.article.length == perPage);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
16
convert.ts
16
convert.ts
|
@ -1,7 +1,10 @@
|
||||||
|
import { existsSync } from "fs";
|
||||||
|
|
||||||
import { asyncLoadJSON, asyncSaveJSON } from "./utils";
|
import { asyncLoadJSON, asyncSaveJSON } from "./utils";
|
||||||
|
|
||||||
interface JSONCard {
|
interface JSONCard {
|
||||||
mcmId: number;
|
mcmId: number;
|
||||||
|
set: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface JSONSet {
|
interface JSONSet {
|
||||||
|
@ -11,12 +14,23 @@ interface JSONSet {
|
||||||
type JSONDB = Record<string, JSONSet>;
|
type JSONDB = Record<string, JSONSet>;
|
||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
|
if (!existsSync("AllPrintings.json")) {
|
||||||
|
console.error(
|
||||||
|
"AllPrintings.json not found. Download from: https://www.mtgjson.com/files/AllPrintings.json"
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
console.info("Loading AllPrintings.json");
|
||||||
const db = await asyncLoadJSON<JSONDB>("AllPrintings.json");
|
const db = await asyncLoadJSON<JSONDB>("AllPrintings.json");
|
||||||
let acc = {};
|
let acc = {};
|
||||||
for (const set in db) {
|
for (const set in db) {
|
||||||
db[set].cards.forEach(c => (acc[c.mcmId] = c));
|
db[set].cards.forEach(c => {
|
||||||
|
c.set = set;
|
||||||
|
acc[c.mcmId] = c;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
asyncSaveJSON("mcmCards.json", acc);
|
asyncSaveJSON("mcmCards.json", acc);
|
||||||
|
console.info("Saved converted info to mcmCards.json. You're ready to go!");
|
||||||
}
|
}
|
||||||
|
|
||||||
run();
|
run();
|
||||||
|
|
34
fetch.ts
34
fetch.ts
|
@ -1,30 +1,5 @@
|
||||||
import { existsSync } from "fs";
|
|
||||||
|
|
||||||
import CardMarketApi from "./api";
|
import CardMarketApi from "./api";
|
||||||
import { asyncLoadJSON, asyncSaveJSON } from "./utils";
|
import { asyncSaveJSON } from "./utils";
|
||||||
|
|
||||||
interface MCMCard {
|
|
||||||
name: string;
|
|
||||||
rarity: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
type MCMDB = Record<string, MCMCard>;
|
|
||||||
|
|
||||||
async function getAllArticles(api: CardMarketApi, uid: string) {
|
|
||||||
const perPage = 1000;
|
|
||||||
let start = 0;
|
|
||||||
let response;
|
|
||||||
let data = [];
|
|
||||||
do {
|
|
||||||
response = await api.get(`/users/${uid}/articles`, {
|
|
||||||
start,
|
|
||||||
maxResults: perPage
|
|
||||||
});
|
|
||||||
data = data.concat(response.article);
|
|
||||||
start += response.article.length;
|
|
||||||
} while (response.article.length == perPage);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
if (process.argv.length < 3) {
|
if (process.argv.length < 3) {
|
||||||
|
@ -33,13 +8,8 @@ async function run() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const uid = process.argv[2];
|
const uid = process.argv[2];
|
||||||
if (!existsSync("mcmCards.json")) {
|
|
||||||
console.error("Card db is missing! Run 'yarn convert-db' first.");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
let db = await asyncLoadJSON<MCMDB>("mcmCards.json");
|
|
||||||
let api = new CardMarketApi();
|
let api = new CardMarketApi();
|
||||||
const articles = await getAllArticles(api, uid);
|
const articles = await api.getAllArticles(uid);
|
||||||
await asyncSaveJSON(`${uid}-cards.json`, articles);
|
await asyncSaveJSON(`${uid}-cards.json`, articles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
55
find.ts
Normal file
55
find.ts
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import { existsSync } from "fs";
|
||||||
|
|
||||||
|
import { Article, MCMDB } from "./types";
|
||||||
|
import { asyncLoadJSON, asyncSaveJSON, leanArticle, leanCard } from "./utils";
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
if (process.argv.length < 3) {
|
||||||
|
console.error("Usage: yarn fetch <uid>");
|
||||||
|
process.exit(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const uid = process.argv[2];
|
||||||
|
const uidCards = `${uid}-cards.json`;
|
||||||
|
if (!existsSync("mcmCards.json")) {
|
||||||
|
console.error("Card db is missing! Run 'yarn convert-db' first.");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
if (!existsSync(uidCards)) {
|
||||||
|
console.error(`Could not find ${uidCards}! Run 'yarn fetch ${uid}' first.`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
let db = await asyncLoadJSON<MCMDB>("mcmCards.json");
|
||||||
|
let articles = await asyncLoadJSON<Article[]>(`${uid}-cards.json`);
|
||||||
|
|
||||||
|
let cards = articles
|
||||||
|
.filter(art => art.idProduct in db)
|
||||||
|
.map(art => {
|
||||||
|
const card = db[art.idProduct];
|
||||||
|
return {
|
||||||
|
...leanArticle(art),
|
||||||
|
...leanCard(card)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
let valid = cards.filter(
|
||||||
|
c =>
|
||||||
|
(c.language == "Italian" || c.language == "English") &&
|
||||||
|
c.price <= 0.1 &&
|
||||||
|
(c.rarity == "rare" || c.rarity == "mythic")
|
||||||
|
);
|
||||||
|
let netprice = valid.reduce((a, c) => (a += c.price), 0);
|
||||||
|
console.log(
|
||||||
|
`Found ${
|
||||||
|
valid.length
|
||||||
|
} cards (rare or mythic) <= € 0.10 (IT/EN), price to buy all: € ${netprice.toFixed(
|
||||||
|
2
|
||||||
|
)}.`
|
||||||
|
);
|
||||||
|
await asyncSaveJSON(
|
||||||
|
`${uid}-filtered-cards.json`,
|
||||||
|
valid.sort((a, b) => a.edhrecRank - b.edhrecRank)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
|
@ -7,7 +7,8 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"fetch": "ts-node fetch.ts",
|
"fetch": "ts-node fetch.ts",
|
||||||
"convert-db": "ts-node convert.ts"
|
"init-db": "ts-node convert.ts",
|
||||||
|
"find": "ts-node find.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^12.12.11",
|
"@types/node": "^12.12.11",
|
||||||
|
|
133
types.ts
Normal file
133
types.ts
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
export interface ForeignData {
|
||||||
|
flavorText: string;
|
||||||
|
language: string;
|
||||||
|
multiverseId: number;
|
||||||
|
name: string;
|
||||||
|
text: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Legalities {
|
||||||
|
commander: string;
|
||||||
|
duel: string;
|
||||||
|
legacy: string;
|
||||||
|
modern: string;
|
||||||
|
vintage: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Prices {
|
||||||
|
mtgo: Record<string, number>;
|
||||||
|
mtgoFoil: Record<string, number>;
|
||||||
|
paper: Record<string, number>;
|
||||||
|
paperFoil: Record<string, number>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PurchaseUrls {
|
||||||
|
cardmarket: string;
|
||||||
|
tcgplayer: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MCMCard {
|
||||||
|
artist: string;
|
||||||
|
borderColor: string;
|
||||||
|
colorIdentity: string[];
|
||||||
|
colors: string[];
|
||||||
|
convertedManaCost: number;
|
||||||
|
edhrecRank: number;
|
||||||
|
flavorText: string;
|
||||||
|
foreignData: ForeignData[];
|
||||||
|
frameVersion: string;
|
||||||
|
hasFoil: boolean;
|
||||||
|
hasNonFoil: boolean;
|
||||||
|
isMtgo: boolean;
|
||||||
|
isPaper: boolean;
|
||||||
|
layout: string;
|
||||||
|
legalities: Legalities;
|
||||||
|
manaCost: string;
|
||||||
|
mcmId: number;
|
||||||
|
mcmMetaId: number;
|
||||||
|
mtgoFoilId: number;
|
||||||
|
mtgoId: number;
|
||||||
|
multiverseId: number;
|
||||||
|
name: string;
|
||||||
|
number: string;
|
||||||
|
originalText: string;
|
||||||
|
originalType: string;
|
||||||
|
power: string;
|
||||||
|
prices: Prices;
|
||||||
|
printings: string[];
|
||||||
|
purchaseUrls: PurchaseUrls;
|
||||||
|
rarity: string;
|
||||||
|
rulings: any[];
|
||||||
|
scryfallId: string;
|
||||||
|
scryfallIllustrationId: string;
|
||||||
|
scryfallOracleId: string;
|
||||||
|
subtypes: string[];
|
||||||
|
supertypes: any[];
|
||||||
|
tcgplayerProductId: number;
|
||||||
|
text: string;
|
||||||
|
toughness: string;
|
||||||
|
type: string;
|
||||||
|
types: string[];
|
||||||
|
uuid: string;
|
||||||
|
set: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MCMDB = Record<string, MCMCard>;
|
||||||
|
|
||||||
|
// Lighther version of MCMCard without a lot of useless information
|
||||||
|
export interface LeanMCMCard {
|
||||||
|
colorIdentity: string[];
|
||||||
|
colors: string[];
|
||||||
|
convertedManaCost: number;
|
||||||
|
edhrecRank: number;
|
||||||
|
manaCost: string;
|
||||||
|
mcmId: number;
|
||||||
|
name: string;
|
||||||
|
rarity: string;
|
||||||
|
scryfallId: string;
|
||||||
|
subtypes: string[];
|
||||||
|
supertypes: any[];
|
||||||
|
text: string;
|
||||||
|
type: string;
|
||||||
|
types: string[];
|
||||||
|
set: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Language {
|
||||||
|
idLanguage: number;
|
||||||
|
languageName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Link {
|
||||||
|
rel: string;
|
||||||
|
href: string;
|
||||||
|
method: string;
|
||||||
|
action: string;
|
||||||
|
idArticle?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Article {
|
||||||
|
idArticle: number;
|
||||||
|
idProduct: number;
|
||||||
|
language: Language;
|
||||||
|
comments: string;
|
||||||
|
price: number;
|
||||||
|
count: number;
|
||||||
|
inShoppingCart: boolean;
|
||||||
|
condition: string;
|
||||||
|
isFoil: boolean;
|
||||||
|
isSigned: boolean;
|
||||||
|
isPlayset: boolean;
|
||||||
|
isAltered: boolean;
|
||||||
|
links: Link[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LeanArticle {
|
||||||
|
idArticle: number;
|
||||||
|
language: string;
|
||||||
|
comments: string;
|
||||||
|
price: number;
|
||||||
|
count: number;
|
||||||
|
condition: string;
|
||||||
|
}
|
33
utils.ts
33
utils.ts
|
@ -1,5 +1,7 @@
|
||||||
import { readFile, writeFile } from "fs";
|
import { readFile, writeFile } from "fs";
|
||||||
|
|
||||||
|
import { Article, LeanArticle, LeanMCMCard, MCMCard } from "./types";
|
||||||
|
|
||||||
export async function asyncLoadJSON<T>(filename: string): Promise<T> {
|
export async function asyncLoadJSON<T>(filename: string): Promise<T> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
readFile(filename, "utf8", (err, data) => {
|
readFile(filename, "utf8", (err, data) => {
|
||||||
|
@ -24,3 +26,34 @@ export async function asyncSaveJSON(
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function leanCard(card: MCMCard): LeanMCMCard {
|
||||||
|
return {
|
||||||
|
colorIdentity: card.colorIdentity,
|
||||||
|
colors: card.colors,
|
||||||
|
convertedManaCost: card.convertedManaCost,
|
||||||
|
edhrecRank: card.edhrecRank,
|
||||||
|
manaCost: card.manaCost,
|
||||||
|
mcmId: card.mcmId,
|
||||||
|
name: card.name,
|
||||||
|
rarity: card.rarity,
|
||||||
|
scryfallId: card.scryfallId,
|
||||||
|
subtypes: card.subtypes,
|
||||||
|
supertypes: card.supertypes,
|
||||||
|
text: card.text,
|
||||||
|
type: card.type,
|
||||||
|
types: card.types,
|
||||||
|
set: card.set
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function leanArticle(article: Article): LeanArticle {
|
||||||
|
return {
|
||||||
|
idArticle: article.idArticle,
|
||||||
|
language: article.language.languageName,
|
||||||
|
comments: article.comments,
|
||||||
|
price: article.price,
|
||||||
|
count: article.count,
|
||||||
|
condition: article.condition
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
1206
yarn-error.log
Normal file
1206
yarn-error.log
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue