Exemplo n.º 1
0
 def has_cached_app_depot_info(self, app_id):
     if app_id in self.app_depots:
         return True
     cached_appinfo = UserCacheFile("appinfo/{}.json".format(app_id))
     if cached_appinfo.exists():
         return True
     return False
Exemplo n.º 2
0
    def get_manifest(self, app_id, depot_id, manifest_gid, decrypt=True):
        key = (app_id, depot_id, manifest_gid)
        cached_manifest = UserCacheFile("manifests/{}_{}_{}".format(*key))

        if key not in self.manifests:
            self.get_cached_manifest(*key)

        # if we still dont have a manifest, load it from CDN
        if key not in self.manifests:
            manifest = CDNClient.get_manifest(self, app_id, depot_id,
                                              manifest_gid, decrypt)

            # cache the manifest
            with cached_manifest.open('wb') as fp:
                # if we have the key decrypt the manifest before caching
                if manifest.filenames_encrypted and manifest.depot_id in self.depot_keys:
                    manifest = self.DepotManifestClass(
                        self, manifest.app_id,
                        manifest.serialize(compress=False))
                    manifest.decrypt_filenames(
                        self.depot_keys[manifest.depot_id])

                fp.write(manifest.serialize(compress=False))

        return self.manifests[key]
Exemplo n.º 3
0
    def get_product_info(self, apps=[], packages=[], *args, **kwargs):
        resp = {'apps': {}, 'packages': {}}

        # if we have cached info for all apps, just serve from cache
        if apps and all(map(self.has_cached_appinfo, apps)):
            self._LOG.debug("Serving appinfo from cache")

            for app_id in apps:
                resp['apps'][app_id] = self.get_cached_appinfo(app_id)

            apps = []

        if apps or packages:
            self._LOG.debug("Fetching product info")
            fresh_resp = SteamClient.get_product_info(self, apps, packages,
                                                      *args, **kwargs)

            if apps:
                for app_id, appinfo in fresh_resp['apps'].items():
                    if not appinfo['_missing_token']:
                        UserCacheFile("appinfo/{}.json".format(
                            app_id)).write_json(appinfo)
                resp = fresh_resp
            else:
                resp['packages'] = fresh_resp['packages']

        return resp
Exemplo n.º 4
0
    def endpoint_autocomplete(prefix, parsed_args, **kwargs):
        interfaces = UserCacheFile('webapi_interfaces.json').read_json()

        if not interfaces:
            warn("To enable endpoint tab completion run: steamctl webapi list")
            return []

        return ('{}.{}'.format(a['name'], b['name']) for a in interfaces for b in a['methods'])
Exemplo n.º 5
0
    def check_for_changes(self):
        changefile = UserCacheFile('last_change_number')
        change_number = 0

        if changefile.exists():
            try:
                change_number = int(changefile.read_full())
            except:
                changefile.remove()

        self._LOG.debug("Checking PICS for app changes")
        resp = self.steam.get_changes_since(change_number, True, False)

        if resp.force_full_app_update:
            change_number = 0

        if resp.current_change_number != change_number:
            with changefile.open('w') as fp:
                fp.write(str(resp.current_change_number))

            changed_apps = set((entry.appid for entry in resp.app_changes))

            if change_number == 0 or changed_apps:
                self._LOG.debug("Checking for outdated cached appinfo files")

                for appinfo_file in UserCacheDirectory('appinfo').iter_files(
                        '*.json'):
                    app_id = int(appinfo_file.filename[:-5])

                    if change_number == 0 or app_id in changed_apps:
                        appinfo_file.remove()
Exemplo n.º 6
0
    def get_manifest(self, app_id, depot_id, manifest_gid, decrypt=True):
        key = (app_id, depot_id, manifest_gid)
        cached_manifest = UserCacheFile("manifests/{}_{}_{}".format(*key))

        if decrypt and depot_id not in self.depot_keys:
            self.get_depot_key(app_id, depot_id)

        manifest = self.get_cached_manifest(*key)

        # if manifest not cached, download from CDN
        if not manifest:
            manifest = CDNClient.get_manifest(self, app_id, depot_id, manifest_gid, decrypt)

            # cache the manifest
            with cached_manifest.open('wb') as fp:
                fp.write(manifest.serialize(compress=False))

        return self.manifests[key]
Exemplo n.º 7
0
    def get_cached_manifest(self, app_id, depot_id, manifest_gid):
        key = (app_id, depot_id, manifest_gid)

        if key in self.manifests:
            return self.manifests[key]

        # if we don't have the manifest loaded, check cache
        cached_manifest = UserCacheFile("manifests/{}_{}_{}".format(app_id, depot_id, manifest_gid))

        # we have a cached manifest file, load it
        if cached_manifest.exists():
            with cached_manifest.open('r+b') as fp:
                try:
                    manifest = self.DepotManifestClass(self, app_id, fp.read())
                except Exception as exp:
                    self._LOG.debug("Error parsing cached manifest: %s", exp)
                else:
                    # if its not empty, load it
                    if manifest.gid > 0:
                        self.manifests[key] = manifest

                        # update cached file if we have depot key for it
                        if manifest.filenames_encrypted and manifest.depot_id in self.depot_keys:
                            manifest.decrypt_filenames(self.depot_keys[manifest.depot_id])
                            fp.seek(0)
                            fp.write(manifest.serialize(compress=False))
                            fp.truncate()

                        return manifest

            # empty manifest files shouldn't exist, handle it gracefully by removing the file
            if key not in self.manifests:
                self._LOG.debug("Found cached manifest, but encountered error or file is empty")
                cached_manifest.remove()
Exemplo n.º 8
0
    def get_app_depot_info(self, app_id):
        if app_id not in self.app_depots:
            cached_appinfo = UserCacheFile("appinfo/{}.json".format(app_id))
            appinfo = None

            if cached_appinfo.exists():
                appinfo = cached_appinfo.read_json()

            if not appinfo:
                app_req = {'appid': app_id}
                token = self.get_app_access_token(app_id)

                if token:
                    app_req['access_token'] = token

                try:
                    appinfo = self.steam.get_product_info([app_req])['apps'][app_id]
                except KeyError:
                    raise SteamError("Invalid app id")

                if appinfo['_missing_token']:
                    raise SteamError("No access token available")

                cached_appinfo.write_json(appinfo)

            self.app_depots[app_id] = appinfo.get('depots', {})

        return self.app_depots[app_id]
Exemplo n.º 9
0
    def parameter_autocomplete(prefix, parsed_args, **kwargs):
        interfaces = UserCacheFile('webapi_interfaces.json').read_json()

        if not interfaces:
            warn("To enable endpoint tab completion run: steamctl webapi list")
            return []

        parameters = []
        ainterface, amethod = parsed_args.endpoint.split('.', 1)

        for interface in filter(lambda a: a['name'] == ainterface, interfaces):
            for method in filter(lambda b: b['name'] == amethod, interface['methods']):
                for param in method['parameters']:
                    if param['name'][-3:] == '[0]':
                        param['name'] = param['name'][:-3]

                    parameters.append(param['name'] + '=')
                break

        return parameters
Exemplo n.º 10
0
    def get_app_depot_info(self, app_id):
        if app_id not in self.app_depots:
            cached_appinfo = UserCacheFile("appinfo/{}.json".format(app_id))
            appinfo = None

            if cached_appinfo.exists():
                appinfo = cached_appinfo.read_json()

            if not appinfo:
                appinfo = self.steam.get_product_info([app_id])['apps'][app_id]
                cached_appinfo.write_json(appinfo)

            self.app_depots[app_id] = appinfo['depots']

        return self.app_depots[app_id]
Exemplo n.º 11
0
def get_app_names():
    papps = SqliteDict(UserCacheFile("app_names.sqlite3").path)

    try:
        last = int(papps[-7])  # use a key that will never be used
    except KeyError:
        last = 0

    if last < time():
        resp = webapi.get('ISteamApps', 'GetAppList', version=2)
        apps = resp.get('applist', {}).get('apps', [])

        if not apps and len(papps) == 0:
            raise RuntimeError("Failed to fetch apps")

        for app in apps:
            papps[int(app['appid'])] = app['name']

        papps[-7] = str(int(time()) + 86400)
        papps.commit()

    return papps
Exemplo n.º 12
0
    def get_cached_appinfo(self, app_id):
        cache_file = UserCacheFile("appinfo/{}.json".format(app_id))

        if cache_file.exists():
            return cache_file.read_json()
Exemplo n.º 13
0
 def has_cached_appinfo(self, app_id):
     return UserCacheFile("appinfo/{}.json".format(app_id)).exists()
Exemplo n.º 14
0
def cmd_webapi_call(args):
    # load key=value pairs. Stuff thats start with [ is a list, so parse as json
    try:
        params = {
            k: (json.loads(v) if v[0:1] == '[' else v)
            for k, v in args.params
        }
    except Exception as exp:
        LOG.error("Error parsing params: %s", str(exp))
        return 1  # error

    apicall = webapi.get
    version = args.version or 1

    if args.method == 'POST':
        apicall = webapi.post

    webapi_map = {}

    # load cache webapi_interfaces if available
    for interface in (UserCacheFile('webapi_interfaces.json').read_json()
                      or {}):
        for method in interface['methods']:
            key = "{}.{}".format(interface['name'], method['name'])

            if key not in webapi_map or webapi_map[key][1] < method['version']:
                webapi_map[key] = method['httpmethod'], method['version']

    # if --method or --version are unset, take them the cache
    # This will the call POST if needed with specifying explicity
    # This will prever the highest version of a method
    if args.endpoint in webapi_map:
        if args.method is None:
            if webapi_map[args.endpoint][0] == 'POST':
                apicall = webapi.post
        if args.version is None:
            version = webapi_map[args.endpoint][1]

    # drop reserved words. these have special meaning for steam.webapi
    for reserved in ('key', 'format', 'raw', 'http_timeout', 'apihost',
                     'https'):
        params.pop(reserved, None)

    # load key if available
    params.setdefault('key', args.apikey or get_webapi_key())

    if args.format != 'text':
        params[
            'format'] = 'json' if args.format == 'json_line' else args.format
        params['raw'] = True

    try:
        interface, method = args.endpoint.split('.', 1)
        resp = apicall(interface, method, version, params=params)
    except Exception as exp:
        LOG.error("%s failed: %s", args.endpoint, str(exp))
        if getattr(exp, 'response', None):
            LOG.error("Response body: %s", exp.response.text)
        return 1  # error

    # by default we print json, other formats are shown as returned from api
    if args.format == 'json':
        json.dump(json.loads(resp.rstrip('\n\t\x00 ')),
                  sys.stdout,
                  indent=4,
                  sort_keys=True)
        print('')
    else:
        print(resp)
Exemplo n.º 15
0
def cmd_webapi_list(args):
    params = {}
    params.setdefault('key', args.apikey or get_webapi_key())

    if args.format != 'text':
        params[
            'format'] = 'json' if args.format == 'json_line' else args.format
        params['raw'] = True

    try:
        resp = webapi.get('ISteamWebAPIUtil',
                          'GetSupportedAPIList',
                          params=params)

        if args.format == 'text':
            interfaces = resp['apilist']['interfaces']
            UserCacheFile('webapi_interfaces.json').write_json(interfaces)
    except Exception as exp:
        LOG.error("GetSupportedAPIList failed: %s", str(exp))
        if getattr(exp, 'response', None):
            LOG.error("Response body: %s", exp.response.text)
        return 1  # error

    if args.format != 'text':
        if args.format == 'json':
            json.dump(json.loads(resp), sys.stdout, indent=4, sort_keys=True)
            print('')
        else:
            print(resp)
        return

    for interface in interfaces:
        for method in interface['methods']:
            if args.search:
                if args.search.lower() not in "{}.{}".format(
                        interface['name'], method['name']).lower():
                    continue

            out = "{:>4} {}.{} v{} {}".format(
                method['httpmethod'],
                interface['name'],
                method['name'],
                method['version'],
                ('- ' +
                 method['description']) if 'description' in method else '',
            )

            print(out)

            if args.verbose:
                for param in method.get('parameters', []):
                    name = param['name']
                    if name[-3:] == '[0]':
                        name = name[:-3]

                    print("       {:<10} {}{:<10} {}".format(
                        param['type'],
                        ' ' if param['optional'] else '*',
                        name,
                        ('- ' + param['description'])
                        if 'description' in param else '',
                    ))

                print('')