import * as ejs from "ejs"; import { existsSync } from "fs"; import { createServer } from "http"; import { Article, asyncLoadJSON, CardItem, dictMap, filterDict, leanArticle, leanCard, MCMDB, onlyUnique, spanBy, } from "../lib"; const colorNames = { CL: "Colorless", MC: "Multicolor", L: "Land", W: "White", U: "Blue", B: "Black", R: "Red", G: "Green", WU: "Azorius", UB: "Dimir", BR: "Rakdos", RG: "Gruul", WG: "Selesnya", WB: "Orzhov", UR: "Izzet", BG: "Golgari", WR: "Boros", UG: "Simic", WUG: "Bant", WUB: "Esper", UBR: "Grixis", BRG: "Jund", WRG: "Naya", WBG: "Abzan", WUR: "Jeskai", UBG: "Sultai", WBR: "Mardu", URG: "Temur" }; const columns: Record boolean> = { W: c => c.types[0] != "Land" && colorid(c.colorIdentity) == "W", U: c => c.types[0] != "Land" && colorid(c.colorIdentity) == "U", B: c => c.types[0] != "Land" && colorid(c.colorIdentity) == "B", R: c => c.types[0] != "Land" && colorid(c.colorIdentity) == "R", G: c => c.types[0] != "Land" && colorid(c.colorIdentity) == "G", MC: c => c.types[0] != "Land" && c.colorIdentity.length > 1, CL: c => c.types[0] != "Land" && colorid(c.colorIdentity) == "CL", L: c => c.types[0] == "Land" }; function wubrg(a: string, b: string) { const order = ["W", "U", "B", "R", "G"]; const indexA = order.indexOf(a); const indexB = order.indexOf(b); return indexA - indexB; } function colorid(colors: string[]): string { if (colors.length < 1) { return "CL"; } return colors.sort(wubrg).join(""); } function prettyColor(color: string) { if (color in colorNames) { return colorNames[color]; } return color; } const allTypes = [ "Creature", "Planeswalker", "Instant", "Sorcery", "Artifact", "Enchantment" ]; function typeSort(a: string, b: string): number { const indexA = allTypes.indexOf(a); const indexB = allTypes.indexOf(b); return indexA - indexB; } function abcSort(a: string, b: string): number { if (a > b) { return 1; } if (b > a) { return -1; } return 0; } // This would be properly typed but as of currently TS does not allow circular references for types function deepSum(dict: any): number { if (Array.isArray(dict)) { return dict.length; } let total = 0; for (let key in dict) { if (Array.isArray(dict[key])) { total += dict[key].length; } else { total += deepSum(dict[key]); } } return total; } async function run() { if (process.argv.length < 3) { console.error("Usage: yarn fetch "); 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("mcmCards.json"); let articles = await asyncLoadJSON(`${uid}-cards.json`); let cards: CardItem[] = 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") ) .filter(onlyUnique); // Filter by colors and cmc (nested) let colorcards = filterDict(valid, columns); let cmccards = dictMap(colorcards, (cards, col) => dictMap( spanBy(cards, c => col == "MC" || col == "L" ? colorid(c.colorIdentity) : c.types[0] ), typed => spanBy(typed, c => c.convertedManaCost) ) ); const server = createServer(async (req, res) => { const template = await ejs.renderFile("templates/cube.ejs", { user: uid, cards: valid, cmccards, columns, utils: { wubrg, prettyColor, colorid, deepSum, typeSort, abcSort } }); res.end(template); }); server.on("clientError", (err, socket) => { socket.end("HTTP/1.1 400 Bad Request\r\n\r\n"); }); server.listen(8000); } run();