Source code for src.nameinternal

from __future__ import annotations

from collections import defaultdict
from functools import total_ordering
from aiohttp import ClientSession

import json
import os

from src.configuration import config
from src.events import add_listener
from src.utils import complete_match

# this is an iterable of 1-length str to remove from queries
_replace_str = " -'()."

__all__ = [
    "get", "get_card",
    "get_relic_stats",
    "get_event",
    "query", "get_run_mod",
]

_cache: dict[str, dict[str, str]] = {}
_internal_cache: dict[str, Base] = {}
_query_cache: dict[str, list[Base]] = defaultdict(list)

def sanitize(x: str) -> str:
    x = x.lower()
    for s in _replace_str:
        x = x.replace(s, "")
    return x

[docs] def query(name: str, type: str | None = None): name = sanitize(name) res = complete_match(name, _query_cache) if len(res) == 1: ret = _query_cache[res[0]].pop(0) # this makes sure to cycle through cards if there are multiple _query_cache[res[0]].append(ret) return ret return None
[docs] def get(name: str) -> Base: if name in _internal_cache: return _internal_cache[name] return Unknown(name)
[docs] def get_card(card: str) -> SingleCard: name, _, upgrades = card.partition("+") inst = get(name) return SingleCard(inst, int(upgrades or 0))
@total_ordering class Base: cls_name = "" store_internal = True def __init__(self, data: dict[str, str]): assert self.cls_name, "Cannot instantiate Base" self.internal = data.get("id", data["name"]) self.name = data["name"] self.description = data["description"] self.mod = data.get("mod") if self.mod == "Slay the Spire": self.mod = None @property def info(self) -> str: return f"{self.name}: {self.description}" def __str__(self): return self.name def __hash__(self) -> int: return hash(self.internal) def __eq__(self, other) -> bool: # technically, this could be checking against just Base # but we don't want to accidentally compare cards and relics # so it's disallowed here and will throw an error instead if not isinstance(other, type(self)): return NotImplemented return self.name == other.name def __lt__(self, other) -> bool: if not isinstance(other, type(self)): return NotImplemented return self.name < other.name class Card(Base): cls_name = "card" def __init__(self, data: dict[str, str]): super().__init__(data) self.color: str = data["color"] self.rarity: str = data["rarity"] self.type: str = data["type"] self.cost: str | None = data["cost"] or None self.pack: str | None = data.get("pack") @property def info(self) -> str: mod = "" if self.pack: mod = f"(Packmaster: {self.pack})" elif self.mod: mod = f"(Mod: {self.mod})" return f"{self.name} - [{self.cost}] {self.color} {self.rarity} {self.type}: {self.description} {mod}" class SingleCard: def __init__(self, card: Card, upgrades: int = 0): self.card = card self.upgrades = upgrades @property def name(self): match self.upgrades: case 0: up = "" case 1: up = "+" case n: up = f"+{n}" return f"{self.card.name}{up}" def __hash__(self): return hash(self.name) def __str__(self): return self.name def __eq__(self, value): return isinstance(value, SingleCard) and self.name == value.name class Relic(Base): cls_name = "relic" def __init__(self, data: dict[str, str]): super().__init__(data) self.pool: str | None = data.get("pool") self.tier: str = data["tier"] self.flavour_text: str = data["flavorText"] @property def info(self) -> str: mod = "" if self.mod: mod = f"(Mod: {self.mod})" pool = " " if self.pool: pool = f" ({self.pool})" return f"{self.name} - {self.tier}{pool}: {self.description} {mod}" class RelicSet(Base): cls_name = "relic_set" def __init__(self, data: dict[str, list[str]]): super().__init__(data) self.relic_list: list[str] = data["relic_list"] self.description: str = f"{data['description']}: {', '.join(data['relic_list'])}" class Potion(Base): cls_name = "potion" def __init__(self, data: dict[str, str]): super().__init__(data) self.rarity: str = data["rarity"] self.color: str = data.get("color") @property def info(self) -> str: mod = "" if self.mod: mod = f"(Mod: {self.mod})" color = "" if self.color: color = f" ({self.color})" return f"{self.name} - {self.rarity}{color}: {self.description} {mod}" class Keyword(Base): cls_name = "keyword" store_internal = False def __init__(self, data: dict[str, str]): super().__init__(data) self.names: list[str] = data.get("names", []) class ScoreBonus(Base): cls_name = "score_bonus" def __init__(self, data: dict[str, str]): super().__init__(data) self.format_string: str = data.get("format_string", self.name) class Unknown(Base): cls_name = "<unknown>" def __init__(self, name: str): self.internal = name self.name = name self.description = f"Could not find description for {name!r} (this is a bug)" self.mod = None def __getattr__(self, attr: str): return f"<Unknown attribute {attr}>" _str_to_cls: dict[str, Base] = { "cards": Card, "relics": Relic, "relic_set": RelicSet, "potions": Potion, "keywords": Keyword, "score_bonuses": ScoreBonus, } def _get_name(x: str, d: str, default: str) -> str: return _cache[d].get(x, {}).get("NAME", default)
[docs] def get_event(name: str, default: str | None = None) -> str: if default is None: default = f"<Unknown Event {name}>" return _get_name(name, "events", default)
[docs] def get_relic_stats(name: str) -> list[str]: return _cache["relic_stats"][name]["TEXT"]
[docs] def get_run_mod(name: str) -> str: return f'{_cache["run_mods"][name]["NAME"]} - {_cache["run_mods"][name]["DESCRIPTION"]}'
@add_listener("setup_init") async def load(): _cache.clear() async with ClientSession() as session: for mod in config.spire.enabled_mods: data = None async with session.get(f"https://raw.githubusercontent.com/OceanUwU/slaytabase/main/docs/{mod.lower()}/data.json") as resp: if resp.ok: decoded = await resp.text() data = json.loads(decoded) if data is None: raise ValueError(f"Mod {mod} could not be found.") for cat, maps in data.items(): if cat in _str_to_cls: for mapping in maps: inst: Base = _str_to_cls[cat](mapping) if inst.store_internal: _internal_cache[inst.internal] = inst _query_cache[sanitize(inst.name)].append(inst) with open("score_bonuses.json") as f: j = json.load(f) for x in j["score_bonuses"]: inst = ScoreBonus(x) _internal_cache[inst.internal] = inst _query_cache[sanitize(inst.name)].append(inst) for file in os.listdir(os.path.join("argo", "sts1")): name = file[:-5] if not name.startswith("_"): with open(os.path.join("argo", "sts1", file)) as f: _cache[name] = json.load(f) # Load relic sets try: with open("relic_sets.json", "r") as f: j = json.load(f) for relic_set in j['relic_sets']: relset = RelicSet(relic_set) for alias in relic_set['set_aliases']: _query_cache[alias].append(relset) except FileNotFoundError: pass