Skip to content

Commit

Permalink
Merge branch 'master' into tooomm-patch-1
Browse files Browse the repository at this point in the history
  • Loading branch information
tooomm authored Mar 2, 2025
2 parents 4509cd2 + 6d0ca40 commit 8812b46
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 66 deletions.
1 change: 1 addition & 0 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ You have to provide updates to Scryfall as all other changes would get overridde

## Anything else? ##
If you notice any other errors or have suggestions to the code, please [file an issue](https://github.com/Cockatrice/Magic-Spoiler/issues) in our repository.
We try to follow [PEP8 Style Guide](https://peps.python.org/pep-0008/).

<br>

Expand Down
7 changes: 0 additions & 7 deletions .github/ISSUE_TEMPLATE/card-has-errors---card-is-missing.md

This file was deleted.

This file was deleted.

17 changes: 0 additions & 17 deletions .github/ISSUE_TEMPLATE/feature_request.md

This file was deleted.

13 changes: 13 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Configuration options: https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates

version: 2
updates:
# Enable version updates for GitHub Actions
- package-ecosystem: "github-actions"
# Directory must be set to "/" to check for workflow files in .github/workflows
directory: "/"
# Check for updates to GitHub Actions once a week
schedule:
interval: "weekly"
# Limit the amout of open PR's (default = 5, disabled = 0, security updates are not impacted)
open-pull-requests-limit: 2
6 changes: 3 additions & 3 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ jobs:

steps:
- name: Checkout repo
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Checkout output branch
# Run only when triggered from master
if: env.DEPLOY == 'true'
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: files
path: ${{env.OUTPUT_PATH}}
Expand All @@ -54,7 +54,7 @@ jobs:
- name: Upload artifacts
# Run only when triggered from a PR
if: github.event_name == 'pull_request'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: spoiler-output
path: ${{github.workspace}}/${{env.OUTPUT_PATH}}
Expand Down
19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
[![](https://img.shields.io/badge/dynamic/xml.svg?label=Included%20sets&colorB=lightgrey&url=https%3A%2F%2Fraw.githubusercontent.com%2FCockatrice%2FMagic-Spoiler%2Ffiles%2Fspoiler.xml&query=count(%2F%2Flongname))](https://github.com/Cockatrice/Magic-Spoiler/tree/files) [![](https://img.shields.io/badge/dynamic/xml.svg?label=&colorB=lightgrey&url=https%3A%2F%2Fraw.githubusercontent.com%2FCockatrice%2FMagic-Spoiler%2Ffiles%2Fspoiler.xml&query=%2F%2Flongname)](https://github.com/Cockatrice/Magic-Spoiler/blob/files/spoiler.xml)
[![](https://img.shields.io/badge/dynamic/xml.svg?label=Release%20dates&colorB=lightgrey&url=https%3A%2F%2Fraw.githubusercontent.com%2FCockatrice%2FMagic-Spoiler%2Ffiles%2Fspoiler.xml&query=%2F%2Freleasedate)](https://github.com/Cockatrice/Magic-Spoiler/blob/files/spoiler.xml) [![](https://img.shields.io/badge/dynamic/xml.svg?label=Included%20cards&colorB=lightgrey&url=https%3A%2F%2Fraw.githubusercontent.com%2FCockatrice%2FMagic-Spoiler%2Ffiles%2Fspoiler.xml&query=count(%2F%2Fcard))](https://github.com/Cockatrice/Magic-Spoiler/blob/files/spoiler.xml)
![](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2FCockatrice%2FMagic-Spoiler%2Ffiles%2Fspoiler.xml&query=%2F%2Finfo%2FcreatedAt&label=Last%20update)

[![](https://img.shields.io/badge/dynamic/xml.svg?label=Included%20sets&colorB=lightgrey&url=https%3A%2F%2Fraw.githubusercontent.com%2FCockatrice%2FMagic-Spoiler%2Ffiles%2Fspoiler.xml&query=count(%2F%2Flongname))](https://github.com/Cockatrice/Magic-Spoiler/tree/files) [![](https://img.shields.io/badge/dynamic/xml.svg?label=&colorB=lightgrey&url=https%3A%2F%2Fraw.githubusercontent.com%2FCockatrice%2FMagic-Spoiler%2Ffiles%2Fspoiler.xml&query=%2F%2Flongname)](https://github.com/Cockatrice/Magic-Spoiler/blob/files/spoiler.xml)<br>
[![](https://img.shields.io/badge/dynamic/xml.svg?label=Release%20dates&colorB=lightgrey&url=https%3A%2F%2Fraw.githubusercontent.com%2FCockatrice%2FMagic-Spoiler%2Ffiles%2Fspoiler.xml&query=%2F%2Freleasedate)](https://github.com/Cockatrice/Magic-Spoiler/blob/files/spoiler.xml)<br>
[![](https://img.shields.io/badge/dynamic/xml.svg?label=Included%20cards&colorB=lightgrey&url=https%3A%2F%2Fraw.githubusercontent.com%2FCockatrice%2FMagic-Spoiler%2Ffiles%2Fspoiler.xml&query=count(%2F%2Fcard))](https://github.com/Cockatrice/Magic-Spoiler/blob/files/spoiler.xml)

<br>

# Magic-Spoiler [![Discord](https://img.shields.io/discord/314987288398659595?label=Discord&logo=discord&logoColor=white)](https://discord.gg/3Z9yzmA) [![Gitter Chat](https://img.shields.io/gitter/room/Cockatrice/Magic-Spoiler)](https://gitter.im/Cockatrice/Magic-Spoiler) #
# Magic-Spoiler [![Discord](https://img.shields.io/discord/314987288398659595?label=Discord&logo=discord&logoColor=white)](https://discord.gg/3Z9yzmA) #

Magic-Spoiler is a Python script to query the [Scryfall](https://scryfall.com) API to compile XML files (Cockatrice formatted) with information about spoiled cards from upcoming sets.

Magic-Spoiler is a Python script to query the <i>[Scryfall](https://scryfall.com)</i> API to compile XML files (Cockatrice formatted) and application-ready JSON files (MTGJSON formatted) with information about spoiled cards from upcoming sets.
## Output [![Build Status](https://github.com/Cockatrice/Magic-Spoiler/actions/workflows/deploy.yml/badge.svg?branch=master)](https://github.com/Cockatrice/Magic-Spoiler/actions/workflows/deploy.yml?query=branch%3Amaster) ##

## Output [![Build Status](https://github.com/Cockatrice/Magic-Spoiler/workflows/Deploy/badge.svg?branch=master)](https://github.com/Cockatrice/Magic-Spoiler/actions?query=workflow%3ADeploy+event%3Aworkflow_dispatch+event%3Aschedule+branch%3Amaster) ##
>[!TIP]
>**Enable "Download Spoilers Automatically" in `Cockatrice → Settings → Card Sources → Spoilers` to get updates automatically pushed to your client!**<br>
You can also [add the desired <b>.xml</b> file(s) to your <i>customsets</i> folder manually](https://github.com/Cockatrice/Cockatrice/wiki/Custom-Cards-&-Sets#to-add-custom-sets-follow-these-steps) to make Cockatrice use it.
You can also [add the desired <b>.xml</b> file(s) to your <i>customsets</i> folder manually](https://github.com/Cockatrice/Cockatrice/wiki/Custom-Cards-&-Sets#to-add-custom-sets-follow-these-steps) to make Cockatrice use them.
Just looking for XML files? [They are in our `files` branch!](https://github.com/Cockatrice/Magic-Spoiler/tree/files)

Expand Down Expand Up @@ -39,7 +44,7 @@ $> python -m magic_spoiler

### Output ###

All XML and JSON spoiler files are written to the `out/` directory:
All spoiler files are written to the `out/` directory:

| File Name | Content |
|:--|:--|
Expand Down
110 changes: 91 additions & 19 deletions magic_spoiler/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import pathlib
import shutil
import time
from enum import Enum
from typing import IO, Any, Dict, List, Tuple, Union

import requests
Expand All @@ -23,10 +24,60 @@
OUTPUT_DIR = pathlib.Path("out")
OUTPUT_TMP_DIR = OUTPUT_DIR.joinpath("tmp")
XML_ESCAPE_TRANSLATE_MAP = str.maketrans(
{"&": "&amp;", '"': "&quot;", "<": "&lt;", ">": "&gt;",
# remove any control characters outright
**{chr(i): "" for i in range(ord(" "))}}
{"&": "&amp;", '"': "&quot;", "<": "&lt;", ">": "&gt;"}
)
# remove any control characters outright
XML_ESCAPE_TRANSLATE_MAP.update({i: "" for i in range(ord(" "))})
# don't remove whitespace characters in the sub " " range
del XML_ESCAPE_TRANSLATE_MAP[ord("\n")]
del XML_ESCAPE_TRANSLATE_MAP[ord("\t")]

# copied from Cockatrice/oracle/src/oracleimporter.h OracleImporter::mainCardTypes
MAINTYPES = (
"Planeswalker",
"Creature",
"Land",
"Sorcery",
"Instant",
"Artifact",
"Enchantment"
)

class Priority(Enum):
FALLBACK = 0
PRIMARY = 10
SECONDARY = 20
REPRINT = 30
OTHER = 40

SET_TYPE_PRIORITY_MAP = {
"core": Priority.PRIMARY,
"expansion": Priority.PRIMARY,

"commander": Priority.SECONDARY,
"starter": Priority.SECONDARY,
"draft_innovation": Priority.SECONDARY,
"duel_deck": Priority.SECONDARY,

"archenemy": Priority.REPRINT,
"arsenal": Priority.REPRINT,
"box": Priority.REPRINT,
"from_the_vault": Priority.REPRINT,
"masterpiece": Priority.REPRINT,
"masters": Priority.REPRINT,
"memorabilia": Priority.REPRINT,
"planechase": Priority.REPRINT,
"premium_deck": Priority.REPRINT,
"promo": Priority.REPRINT,
"spellbook": Priority.REPRINT,
"token": Priority.REPRINT,
"treasure_chest": Priority.REPRINT,

"alchemy": Priority.OTHER,
"funny": Priority.OTHER,
"minigame": Priority.OTHER,
"vanguard": Priority.OTHER,
}


def __get_session() -> Union[requests.Session, Any]:
Expand Down Expand Up @@ -181,7 +232,7 @@ def scryfall2mtgjson(scryfall_cards: List[Dict[str, Any]]) -> List[Dict[str, Any
"rarity": sf_card["rarity"].replace("mythic", "mythic rare").title(),
"text": sf_card.get("oracle_text", ""),
"url": image,
"type": sf_card.get("type_line", "Unknown").replace("—", "-"),
"type": sf_card.get("type_line", "Unknown"),
"colorIdentity": sf_card.get("color_identity", None),
"colors": sf_card.get("colors", []),
"power": sf_card.get("power", None),
Expand Down Expand Up @@ -230,12 +281,14 @@ def fill_header_sets(card_xml_file: IO[Any], set_obj: Dict[str, str]) -> None:
:param card_xml_file: Card file path
:param set_obj: Set object
"""
priority = SET_TYPE_PRIORITY_MAP.get(set_obj["set_type"].lower(), Priority.FALLBACK)
card_xml_file.write(
"<set>\n"
"<name>" + set_obj["code"] + "</name>\n"
"<longname>" + set_obj["name"] + " (Spoiler)</longname>\n"
"<settype>" + set_obj["set_type"].replace("_", " ").title() + "</settype>\n"
"<releasedate>" + set_obj["released_at"] + "</releasedate>\n"
"<priority>" + str(priority.value) + "</priority>\n"
"</set>\n"
)

Expand Down Expand Up @@ -309,7 +362,27 @@ def write_cards(
text = ""

card_cmc = str(card["cmc"])
if card_cmc.endswith(".0"):
card_cmc = card_cmc[:-2]

card_type = card["type"]

table_row = "1"
if "Land" in card_type:
table_row = "0"
elif "Sorcery" in card_type:
table_row = "3"
elif "Instant" in card_type:
table_row = "3"
elif "Creature" in card_type:
table_row = "2"

for maintype in MAINTYPES:
if maintype in card_type:
break
else:
maintype = None

if "names" in card.keys():
if "layout" in card:
if card["layout"] == "split" or card["layout"] == "aftermath":
Expand All @@ -335,16 +408,6 @@ def write_cards(
else:
print(card["name"] + " has multiple names and no 'layout' key")

table_row = "1"
if "Land" in card_type:
table_row = "0"
elif "Sorcery" in card_type:
table_row = "3"
elif "Instant" in card_type:
table_row = "3"
elif "Creature" in card_type:
table_row = "2"

if "number" in card:
if "b" in str(card["number"]):
if "layout" in card:
Expand All @@ -359,13 +422,17 @@ def write_cards(
card_xml_file.write("<name>" + set_name + "</name>\n")
card_xml_file.write("<text>" + text + "</text>\n")
card_xml_file.write("<prop>\n")
if "colors" in card.keys():
for color in card["colors"]:
card_xml_file.write("<color>" + str(color) + "</color>\n")
if "colors" in card.keys() and card["colors"]:
card_xml_file.write("<colors>" + "".join(card["colors"]) + "</colors>\n")

card_xml_file.write("<type>" + card_type + "</type>\n")
if maintype:
card_xml_file.write("<maintype>" + maintype + "</maintype>\n")

card_xml_file.write("<cmc>" + card_cmc + "</cmc>\n")
card_xml_file.write("<manacost>" + mana_cost + "</manacost>\n")
if mana_cost:
card_xml_file.write("<manacost>" + mana_cost + "</manacost>\n")

if pow_tough:
card_xml_file.write("<pt>" + pow_tough + "</pt>\n")

Expand Down Expand Up @@ -498,10 +565,15 @@ def get_spoiler_sets() -> List[Dict[str, str]]:
return []

spoiler_sets = []
# Find list of possible Set Types to exclude here: https://scryfall.com/docs/api/sets
excluded_set_types = ["alchemy", "masterpiece", "arsenal", "from_the_vault", "spellbook", "premium_deck", "duel_deck",
"draft_innovation", "treasure_chest", "planechase", "archenemy", "vanguard", "box", "promo",
"token", "memorabilia", "minigame"]

for sf_set in sf_sets["data"]:
if (
sf_set["released_at"] >= time.strftime("%Y-%m-%d %H:%M:%S")
and sf_set["set_type"] != "token"
and sf_set["set_type"] not in excluded_set_types
and sf_set["card_count"]
):
sf_set["code"] = sf_set["code"].upper()
Expand Down
6 changes: 0 additions & 6 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
Pillow
PyYAML
beautifulsoup4
contextvars
datetime
feedparser
lxml
requests
requests_cache

0 comments on commit 8812b46

Please sign in to comment.