def _send(self, emsg, data={}, proto=None, jobid=None): if not isinstance(data, dict): raise ValueError("data kwarg can only be a dict") if proto is None: proto = find_proto(emsg) if proto is None or not issubclass(proto, google.protobuf.message.Message): raise ValueError( "Unable to find proto for emsg, or proto kwarg is invalid") message = proto() proto_fill_from_dict(message, data) header = GCMsgHdrProto(emsg) if jobid is not None: header.proto.job_id_source = jobid if self.verbose_debug: str_message = '' str_header = str(header) str_body = str(message) if str_header: str_message += "-- header ---------\n%s\n" % str_header if str_body: str_message += "-- message --------\n%s\n" % str_body self._LOG.debug("Outgoing: %s\n%s" % (repr(emsg), str_message)) else: self._LOG.debug("Outgoing: %s", repr(emsg)) GameCoordinator.send(self, header, message.SerializeToString())
def test_proto_from_dict_merge(self): self.msg.list_number32.extend([1, 2, 3]) utp.proto_fill_from_dict(self.msg, {'list_number32': [4, 5, 6]}, clear=False) self.assertEqual(self.msg.list_number32, [4, 5, 6])
def send_um(self, method_name, params=None): """Send service method request :param method_name: method name (e.g. ``Player.GetGameBadgeLevels#1``) :type method_name: :class:`str` :param params: message parameters :type params: :class:`dict` :return: ``job_id`` identifier :rtype: :class:`str` Listen for ``jobid`` on this object to catch the response. """ proto = get_um(method_name) if proto is None: raise ValueError("Failed to find method named: %s" % method_name) message = MsgProto(EMsg.ServiceMethodCallFromClient) message.header.target_job_name = method_name message.body = proto() if params: proto_fill_from_dict(message.body, params) return self.send_job(message)
def test_proto_fill_from_dict__func_generator(self): def number_gen(): yield 1 yield 2 yield 3 utp.proto_fill_from_dict(self.msg, {'list_number32': number_gen()}) self.assertEqual(self.msg.list_number32, [1, 2, 3])
def test_proto_fill_from_dict__dict_func_generator(self): def dict_gen(): yield {'text': 'one'} yield {'text': 'two'} utp.proto_fill_from_dict(self.msg, {'messages': dict_gen()}) self.assertEqual(len(self.msg.messages), 2) self.assertEqual(self.msg.messages[0].text, 'one') self.assertEqual(self.msg.messages[1].text, 'two')
def test_proto_fill_from_dict__dict_list(self): utp.proto_fill_from_dict( self.msg, {'messages': [{ 'text': 'one' }, { 'text': 'two' }]}) self.assertEqual(len(self.msg.messages), 2) self.assertEqual(self.msg.messages[0].text, 'one') self.assertEqual(self.msg.messages[1].text, 'two')
def test_proto_from_dict_merge_dict(self): self.msg.messages.add(text='one') self.msg.messages.add(text='two') utp.proto_fill_from_dict(self.msg, {'messages': [{ 'text': 'three' }]}, clear=False) self.assertEqual(len(self.msg.messages), 1) self.assertEqual(self.msg.messages[0].text, 'three')
def test_proto_fill_from_dict__dict_generator(self): utp.proto_fill_from_dict( self.msg, {'messages': (x for x in [{ 'text': 'one' }, { 'text': 'two' }])}) self.assertEqual(len(self.msg.messages), 2) self.assertEqual(self.msg.messages[0].text, 'one') self.assertEqual(self.msg.messages[1].text, 'two')
def test_proto_fill_from_dict__dict_filter(self): utp.proto_fill_from_dict(self.msg, { 'messages': filter(lambda x: True, [{ 'text': 'one' }, { 'text': 'two' }]) }) self.assertEqual(len(self.msg.messages), 2) self.assertEqual(self.msg.messages[0].text, 'one') self.assertEqual(self.msg.messages[1].text, 'two')
def test_proto_from_dict_to_dict(self): DATA = { 'buffers': [{ 'data': b'some data', 'flags': [{ 'flag': True }, { 'flag': False }, { 'flag': False }] }, { 'data': b'\x01\x02\x03\x04', 'flags': [{ 'flag': False }, { 'flag': True }, { 'flag': True }] }], 'list_number32': [ 4, 16, 64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, 67108864, 268435456, 1073741824 ], 'list_number64': [ 4, 64, 1024, 16384, 262144, 1125899906842624, 18014398509481984, 288230376151711744, 4611686018427387904 ], 'messages': [{ 'text': 'test string' }, { 'text': 'another one' }, { 'text': 'third' }], 'number32': 16777216, 'number64': 72057594037927936 } utp.proto_fill_from_dict(self.msg, DATA) RESULT = utp.proto_to_dict(self.msg) self.assertEqual(DATA, RESULT)
def send(self, message, body_params=None): """Send a message to CM :param message: a message instance :type message: :class:`.Msg`, :class:`.MsgProto` :param body_params: a dict with params to the body (only :class:`.MsgProto`) :type body_params: dict """ if not self.connected: self._LOG.debug("Trying to send message when not connected. (discarded)") else: if body_params and isinstance(message, MsgProto): proto_fill_from_dict(message.body, body_params) CMClient.send(self, message)
def change_status(self, **kwargs): """ Set name, persona state, flags .. note:: Changing persona state will also change :attr:`persona_state` :param persona_state: persona state (Online/Offline/Away/etc) :type persona_state: :class:`.EPersonaState` :param player_name: profile name :type player_name: :class:`str` :param persona_state_flags: persona state flags :type persona_state_flags: :class:`.EPersonaStateFlag` """ if not kwargs: return self.persona_state = kwargs.get('persona_state', self.persona_state) message = MsgProto(EMsg.ClientChangeStatus) proto_fill_from_dict(message.body, kwargs) self.send(message)
def test_proto_from_dict__dict_insteadof_list(self): with self.assertRaises(TypeError): utp.proto_fill_from_dict(self.msg, {'list_number32': [{}, {}]})
def get_product_info(self, apps=[], packages=[], timeout=15): """Get product info for apps and packages :param apps: items in the list should be either just ``app_id``, or :class:`dict` :type apps: :class:`list` :param packages: items in the list should be either just ``package_id``, or :class:`dict` :type packages: :class:`list` :return: dict with ``apps`` and ``packages`` containing their info, see example below :rtype: :class:`dict`, :class:`None` .. code:: python {'apps': {570: {...}, ...}, 'packages': {123: {...}, ...} } Access token is needed to access full information for certain apps, and also package info. Each app and package has its' own access token. If a token is required then ``_missing_token=True`` in the response. App access tokens are obtained by calling :meth:`get_access_tokens`, and are returned only when the account has a license for the specified app. Example code: .. code:: python result = client.get_product_info(apps=[123]) if result['apps'][123]['_missing_token']: tokens = client.get_access_token(apps=[123]) result = client.get_product_info(apps=[{'appid': 123, 'access_token': tokens['apps'][123] }]) .. note:: It is best to just request access token for all apps, before sending a product info request. Package tokens are located in the account license list. See :attr:`.licenses` .. code:: python result = client.get_product_info(packages=[{'packageid': 123, 'access_token': client.licenses[123].access_token, }]) """ if not apps and not packages: return message = MsgProto(EMsg.ClientPICSProductInfoRequest) for app in apps: app_info = message.body.apps.add() app_info.only_public = False if isinstance(app, dict): proto_fill_from_dict(app_info, app) else: app_info.appid = app for package in packages: package_info = message.body.packages.add() if isinstance(package, dict): proto_fill_from_dict(package_info, package) else: package_info.packageid = package message.body.meta_data_only = False job_id = self.send_job(message) data = dict(apps={}, packages={}) while True: chunk = self.wait_event(job_id, timeout=timeout, raises=True) chunk = chunk[0].body for app in chunk.apps: data['apps'][app.appid] = vdf.loads(app.buffer[:-1].decode('utf-8', 'replace'))['appinfo'] data['apps'][app.appid]['_missing_token'] = app.missing_token for pkg in chunk.packages: data['packages'][pkg.packageid] = vdf.binary_loads(pkg.buffer[4:]).get(str(pkg.packageid), {}) data['packages'][pkg.packageid]['_missing_token'] = pkg.missing_token if not chunk.response_pending: break return data
def test_proto_from_dict__list_insteadof_dict(self): with self.assertRaises(TypeError): utp.proto_fill_from_dict(self.msg, {'messages': [1, 2, 3]})
def get_product_info(self, apps=[], packages=[], meta_data_only=False, raw=False, auto_access_tokens=True, timeout=15): """Get product info for apps and packages :param apps: items in the list should be either just ``app_id``, or :class:`dict` :type apps: :class:`list` :param packages: items in the list should be either just ``package_id``, or :class:`dict` :type packages: :class:`list` :param meta_data_only: only meta data will be returned in the reponse (e.g. change number, missing_token, sha1) :type meta_data_only: :class:`bool` :param raw: Data buffer for each app is returned as bytes in its' original form. Apps buffer is text VDF, and package buffer is binary VDF :type raw: :class:`bool` :param auto_access_token: automatically request and fill access tokens :type auto_access_token: :class:`bool` :return: dict with ``apps`` and ``packages`` containing their info, see example below :rtype: :class:`dict`, :class:`None` .. code:: python {'apps': {570: {...}, ...}, 'packages': {123: {...}, ...} } Access token is needed to access full information for certain apps, and also package info. Each app and package has its' own access token. If a token is required then ``_missing_token=True`` in the response. App access tokens are obtained by calling :meth:`get_access_tokens`, and are returned only when the account has a license for the specified app. Example code: .. code:: python result = client.get_product_info(apps=[123]) if result['apps'][123]['_missing_token']: tokens = client.get_access_token(apps=[123]) result = client.get_product_info(apps=[{'appid': 123, 'access_token': tokens['apps'][123] }]) .. note:: It is best to just request access token for all apps, before sending a product info request. Package tokens are located in the account license list. See :attr:`.licenses` .. code:: python result = client.get_product_info(packages=[{'packageid': 123, 'access_token': client.licenses[123].access_token, }]) """ if not apps and not packages: return if auto_access_tokens: tokens = self.get_access_tokens( app_ids=list( map( lambda app: app['appid'] if isinstance(app, dict) else app, apps)), package_ids=list( map( lambda pkg: pkg['packageid'] if isinstance(pkg, dict) else pkg, packages))) else: tokens = None message = MsgProto(EMsg.ClientPICSProductInfoRequest) for app in apps: app_info = message.body.apps.add() if tokens: app_info.access_token = tokens['apps'].get( app['appid'] if isinstance(app, dict) else app, 0) if isinstance(app, dict): proto_fill_from_dict(app_info, app) else: app_info.appid = app for package in packages: package_info = message.body.packages.add() if tokens: package_info.access_token = tokens['packages'].get( package['packageid'] if isinstance(package, dict) else package, 0) if isinstance(package, dict): proto_fill_from_dict(package_info, package) else: package_info.packageid = package message.body.meta_data_only = meta_data_only message.body.num_prev_failed = 0 message.body.supports_package_tokens = 1 job_id = self.send_job(message) data = dict(apps={}, packages={}) while True: chunk = self.wait_event(job_id, timeout=timeout, raises=True) chunk = chunk[0].body for app in chunk.apps: if app.buffer and not raw: data['apps'][app.appid] = vdf.loads(app.buffer[:-1].decode( 'utf-8', 'replace'))['appinfo'] else: data['apps'][app.appid] = {} data['apps'][app.appid]['_missing_token'] = app.missing_token data['apps'][app.appid]['_change_number'] = app.change_number data['apps'][app.appid]['_sha'] = hexlify( app.sha).decode('ascii') data['apps'][app.appid]['_size'] = app.size if app.buffer and raw: data['apps'][app.appid]['_buffer'] = app.buffer for pkg in chunk.packages: if pkg.buffer and not raw: data['packages'][pkg.packageid] = vdf.binary_loads( pkg.buffer[4:]).get(str(pkg.packageid), {}) else: data['packages'][pkg.packageid] = {} data['packages'][ pkg.packageid]['_missing_token'] = pkg.missing_token data['packages'][ pkg.packageid]['_change_number'] = pkg.change_number data['packages'][pkg.packageid]['_sha'] = hexlify( pkg.sha).decode('ascii') data['packages'][pkg.packageid]['_size'] = pkg.size if pkg.buffer and raw: data['packages'][pkg.packageid]['_buffer'] = pkg.buffer if not chunk.response_pending: break return data
def test_proto_fill_from_dict__list(self): utp.proto_fill_from_dict(self.msg, {'list_number32': range(10)}) self.assertEqual(self.msg.list_number32, list(range(10)))
def test_proto_fill_from_dict__filter(self): utp.proto_fill_from_dict( self.msg, {'list_number32': filter(lambda x: True, [1, 2, 3])}) self.assertEqual(self.msg.list_number32, [1, 2, 3])
def test_proto_fill_from_dict__map(self): utp.proto_fill_from_dict(self.msg, {'list_number32': map(int, [1, 2, 3])}) self.assertEqual(self.msg.list_number32, [1, 2, 3])
def test_proto_fill_from_dict__generator(self): utp.proto_fill_from_dict(self.msg, {'list_number32': (x for x in [1, 2, 3])}) self.assertEqual(self.msg.list_number32, [1, 2, 3])
def get_product_info(self, apps=[], packages=[], timeout=15): """Get product info for apps and packages :param apps: items in the list should be either just ``app_id``, or :class:`dict` :type apps: :class:`list` :param packages: items in the list should be either just ``package_id``, or :class:`dict` :type packages: :class:`list` :return: dict with ``apps`` and ``packages`` containing their info, see example below :rtype: :class:`dict`, :class:`None` .. code:: python {'apps': {570: {...}, ...}, 'packages': {123: {...}, ...} } When a token is needed to access the full info (e.g. branches and depots) the ``_missing_token`` will be set to ``True``. The token can be obtained by calling :meth:`get_access_tokens` if the account has a license. .. code:: python result = client.get_product_info(apps=[123]) if result['apps'][123]['_missing_token']: tokens = client.get_access_token(apps=[123]) result = client.get_product_info(apps={'appid': 123, 'access_token': tokens['apps'][123] }) """ if not apps and not packages: return message = MsgProto(EMsg.ClientPICSProductInfoRequest) for app in apps: app_info = message.body.apps.add() app_info.only_public = False if isinstance(app, dict): proto_fill_from_dict(app_info, app) else: app_info.appid = app for package in packages: package_info = message.body.packages.add() if isinstance(package, dict): proto_fill_from_dict(package_info, package) else: package_info.packageid = package message.body.meta_data_only = False job_id = self.send_job(message) data = dict(apps={}, packages={}) while True: chunk = self.wait_event(job_id, timeout=timeout, raises=True) chunk = chunk[0].body for app in chunk.apps: data['apps'][app.appid] = vdf.loads(app.buffer[:-1].decode( 'utf-8', 'replace'))['appinfo'] data['apps'][app.appid]['_missing_token'] = app.missing_token for pkg in chunk.packages: data['packages'][pkg.packageid] = vdf.binary_loads( pkg.buffer[4:])[str(pkg.packageid)] data['packages'][ pkg.packageid]['_missing_token'] = pkg.missing_token if not chunk.response_pending: break return data