#! /usr/bin/env python3 import argparse import json import requests import sys from typing import Dict, List, Any, Optional class APIClient: API_VERSION = "v1" def __init__(self, base_uri: str, token: str) -> None: self.base_uri = base_uri self.token = token def exchange_data( self, request_uri: str, request_args: Dict[str, Any] ) -> Dict[str, Any]: if self.base_uri[-1:] != "/": uri = "{}/{}".format(self.base_uri, request_uri) else: uri = "{}{}".format(self.base_uri, request_uri) headers = { "Authorization": "Token {}".format(self.token), "Content-Type": "application/json; charset=utf-8", } data = json.dumps(request_args).encode("utf8") r = requests.request( "GET", uri, headers=headers, data=data, allow_redirects=False, ) if r.headers["content-type"] != "application/json; charset=utf-8": raise Exception( "API returned invalid content type '{}'!".format( r.headers["content-type"] ) ) jsondata = r.json() if r.status_code == 200: return jsondata if "error" not in jsondata: raise Exception( "API returned error code {} but did not include 'error' attribute in response JSON!".format( r.status_code ) ) error = jsondata["error"] if r.status_code == 401: raise Exception("The API token used is not authorized against the server!") if r.status_code == 404: raise Exception( "The server does not support this game/version or request object and returned '{}'".format( error ) ) if r.status_code == 405: raise Exception( "The server did not recognize the request and returned '{}'".format( error ) ) if r.status_code == 500: raise Exception( "The server had an error processing the request and returned '{}'".format( error ) ) if r.status_code == 501: raise Exception("The server does not support this version of the API!") raise Exception( "The server returned an invalid status code {}!".format(r.status_code) ) def info_exchange(self) -> None: resp = self.exchange_data("", {}) print("Server name: {}".format(resp["name"])) print("Server admin email: {}".format(resp["email"])) print("Server supported versions: {}".format(", ".join(resp["versions"]))) def __id_check(self, idtype: str, ids: List[str]) -> None: if idtype not in ["card", "song", "instance", "server"]: raise Exception("Invalid ID type provided!") if idtype == "card" and len(ids) == 0: raise Exception("Invalid number of IDs given!") if idtype == "song" and len(ids) not in [1, 2]: raise Exception("Invalid number of IDs given!") if idtype == "instance" and len(ids) != 3: raise Exception("Invalid number of IDs given!") if idtype == "server" and len(ids) != 0: raise Exception("Invalid number of IDs given!") def records_exchange( self, game: str, version: str, idtype: str, ids: List[str], since: Optional[int], until: Optional[int], ) -> None: self.__id_check(idtype, ids) params = { "ids": ids, "type": idtype, "objects": ["records"], } # type: Dict[str, Any] if since is not None: params["since"] = since if until is not None: params["until"] = until resp = self.exchange_data( "{}/{}/{}".format(self.API_VERSION, game, version), params, ) print(json.dumps(resp["records"], indent=4)) def profile_exchange( self, game: str, version: str, idtype: str, ids: List[str] ) -> None: self.__id_check(idtype, ids) resp = self.exchange_data( "{}/{}/{}".format(self.API_VERSION, game, version), { "ids": ids, "type": idtype, "objects": ["profile"], }, ) print(json.dumps(resp["profile"], indent=4)) def statistics_exchange( self, game: str, version: str, idtype: str, ids: List[str] ) -> None: self.__id_check(idtype, ids) resp = self.exchange_data( "{}/{}/{}".format(self.API_VERSION, game, version), { "ids": ids, "type": idtype, "objects": ["statistics"], }, ) print(json.dumps(resp["statistics"], indent=4)) def catalog_exchange(self, game: str, version: str) -> None: resp = self.exchange_data( "{}/{}/{}".format(self.API_VERSION, game, version), { "ids": [], "type": "server", "objects": ["catalog"], }, ) print(json.dumps(resp["catalog"], indent=4)) def main() -> None: # Global arguments parser = argparse.ArgumentParser( description="A sample API client for an e-AMUSEMENT API provider." ) parser.add_argument( "-t", "--token", type=str, required=True, help="The authorization token for speaing to the API.", ) parser.add_argument( "-b", "--base", type=str, required=True, help="Base URI to connect to for all requests.", ) subparser = parser.add_subparsers(dest="request") # Info request subparser.add_parser("info") # Score request record_parser = subparser.add_parser("records") record_parser.add_argument( "-g", "--game", type=str, required=True, help="The game we want to look records up for.", ) record_parser.add_argument( "-v", "--version", type=str, required=True, help="The version we want to look records up for.", ) record_parser.add_argument( "-t", "--type", type=str, required=True, choices=["card", "song", "instance", "server"], help="The type of ID used to look up records.", ) record_parser.add_argument( "-s", "--since", metavar="TIMESTAMP", default=None, type=int, help="Only load records updated since TIMESTAMP", ) record_parser.add_argument( "-u", "--until", metavar="TIMESTAMP", default=None, type=int, help="Only load records updated before TIMESTAMP", ) record_parser.add_argument( "id", metavar="ID", nargs="*", type=str, help="The ID we will look up records for.", ) # Profile request profile_parser = subparser.add_parser("profile") profile_parser.add_argument( "-g", "--game", type=str, required=True, help="The game we want to look profiles up for.", ) profile_parser.add_argument( "-v", "--version", type=str, required=True, help="The version we want to look profiles up for.", ) profile_parser.add_argument( "-t", "--type", type=str, required=True, choices=["card", "server"], help="The type of ID used to look up profiles.", ) profile_parser.add_argument( "id", metavar="ID", nargs="*", type=str, help="The ID we will look up profiles for.", ) # Statistics request statistic_parser = subparser.add_parser("statistics") statistic_parser.add_argument( "-g", "--game", type=str, required=True, help="The game we want to look statistics up for.", ) statistic_parser.add_argument( "-v", "--version", type=str, required=True, help="The version we want to look statistics up for.", ) statistic_parser.add_argument( "-t", "--type", type=str, required=True, choices=["card", "song", "instance", "server"], help="The type of ID used to look up statistics.", ) statistic_parser.add_argument( "id", metavar="ID", nargs="*", type=str, help="The ID we will look up statistics for.", ) # Catalog request catalog_parser = subparser.add_parser("catalog") catalog_parser.add_argument( "-g", "--game", type=str, required=True, help="The game we want to look catalog entries up for.", ) catalog_parser.add_argument( "-v", "--version", type=str, required=True, help="The version we want to look catalog entries up for.", ) # Grab args args = parser.parse_args() client = APIClient(args.base, args.token) if args.request == "info": client.info_exchange() elif args.request == "records": client.records_exchange( args.game, args.version, args.type, args.id, args.since, args.until, ) elif args.request == "profile": client.profile_exchange( args.game, args.version, args.type, args.id, ) elif args.request == "statistics": client.statistics_exchange( args.game, args.version, args.type, args.id, ) elif args.request == "catalog": client.catalog_exchange( args.game, args.version, ) else: raise Exception("Invalid request type {}!".format(args.request)) if __name__ == "__main__": try: main() except Exception as e: print(e, file=sys.stderr) sys.exit(1)