Пример #1
0
    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())
Пример #2
0
    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)
Пример #4
0
    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])
Пример #5
0
    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')
Пример #6
0
 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')
Пример #7
0
    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')
Пример #8
0
 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')
Пример #9
0
 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')
Пример #10
0
    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)
Пример #11
0
    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)
Пример #12
0
    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)
Пример #13
0
 def test_proto_from_dict__dict_insteadof_list(self):
     with self.assertRaises(TypeError):
         utp.proto_fill_from_dict(self.msg, {'list_number32': [{}, {}]})
Пример #14
0
    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
Пример #15
0
 def test_proto_from_dict__list_insteadof_dict(self):
     with self.assertRaises(TypeError):
         utp.proto_fill_from_dict(self.msg, {'messages': [1, 2, 3]})
Пример #16
0
    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
Пример #17
0
 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)))
Пример #18
0
 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])
Пример #19
0
 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])
Пример #20
0
 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])
Пример #21
0
    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