Browse Source
* Removed SteamClient.unified_messages * Added steam.exceptions.SteamErrorpull/202/head
9 changed files with 123 additions and 184 deletions
@ -1,161 +1,79 @@ |
|||
""" |
|||
:class:`SteamUnifiedMessages` provides a simply API to send and receive unified messages. |
|||
Methods to call service methods, also known as unified messages |
|||
|
|||
Example code: |
|||
|
|||
.. code:: python |
|||
|
|||
# the easy way |
|||
response = client.unified_messages.send_and_wait('Player.GetGameBadgeLevels#1', { |
|||
response = client.send_um_and_wait('Player.GetGameBadgeLevels#1', { |
|||
'property': 1, |
|||
'something': 'value', |
|||
}) |
|||
|
|||
print(response.body) |
|||
|
|||
# the other way |
|||
jobid = client.unified_message.send('Player.GetGameBadgeLevels#1', {'something': 1}) |
|||
response, = client.unified_message.wait_event(jobid) |
|||
jobid = client.send_um('Player.GetGameBadgeLevels#1', {'something': 1}) |
|||
response = client.wait_event(jobid) |
|||
|
|||
The backend might error out, but we still get response. Here is how to check for error: |
|||
|
|||
.. code:: python |
|||
|
|||
if response.header.eresult != EResult.OK: |
|||
print(response.header.error_message) |
|||
|
|||
# i know what im doing, alright? |
|||
message = client.unified_message.get('Player.GetGameBadgeLevels#1') |
|||
message.something = 1 |
|||
response = client.unified_message.send_and_wait(message) |
|||
""" |
|||
import logging |
|||
from eventemitter import EventEmitter |
|||
from steam.core.msg import MsgProto, get_um |
|||
from steam.enums import EResult |
|||
from steam.enums.emsg import EMsg |
|||
from steam.util import WeakRefKeyDict, proto_fill_from_dict |
|||
from steam.util import proto_fill_from_dict |
|||
|
|||
|
|||
class UnifiedMessages(object): |
|||
def __init__(self, *args, **kwargs): |
|||
super(UnifiedMessages, self).__init__(*args, **kwargs) |
|||
|
|||
name = "%s.unified_messages" % self.__class__.__name__ |
|||
self.unified_messages = SteamUnifiedMessages(self, name) #: instance of :class:`SteamUnifiedMessages` |
|||
|
|||
|
|||
class UnifiedMessageError(Exception): |
|||
def __init__(self, message, eresult=EResult.Invalid): |
|||
self.eresult = eresult |
|||
self.message = message |
|||
|
|||
def __repr__(self): |
|||
return "%s(%s, %s)" % (self.__class__.__name__, self.eresult, repr(self.message)) |
|||
|
|||
def __str__(self): |
|||
return "(%s) %s" % (self.eresult, self.message) |
|||
|
|||
|
|||
|
|||
class SteamUnifiedMessages(EventEmitter): |
|||
"""Simple API for send/recv of unified messages |
|||
|
|||
Incoming messages are emitted as events once with their ``jobid`` |
|||
and once with their method name (e.g. ``Player.GetGameBadgeLevels#1``) |
|||
""" |
|||
def __init__(self, steam, logger_name=None): |
|||
self._LOG = logging.getLogger(logger_name if logger_name else self.__class__.__name__) |
|||
self._steam = steam |
|||
self._data = WeakRefKeyDict() |
|||
|
|||
steam.on(EMsg.ServiceMethod, self._handle_service_method) |
|||
steam.on(EMsg.ClientServiceMethodResponse, self._handle_client_service_method) |
|||
|
|||
def emit(self, event, *args): |
|||
if event is not None: |
|||
self._LOG.debug("Emit event: %s" % repr(event)) |
|||
EventEmitter.emit(self, event, *args) |
|||
|
|||
def _handle_service_method(self, message): |
|||
self.emit(message.header.target_job_name, message.body) |
|||
|
|||
def _handle_client_service_method(self, message): |
|||
method_name = message.body.method_name |
|||
proto = get_um(method_name, response=True) |
|||
|
|||
if proto is None: |
|||
self._LOG.error("Unable to find proto for %s" % repr(method_name)) |
|||
return |
|||
|
|||
error = None |
|||
if message.header.eresult != EResult.OK: |
|||
error = UnifiedMessageError(message.header.error_message, |
|||
EResult(message.header.eresult), |
|||
) |
|||
|
|||
resp = proto() |
|||
resp.ParseFromString(message.body.serialized_method_response) |
|||
|
|||
self.emit(method_name, resp, error) |
|||
|
|||
jobid = message.header.jobid_target |
|||
if jobid not in (-1, 18446744073709551615): |
|||
self.emit("job_%d" % jobid, resp, error) |
|||
|
|||
def get(self, method_name): |
|||
"""Get request proto instance for given methed name |
|||
|
|||
:param method_name: name for the method (e.g. ``Player.GetGameBadgeLevels#1``) |
|||
:type method_name: :class:`str` |
|||
:return: proto message instance, or :class:`None` if not found |
|||
""" |
|||
proto = get_um(method_name) |
|||
if proto is None: |
|||
return None |
|||
message = proto() |
|||
self._data[message] = method_name |
|||
return message |
|||
|
|||
def send(self, message, params=None): |
|||
def send_um(self, method_name, params=None): |
|||
"""Send service method request |
|||
|
|||
:param message: |
|||
proto message instance (use :meth:`SteamUnifiedMessages.get`) |
|||
or method name (e.g. ``Player.GetGameBadgeLevels#1``) |
|||
:type message: :class:`str`, proto message instance |
|||
:param method_name: method name (e.g. ``Player.GetGameBadgeLevels#1``) |
|||
:type method_name: :class:`str` |
|||
:param params: message parameters |
|||
:type params: :class:`dict` |
|||
:return: ``jobid`` event identifier |
|||
:type params: :class:`dict` |
|||
:return: ``job_id`` identifier |
|||
:rtype: :class:`str` |
|||
|
|||
Listen for ``jobid`` on this object to catch the response. |
|||
|
|||
.. note:: |
|||
If you listen for ``jobid`` on the client instance you will get the encapsulated message |
|||
""" |
|||
if isinstance(message, str): |
|||
message = self.get(message) |
|||
if message not in self._data: |
|||
raise ValueError("Supplied message is invalid. Use 'get' method.") |
|||
proto = get_um(method_name) |
|||
|
|||
if params: |
|||
proto_fill_from_dict(message, params) |
|||
if proto is None: |
|||
raise ValueError("Failed to find method named: %s" % method_name) |
|||
|
|||
capsule = MsgProto(EMsg.ClientServiceMethod) |
|||
capsule.body.method_name = self._data[message] |
|||
capsule.body.serialized_method = message.SerializeToString() |
|||
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._steam.send_job(capsule) |
|||
return self.send_job(message) |
|||
|
|||
def send_and_wait(self, message, params=None, timeout=10, raises=False): |
|||
def send_um_and_wait(self, method_name, params=None, timeout=10, raises=False): |
|||
"""Send service method request and wait for response |
|||
|
|||
:param message: |
|||
proto message instance (use :meth:`SteamUnifiedMessages.get`) |
|||
or method name (e.g. ``Player.GetGameBadgeLevels#1``) |
|||
:type message: :class:`str`, proto message instance |
|||
:param method_name: method name (e.g. ``Player.GetGameBadgeLevels#1``) |
|||
:type method_name: :class:`str` |
|||
:param params: message parameters |
|||
:type params: :class:`dict` |
|||
:type params: :class:`dict` |
|||
:param timeout: (optional) seconds to wait |
|||
:type timeout: :class:`int` |
|||
:type timeout: :class:`int` |
|||
:param raises: (optional) On timeout if :class:`False` return :class:`None`, else raise :class:`gevent.Timeout` |
|||
:type raises: :class:`bool` |
|||
:return: response proto message instance |
|||
:rtype: (proto message, :class:`.UnifiedMessageError`) |
|||
:type raises: :class:`bool` |
|||
:return: response message |
|||
:rtype: proto message instance |
|||
:raises: :class:`gevent.Timeout` |
|||
""" |
|||
job_id = self.send(message, params) |
|||
resp = self.wait_event(job_id, timeout, raises=raises) |
|||
return (None, None) if resp is None else resp |
|||
job_id = self.send_um(method_name, params) |
|||
return self.wait_msg(job_id, timeout, raises=raises) |
|||
|
@ -0,0 +1,7 @@ |
|||
|
|||
from steam.enums import EResult |
|||
|
|||
class SteamError(Exception): |
|||
def __init__(self, message, eresult=EResult.Fail): |
|||
Exception.__init__(self, message) |
|||
self.eresult = EResult(eresult) #: :class:`.EResult` |
Loading…
Reference in new issue