import dataclasses as dc import json from typing import List, Optional import requests class DataclassJSONEncoder(json.JSONEncoder): def default(self, o): if dc.is_dataclass(o): return dc.asdict(o) return super().default(o) @dc.dataclass class WebsiteAuthData(object): cookie_jar: requests.cookies.RequestsCookieJar access_token: str region: str @dc.dataclass(frozen=True) class BlizzardGame: uid: str name: str family: str @dc.dataclass(frozen=True) class ClassicGame(BlizzardGame): registry_path: Optional[str] = None registry_installation_key: Optional[str] = None exe: Optional[str] = None bundle_id: Optional[str] = None @dc.dataclass class RegionalGameInfo: uid: str try_for_free: bool @dc.dataclass class ConfigGameInfo(object): uid: str uninstall_tag: Optional[str] last_played: Optional[str] @dc.dataclass class ProductDbInfo(object): uninstall_tag: str ngdp: str = "" install_path: str = "" version: str = "" playable: bool = False installed: bool = False class Singleton(type): _instances = {} # type: ignore def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] class _Blizzard(object, metaclass=Singleton): TITLE_ID_MAP = { 21297: RegionalGameInfo("s1", True), 21298: RegionalGameInfo("s2", True), 5730135: RegionalGameInfo("wow", True), 5272175: RegionalGameInfo("prometheus", False), 22323: RegionalGameInfo("w3", False), 1146311730: RegionalGameInfo("destiny2", False), 1465140039: RegionalGameInfo("hs_beta", True), 1214607983: RegionalGameInfo("heroes", True), 17459: RegionalGameInfo("diablo3", True), 4613486: RegionalGameInfo("fenris", True), 1447645266: RegionalGameInfo("viper", False), 1329875278: RegionalGameInfo("odin", True), 1279351378: RegionalGameInfo("lazarus", False), 1514493267: RegionalGameInfo("zeus", False), 1381257807: RegionalGameInfo("rtro", False), 1464615513: RegionalGameInfo("wlby", False), 5198665: RegionalGameInfo("osi", False), 1179603525: RegionalGameInfo("fore", False), } TITLE_ID_MAP_CN = {**TITLE_ID_MAP, 17459: RegionalGameInfo("d3cn", False)} BATTLENET_GAMES = [ BlizzardGame("s1", "StarCraft", "S1"), BlizzardGame("s2", "StarCraft II", "S2"), BlizzardGame("wow", "World of Warcraft", "WoW"), BlizzardGame("wow_classic", "World of Warcraft Classic", "WoW_wow_classic"), BlizzardGame("prometheus", "Overwatch", "Pro"), BlizzardGame("w3", "Warcraft III", "W3"), BlizzardGame("hs_beta", "Hearthstone", "WTCG"), BlizzardGame("heroes", "Heroes of the Storm", "Hero"), BlizzardGame("d3cn", "暗黑破壞神III", "D3CN"), BlizzardGame("diablo3", "Diablo III", "D3"), BlizzardGame("fenris", "Diablo IV", "FEN"), BlizzardGame("viper", "Call of Duty: Black Ops 4", "VIPR"), BlizzardGame("odin", "Call of Duty: Modern Warfare", "ODIN"), BlizzardGame("lazarus", "Call of Duty: MW2 Campaign Remastered", "LAZR"), BlizzardGame("zeus", "Call of Duty: Black Ops Cold War", "ZEUS"), BlizzardGame("rtro", "Blizzard Arcade Collection", "RTRO"), BlizzardGame("wlby", "Crash Bandicoot 4: It's About Time", "WLBY"), BlizzardGame("osi", "Diablo® II: Resurrected", "OSI"), BlizzardGame("fore", "Call of Duty: Vanguard", "FORE"), ] CLASSIC_GAMES = [ ClassicGame("d2", "Diablo® II", "Diablo II", "Diablo II", "DisplayIcon", "Game.exe", "com.blizzard.diabloii"), ClassicGame("d2LOD", "Diablo® II: Lord of Destruction®", "Diablo II"), # TODO exe and bundleid ClassicGame( "w3ROC", "Warcraft® III: Reign of Chaos", "Warcraft III", "Warcraft III", "InstallLocation", "Warcraft III.exe", "com.blizzard.WarcraftIII", ), ClassicGame( "w3tft", "Warcraft® III: The Frozen Throne®", "Warcraft III", "Warcraft III", "InstallLocation", "Warcraft III.exe", "com.blizzard.WarcraftIII", ), ClassicGame("sca", "StarCraft® Anthology", "Starcraft", "StarCraft"), # TODO exe and bundleid ] def __init__(self): self._games = {game.uid: game for game in self.BATTLENET_GAMES + self.CLASSIC_GAMES} def __getitem__(self, key: str) -> BlizzardGame: """ :param key: str uid (eg. "prometheus") :returns: game by `key` """ return self._games[key] def game_by_title_id(self, title_id: int, cn: bool) -> BlizzardGame: """ :param cn: flag if china game definitions should be search though :raises KeyError: when unknown title_id for given region """ if cn: regional_info = self.TITLE_ID_MAP_CN[title_id] else: regional_info = self.TITLE_ID_MAP[title_id] return self[regional_info.uid] def try_for_free_games(self, cn: bool) -> List[BlizzardGame]: """ :param cn: flag if china game definitions should be search though """ return [ self[info.uid] for info in (self.TITLE_ID_MAP_CN if cn else self.TITLE_ID_MAP).values() if info.try_for_free ] Blizzard = _Blizzard()