Browse Source

Added webpresence.py

pull/35/head
philippj 9 years ago
parent
commit
c6be934eec
  1. 363
      steam/webpresence.py

363
steam/webpresence.py

@ -0,0 +1,363 @@
# -*- coding: utf-8 -*-
"""
Wrapping methods for `ISteamWebUserPresenceOAuth` and `Polling` functionality
Example usage:
.. code:: python
import steam.webauth
import steam.webpresence
webAuth = steam.webauth.MobileWebAuth('username', 'password')
webAuth.login()
def my_callback(messages):
for message in messages:
print '[%s] %s from %s' % (message.timestamp, message.type, message.steamid_from.as_64)
webPresence = steam.webpresence.WebUserPresence(webAuth)
webPresence.logon()
webPresence.start_polling(my_callback)
print 'Started polling'
"""
import threading
import requests
from steam.webauth import MobileWebAuth
from steam import SteamID
DEFAULT_MOBILE_HEADERS = {
'X-Requested-With': 'com.valvesoftware.android.steam.community',
'User-agent': 'Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) \
AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30'
}
API_BASE = 'https://api.steampowered.com'
DEFAULT_TIMEOUT = 30
PERSONA_STATES = ['Offline', 'Online', 'Busy', 'Away', 'Snooze', 'Looking to trade', 'Looking to play']
class WebUserPresence:
"""
Wrapping methods for `ISteamWebUserPresenceOAuth` and `Polling` functionality
"""
_loggedon = False
_timeout = None
_oauth_token = None
_session = None
_steamid = None
_umqid = None
_message_base = None
def __init__(self, mobile_web_auth, timeout=None):
if not isinstance(mobile_web_auth, MobileWebAuth):
raise InvalidInstanceSupplied('The instance supplied as parameter is no valid instance of `MobileWebAuth`.')
if not mobile_web_auth.complete:
raise InstanceNotReady('Please make sure your `MobileWebAuth` instance is logged in.')
self._timeout = timeout
self._oauth_token = mobile_web_auth.oauth_token
self._session = mobile_web_auth.session
def _request(self, uri, data, timeout=None):
"""
HTTP Request
:param uri: URI to be requested
:param data: Data to be delivered
:param timeout: HTTP timeout
:return: `requests.Response`
"""
timeout = timeout or self._timeout or DEFAULT_TIMEOUT
return self._session.post(uri, data=data, headers=DEFAULT_MOBILE_HEADERS, timeout=timeout)
def _call(self, method, data, timeout=None):
"""
Calling an `ISteamWebUserPresenceOAuth` api method
:param method: Method to be called
:param data: Data to be delivered
:param timeout: HTTP timeout
:return: If successful, response as tuple, otherwise exceptions are thrown
"""
uri = '%s/ISteamWebUserPresenceOAuth/%s/v1/' % (API_BASE, method)
try:
response = self._request(uri, data, timeout=timeout)
except requests.exceptions.ReadTimeout:
raise HTTPError('Timeout')
if response.status_code == 401:
raise NotAuthorized('Not authorized. Please check your OAuth token and verify your `MobileWebAuth` login.')
elif response.status_code != 200:
raise HTTPError('HTTP request failed. Status code: %s' % response.status_code)
if self._loggedon:
self._message_base += 1
try:
json_response = response.json()
except:
raise ValueError('Could not build json_response')
else:
return json_response
def logon(self):
"""
Sends logon to the api
:return: True if successful. Raises an `LogonFailed` exception otherwise
"""
login_data = {
'access_token': self._oauth_token
}
response = self._call('Logon', login_data)
if response.get('error') == 'OK':
self._steamid = SteamID(response.get('steamid'))
self._umqid = response.get('umqid')
self._message_base = response.get('message')
self._loggedon = True
return True
else:
raise LogonFailed('Logon failed. Please check your OAuth token and verify your `MobileWebAuth` login.')
def logoff(self):
"""
Sends logoff to the api
:return: True if logoff was successful, False otherwise.
"""
response = self._call('Logoff', self._build_call_data({}), True)
self._loggedon = False
return response.get('error') == 'OK' or False
def poll(self):
"""
Starts an poll request
:return: Full response as Tuple
"""
poll_data = {
'pollid': 0,
'sectimeout': 5,
'secidletime': 0,
'use_accountids': 1
}
response = self._call('Poll', self._build_call_data(poll_data, True), timeout=60)
if response.get('error') == 'OK':
return response
else:
raise PollCreationFailed(response.get('error'))
def start_polling(self, callback):
"""
Creates an instance of `WebPolling` and starts the thread
:param callback: Function reference for handling `WebPollMessages`
:return: True
"""
web_polling = self._spawn_web_polling(callback)
web_polling.start()
return True
def stop_polling(self):
"""
Stops the active `WebPolling`
:return: True, False.
"""
return self._kill_web_polling()
def message(self, steamid, text):
"""
Delivers a steam message
:param steamid: SteamID64 of the target user
:param text: Message to deliver
:return: True if deliver was successful, False otherwise
"""
message_data = self._build_call_data({
'text': text,
'type': 'saytext',
'steamid_dst': steamid
})
response = self._call('Message', message_data)
return response.get('error') == 'OK' or False
def _spawn_web_polling(self, callback):
"""
Spawns a `WebPolling` instance
:param callback: A method reference for handling incoming `WebPollMessages`
:return: Instance reference
"""
self._web_polling = WebPolling(self, callback)
return self._web_polling
def _kill_web_polling(self):
"""
Stops the run() method of WebPolling after the current poll request
:return: True if `_web_polling` is spawned and could be stopped, False otherwise
"""
if getattr(self, '_web_polling'):
self._web_polling._active = False
return True
return False
def _prep_messages(self, messages):
"""
Prepares incoming raw `WebPolling` messages by creating `WebPollMessage` instances
:param messages: raw `WebPolling` messages
:return: List of `WebPollMessages` instances
"""
prepped_messages = [ ]
for message in messages:
prepped_messages.append(WebPollMessage(message, self))
return prepped_messages
def _build_call_data(self, data, set_message_base=False):
"""
Builds the data for `_call`'s
:param data: Data to deliver as Tuple
:param set_message_base: True, False. Only required for calls where `message` has to be set
:return: Prepared call data
"""
base_data = {
'umqid': self._umqid,
'access_token': self._oauth_token
}
if set_message_base:
base_data.__setitem__('message', self._message_base)
for key in data:
base_data.__setitem__(key, data.get(key))
return base_data
class WebPolling(threading.Thread):
"""
Threaded class for `Polling`
"""
_active = True
_web_user_presence = None
_callback = None
def __init__(self, web_user_presence, callback):
threading.Thread.__init__(self)
if not isinstance(web_user_presence, WebUserPresence):
raise InvalidInstanceSupplied('The instance supplied as parameter is no valid instance of `WebUserPresence`.')
if not web_user_presence._loggedon:
raise InstanceNotReady('The `WebUserPresence` has to be logged on.')
self._web_user_presence = web_user_presence
self._callback = callback
self.setDaemon(False)
def run(self):
"""
Sends HTTP requests while class is `_active` and calls a specified callback
:return:
"""
while self._active:
try:
response = self._web_user_presence.poll()
except PollCreationFailed:
"""
Ignore timeout exception
"""
pass
except HTTPError:
"""
Ignore http exceptions
"""
pass
else:
prepped_messages = self._web_user_presence._prep_messages(response.get('messages'))
if self._callback:
self._callback(prepped_messages)
class WebPollMessage(object):
"""
Class for proper handling of polling messages
"""
complete = False
_web_user_presence = None
timestamp = 0
type = None
steamid_from = SteamID()
text = None
persona_state = 0
persona_name = None
status_flags = None
_answerable = False
_full_data = None
def __init__(self, message, web_user_presence):
if not isinstance(web_user_presence, WebUserPresence):
raise InvalidInstanceSupplied('The instance supplied as parameter is no valid instance of `WebUserPresence`.')
if not web_user_presence._loggedon:
raise InstanceNotReady('The `WebUserPresence` has to be logged on.')
self._full_data = message
self._web_user_presence = web_user_presence
self.timestamp = message.get('utc_timestamp')
self.type = message.get('type')
self.steamid_from = SteamID(message.get('accountid_from'))
if self.type == 'typing':
self._answerable = True
elif self.type == 'saytext':
self._answerable = True
self.text = message.get('text')
elif self.type == 'personastate':
self.persona_name = message.get('persona_name')
self.persona_state = message.get('persona_state')
self.status_flags = message.get('status_flags')
def answer(self, message):
"""
If the current message is `_answerable` ( current message has something to do with the steam chat ) an
answer can be sent
:param message: Message to be delivered
:return: True if message could be delivered, False otherwise
"""
if not self._answerable:
return False
self._web_user_presence.message(self.steamid_from.as_64, message)
return True
def persona_state_to_str(self):
"""
Returns the `persona_state` as string
:return: `persona_state` as String or False if `persona_state` is not available
"""
if self.persona_state:
return PERSONA_STATES.__getitem__(self.persona_state)
return False
class WebUserPresenceException(Exception):
pass
class InvalidInstanceSupplied(WebUserPresenceException):
pass
class InstanceNotReady(WebUserPresenceException):
pass
class NotAuthorized(WebUserPresenceException):
pass
class LogonFailed(WebUserPresenceException):
pass
class PollCreationFailed(WebUserPresenceException):
pass
class HTTPError(WebUserPresenceException):
pass
Loading…
Cancel
Save