bf3api.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. # -*- coding: UTF-8 -*-
  2. #
  3. # Copyright (c) 2012 Harry Gabriel <h.gabriel@nodefab.de>
  4. #
  5. # This program is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. import urllib
  18. import time
  19. import base64
  20. import hashlib
  21. import hmac
  22. try:
  23. # python version 2.6 or newer
  24. import json
  25. except ImportError:
  26. try:
  27. # older python versions
  28. import simplejson as json
  29. except ImportError:
  30. # Houston, we have a problem
  31. raise ImportError, 'Unable to load a json library'
  32. class API(object):
  33. """A python interface for the bf3stats.com API"""
  34. def __init__(self, base_url=None, plattform='pc', secret=None, ident=None):
  35. # setup url to bf3stats.com API
  36. if base_url is None:
  37. self._base_url = 'http://api.bf3stats.com'
  38. else:
  39. self._base_url = base_url
  40. # can be pc, 360 or ps3
  41. self._plattform = plattform
  42. # ident and secret for signed requests
  43. if ident:
  44. self._ident = ident
  45. if secret:
  46. self._secret = secret
  47. def _request(self, post_data, data_group, sign=False, plattform=None):
  48. """Access bfstats.com API via HTTP POST"""
  49. # build url for current request
  50. if plattform is None:
  51. plattform = self._plattform
  52. api_url = '%s/%s/%s/' % (self._base_url, plattform, data_group)
  53. # sign data if we need
  54. if sign:
  55. post_data = self._sign(post_data)
  56. try:
  57. con = urllib.urlopen(api_url, urllib.urlencode(post_data))
  58. result = con.read()
  59. con.close()
  60. raw_data = json.loads(result)
  61. except IOError, err:
  62. raw_data = {'status' : 'error', 'error': err}
  63. return objDict(raw_data)
  64. def _sign(self, data_dict):
  65. """Sign data for a signed request"""
  66. data = base64.urlsafe_b64encode(json.dumps(data_dict)).rstrip('=')
  67. sig = base64.urlsafe_b64encode(
  68. hmac.new(self._secret, msg=data, digestmod=hashlib.sha256).digest()
  69. ).rstrip('=')
  70. return { 'data': data, 'sig': sig }
  71. # reimplement bf3stats.com JSON API
  72. # Method names taken from the documentation
  73. # http://bf3stats.com/api_url
  74. def playerlist(self, players):
  75. """Request a list of players"""
  76. pass
  77. def player(self, player_name, platform, parts=None):
  78. """Request a player"""
  79. post_data = {
  80. 'player' : player_name,
  81. 'opt' : parts
  82. }
  83. return self._request(post_data, 'player', False, platform)
  84. def dogtags(self, player_name):
  85. """Request Player dogtags"""
  86. return self._request(post_data = {'player': player_name}, data_group='dogtags')
  87. def onlinestats(self):
  88. """Count of online players"""
  89. return self._request(post_data={}, data_group='onlinestats', plattform='global')
  90. def playerupdate(self, player_name, data_group='playerupdate'):
  91. """Request a playerupdate. (signed request)
  92. bf3stats.com request the current data from EA for this player.
  93. If the player was not in bf3stats.com database, they do automatically a lookup and add the player.
  94. Note: Clock should not have more than 1 minute difference to current time.
  95. """
  96. post_data = {
  97. 'ident': self._ident,
  98. 'time': int(time.time()),
  99. 'player': player_name
  100. }
  101. return self._request(post_data, data_group, sign=True)
  102. def playerlookup(self, player_name):
  103. """Lookup a player. (signed request)"""
  104. # the code would be the same as playerupdate - let reuse this function
  105. return self.playerupdate(player_name, data_group='playerlookup')
  106. def setupkey(self, client_ident, name):
  107. """Generate an individual client key for every installation"""
  108. post_data = {
  109. 'ident': self._ident,
  110. 'time': int(time.time()),
  111. 'clientident': client_ident,
  112. 'name': name
  113. }
  114. return self._request(post_data, data_group='setupkey', plattform='global', sign=True)
  115. def getkey(self, client_ident):
  116. """"Get information about a existing client key or your own key."""
  117. post_data = {
  118. 'ident': self._ident,
  119. 'time': int(time.time()),
  120. 'clientident': client_ident,
  121. }
  122. return self._request(post_data, data_group='getkey', plattform='global', sign=True)
  123. class objDict(object):
  124. '''The recursive class for building and representing objects with.'''
  125. # http://stackoverflow.com/questions/1305532/convert-python-dict-to-object
  126. def __init__(self, obj):
  127. for k, v in obj.iteritems():
  128. if isinstance(v, dict):
  129. setattr(self, _to_str(k).title(), objDict(v))
  130. else:
  131. setattr(self, k, v)
  132. def __getitem__(self, val):
  133. return self.__dict__[val]
  134. def __repr__(self):
  135. return '{%s}' % str(', '.join('%s : %s' % (k, repr(v)) for (k, v) in self.__dict__.iteritems()))
  136. def _to_str(name):
  137. """Add _ if given string is a digit."""
  138. if name.isdigit():
  139. name = '_%s' % name
  140. return name