Skip to content

Commit

Permalink
Merge branch 'master' into stable
Browse files Browse the repository at this point in the history
  • Loading branch information
oczkers committed Dec 30, 2013
2 parents 119c71b + 6e39d82 commit 308f8ed
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 55 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ test.py
log.log
content.log
error.log
fut14.log
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ python:
- 2.7
- 3.2
- 3.3
- pypy
#- pypy
script: make test
install:
- make test-deps
Expand Down
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ Patches and Suggestions
```````````````````````
- mvillarejo
- Mauro Marano
- John Nurseri <in.nursery@gmail.com>
4 changes: 3 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ Usage
.. code-block:: pycon
>>> import fut14
>>> fut = fut14.Core('email', 'password', 'secret answer')
>>> fut = fut14.Core('email', 'password', 'secret answer', platform='xbox', debug=True)
>>> # PLATFORM: pc / ps3 / xbox / and / ios (pc default)
>>> # DEBUG: save http response to fut14.log)
>>> items = fut.searchAuctions('development', # search items
... level='gold', category='fitness', min_price=300, # optional parametrs
Expand Down
95 changes: 57 additions & 38 deletions fut14/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,19 +71,24 @@ def itemParse(item_data):
'expires': item_data.get('expires'), # seconds left
}

''' # different urls (platforms)
def cardInfo(resource_id):
"""Returns card info."""
# TODO: add referer to headers (futweb)
url = '{}{}.json'.format(urls['card_info'], baseId(resource_id))
url = '{}{}.json'.format(self.urls['card_info'], baseId(resource_id))
return requests.get(url).json()
'''


class Core(object):
def __init__(self, email, passwd, secret_answer):
def __init__(self, email, passwd, secret_answer, platform='pc', debug=False):
# TODO: validate fut request response (200 OK)
self.debug = debug
self.email = email
self.passwd = passwd
self.secret_answer_hash = EAHashingAlgorithm().EAHash(secret_answer)
self.platform = platform
self.urls = urls(platform)
self.r = requests.Session() # init/reset requests session object
self.r.headers = headers # i'm chrome browser now ;-)
self.credits = 0
Expand All @@ -93,26 +98,29 @@ def __login__(self, email, passwd, secret_answer_hash):
"""Just log in."""
# TODO: split into smaller methods
# === login
urls['login'] = self.r.get(urls['fut_home']).url
self.r.headers['Referer'] = urls['main_site'] # prepare headers
self.urls['login'] = self.r.get(self.urls['fut_home']).url
self.r.headers['Referer'] = self.urls['main_site'] # prepare headers
data = {'email': email,
'password': passwd,
'_rememberMe': 'on',
'rememberMe': 'on',
'_eventId':
'submit', 'facebookAuth': ''}
rc = self.r.post(urls['login'], data=data).text
rc = self.r.post(self.urls['login'], data=data)
if self.debug: open('fut14.log', 'wb').write(rc.content)
# TODO: catch invalid data exception
#self.nucleus_id = re.search('userid : "([0-9]+)"', rc).group(1) # we'll get it later
#self.nucleus_id = re.search('userid : "([0-9]+)"', rc.text).group(1) # we'll get it later

# === lanuch futweb
self.r.headers['Referer'] = urls['fut_home'] # prepare headers
rc = self.r.get(urls['futweb']).text
self.r.headers['Referer'] = self.urls['fut_home'] # prepare headers
rc = self.r.get(self.urls['futweb'])
if self.debug: open('fut14.log', 'wb').write(rc.content)
rc = rc.text
if 'EASW_ID' not in rc:
raise Fut14Error('Invalid email or password.')
self.nucleus_id = re.search("var EASW_ID = '([0-9]+)';", rc).group(1)
#urls['fut_base'] = re.search("var BASE_FUT_URL = '(https://.+?)';", rc).group(1)
#urls['fut_home'] = re.search("var GUEST_APP_URI = '(http://.+?)';", rc).group(1)
#self.urls['fut_base'] = re.search("var BASE_FUT_URL = '(https://.+?)';", rc).group(1)
#self.urls['fut_home'] = re.search("var GUEST_APP_URI = '(http://.+?)';", rc).group(1)

# acc info
self.r.headers.update({ # prepare headers
Expand All @@ -121,10 +129,12 @@ def __login__(self, email, passwd, secret_answer_hash):
'Easw-Session-Data-Nucleus-Id': self.nucleus_id,
'X-UT-Embed-Error': 'true',
'X-Requested-With': 'XMLHttpRequest',
'X-UT-Route': urls['fut_host'],
'Referer': urls['futweb'],
'X-UT-Route': self.urls['fut_host'],
'Referer': self.urls['futweb'],
})
rc = self.r.get(urls['acc_info']).json()['userAccountInfo']['personas'][0]
rc = self.r.get(self.urls['acc_info'])
if self.debug: open('fut14.log', 'wb').write(rc.content)
rc = rc.json()['userAccountInfo']['personas'][0]
self.persona_id = rc['personaId']
self.persona_name = rc['personaName']
self.clubs = [i for i in rc['userClubList']]
Expand All @@ -142,24 +152,30 @@ def __login__(self, email, passwd, secret_answer_hash):
'nuc': self.nucleus_id,
'nucleusPersonaId': self.persona_id,
'nucleusPersonaDisplayName': self.persona_name,
'nucleusPersonaPlatform': 'pc', # TODO: multiplatform
'nucleusPersonaPlatform': self.platform,
'locale': 'en-GB',
'method': 'authcode',
'priorityLevel': 4,
'identification': {'AuthCode': ''}}
rc = self.r.post(urls['fut']['authentication'], data=json.dumps(data)).json()
#urls['fut_host'] = '{0}://{1}'.format(rc['protocol']+rc['ipPort'])
rc = self.r.post(self.urls['fut']['authentication'], data=json.dumps(data))
if self.debug: open('fut14.log', 'wb').write(rc.content)
rc = rc.json()
#self.urls['fut_host'] = '{0}://{1}'.format(rc['protocol']+rc['ipPort'])
self.r.headers['X-UT-SID'] = self.sid = rc['sid']

# validate (secret question)
self.r.headers['Accept'] = 'text/json' # prepare headers
del self.r.headers['Origin']
rc = self.r.get(urls['fut_question']).json()
rc = self.r.get(self.urls['fut_question'])
if self.debug: open('fut14.log', 'wb').write(rc.content)
rc = rc.json()
if rc.get('string') != 'Already answered question.':
# answer question
data = {'answer': self.secret_answer_hash}
self.r.headers['Content-Type'] = 'application/x-www-form-urlencoded' # requests bug?
rc = self.r.post(urls['fut_validate'], data=data).json()
rc = self.r.post(self.urls['fut_validate'], data=data)
if self.debug: open('fut14.log', 'wb').write(rc.content)
rc = rc.json()
self.r.headers['Content-Type'] = 'application/json'
self.r.headers['X-UT-PHISHING-TOKEN'] = self.token = rc['token']

Expand All @@ -177,7 +193,7 @@ def __login__(self, email, passwd, secret_answer_hash):

# get basic user info
# TODO: parse response (https://gist.github.com/oczkers/526577572c097eb8172f)
self.__get__(urls['fut']['user'])
self.__get__(self.urls['fut']['user'])
# size of piles
piles = self.pileSize()
self.tradepile_size = piles['tradepile']
Expand All @@ -186,15 +202,16 @@ def __login__(self, email, passwd, secret_answer_hash):
# def __shards__(self):
# """Returns shards info."""
# # TODO: headers
# self.r.headers['X-UT-Route'] = urls['fut_base']
# return self.r.get(urls['shards']).json()
# # self.r.headers['X-UT-Route'] = urls['fut_pc']
# self.r.headers['X-UT-Route'] = self.urls['fut_base']
# return self.r.get(self.urls['shards']).json()
# # self.r.headers['X-UT-Route'] = self.urls['fut_pc']

def __request__(self, method, url, *args, **kwargs):
"""Prepares headers and sends request. Returns response as a json object."""
# TODO: update credtis?
self.r.headers['X-HTTP-Method-Override'] = method.upper()
rc = self.r.post(url, *args, **kwargs)
if self.debug: open('fut14.log', 'wb').write(rc.content)
if rc.text == '':
rc = {}
else:
Expand Down Expand Up @@ -222,9 +239,11 @@ def baseId(self, *args, **kwargs):
"""Alias for baseId."""
return baseId(*args, **kwargs)

def cardInfo(self, *args, **kwargs):
"""Alias for cardInfo."""
return cardInfo(*args, **kwargs)
def cardInfo(self, resource_id):
"""Returns card info."""
# TODO: add referer to headers (futweb)
url = '{}{}.json'.format(self.urls['card_info'], baseId(resource_id))
return requests.get(url).json()

def searchAuctions(self, ctype, level=None, category=None, assetId=None,
min_price=None, max_price=None, min_buy=None, max_buy=None,
Expand Down Expand Up @@ -254,15 +273,15 @@ def searchAuctions(self, ctype, level=None, category=None, assetId=None,
if nationality: params['nat'] = nationality
if playStyle: params['playStyle'] = playStyle

rc = self.__get__(urls['fut']['SearchAuctions'], params=params)
rc = self.__get__(self.urls['fut']['SearchAuctions'], params=params)
return [itemParse(i) for i in rc['auctionInfo']]

def bid(self, trade_id, bid):
"""Make a bid."""
rc = self.__get__(urls['fut']['PostBid'], params={'tradeIds': trade_id})['auctionInfo'][0]
rc = self.__get__(self.urls['fut']['PostBid'], params={'tradeIds': trade_id})['auctionInfo'][0]
if rc['currentBid'] < bid and self.credits >= bid:
data = {'bid': bid}
url = '{0}/{1}/bid'.format(urls['fut']['PostBid'], trade_id)
url = '{0}/{1}/bid'.format(self.urls['fut']['PostBid'], trade_id)
rc = self.__put__(url, data=json.dumps(data))['auctionInfo'][0]
if rc['bidState'] == 'highest' or (rc['tradeState'] == 'closed' and rc['bidState'] == 'buyNow'): # checking 'tradeState' is required?
return True
Expand All @@ -271,38 +290,38 @@ def bid(self, trade_id, bid):

def tradepile(self):
"""Returns items in tradepile."""
rc = self.__get__(urls['fut']['TradePile'])
rc = self.__get__(self.urls['fut']['TradePile'])
return [itemParse(i) for i in rc['auctionInfo']]

def watchlist(self):
"""Returns items in watchlist."""
rc = self.__get__(urls['fut']['WatchList'])
rc = self.__get__(self.urls['fut']['WatchList'])
return [itemParse(i) for i in rc['auctionInfo']]

def unassigned(self):
"""Returns Unassigned items (i.e. buyNow items)."""
rc = self.__get__(urls['fut']['Unassigned'])
rc = self.__get__(self.urls['fut']['Unassigned'])
return [itemParse({'itemData': i}) for i in rc['itemData']]

# def relistAll(self, item_id):
# """Relist all items in trade pile."""
# print(self.r.get(urls['fut']['Item']+'/%s' % item_id).text)
# print(self.r.get(self.urls['fut']['Item']+'/%s' % item_id).text)

def sell(self, item_id, bid, buy_now=0, duration=3600):
"""Starts auction. Returns trade_id."""
data = {'buyNowPrice': buy_now, 'startingBid': bid, 'duration': duration, 'itemData':{'id': item_id}}
rc = self.__post__(urls['fut']['SearchAuctionsListItem'], data=json.dumps(data))
rc = self.__post__(self.urls['fut']['SearchAuctionsListItem'], data=json.dumps(data))
return rc['id']

def watchlistDelete(self, trade_id):
"""Removes card from watchlist."""
params = {'tradeId': trade_id}
self.__delete__(urls['fut']['WatchList'], params=params) # returns nothing
self.__delete__(self.urls['fut']['WatchList'], params=params) # returns nothing
return True

def tradepileDelete(self, trade_id):
"""Removes card from tradepile."""
url = '{}/{}'.format(urls['fut']['TradeInfo'], trade_id)
url = '{}/{}'.format(self.urls['fut']['TradeInfo'], trade_id)
self.__delete__(url) # returns nothing
return True

Expand All @@ -316,16 +335,16 @@ def sendToTradepile(self, trade_id, item_id):
# unassigned item
data = {"itemData": [{"pile": "trade", "id": str(item_id)}]}

rc = self.__put__(urls['fut']['Item'], data=json.dumps(data))
rc = self.__put__(self.urls['fut']['Item'], data=json.dumps(data))
return rc['itemData'][0]['success']

def keepalive(self):
"""Just refresh credits ammount to let know that we're still online."""
self.__get__(urls['fut']['Credits'])
self.__get__(self.urls['fut']['Credits'])
return True

def pileSize(self):
"""Returns size of tradepile and watchlist."""
rc = self.__get__(urls['fut']['PileSize'])['entries']
rc = self.__get__(self.urls['fut']['PileSize'])['entries']
return {'tradepile': rc[0]['value'],
'watchlist': rc[2]['value']}
51 changes: 36 additions & 15 deletions fut14/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import xmltodict
from time import time

from .exceptions import Fut14Error

# TODO: add timestamp dynamic (just right before every request)

def updateUrls(urls):
def __updateUrls__(urls):
"""Gets services urls."""
rc = xmltodict.parse(requests.get(urls['fut_config']).content)
services = rc['main']['services']['prod']
Expand All @@ -20,17 +22,36 @@ def updateUrls(urls):
urls['fut'][i] = path+services[i]
return urls

urls = updateUrls({
'main_site': 'https://www.easports.com',
'futweb': 'http://www.easports.com/iframe/fut/?locale=en_US&baseShowoffUrl=http%3A%2F%2Fwww.easports.com%2Fuk%2Ffifa%2Ffootball-club%2Fultimate-team%2Fshow-off&guest_app_uri=http%3A%2F%2Fwww.easports.com%2Fuk%2Ffifa%2Ffootball-club%2Fultimate-team',
'fut_config': 'http://www.easports.com/iframe/fut/bundles/futweb/web/flash/xml/site_config.xml',
'fut_home': 'http://www.easports.com/uk/fifa/football-club/ultimate-team',
'fut_host': 'https://utas.s2.fut.ea.com:443', # PC - different on other platforms
'fut': {}, # it's updated dynamicly (based on fut_config)
'fut_question': 'http://www.easports.com/iframe/fut/p/ut/game/fifa14/phishing/question?_=%s' % time(),
'fut_validate': 'http://www.easports.com/iframe/fut/p/ut/game/fifa14/phishing/validate',

'shards': 'http://www.easports.com/iframe/fut/p/ut/shards?_=%s' % time(),
'acc_info': 'http://www.easports.com/iframe/fut/p/ut/game/fifa14/user/accountinfo?_=%s' % time(),
'card_info': 'http://cdn.content.easports.com/fifa/fltOnlineAssets/C74DDF38-0B11-49b0-B199-2E2A11D1CC13/2014/fut/items/web/',
})


def urls(platform):
"""Returns services urls."""
urls = {
'main_site': 'https://www.easports.com',
'futweb': 'http://www.easports.com/iframe/fut/?locale=en_US&baseShowoffUrl=http%3A%2F%2Fwww.easports.com%2Fuk%2Ffifa%2Ffootball-club%2Fultimate-team%2Fshow-off&guest_app_uri=http%3A%2F%2Fwww.easports.com%2Fuk%2Ffifa%2Ffootball-club%2Fultimate-team',
'fut_config': 'http://www.easports.com/iframe/fut/bundles/futweb/web/flash/xml/site_config.xml',
'fut_home': 'http://www.easports.com/uk/fifa/football-club/ultimate-team',
'fut': {}, # it's updated dynamicly (based on fut_config)
'fut_question': 'http://www.easports.com/iframe/fut/p/ut/game/fifa14/phishing/question?_=%s' % time(),
'fut_validate': 'http://www.easports.com/iframe/fut/p/ut/game/fifa14/phishing/validate',

'fut_host': {'pc': 'https://utas.s2.fut.ea.com:443', # PC - different on other platforms
'ps3': 'https://utas.s2.fut.ea.com:443',
'xbox': 'https://utas.fut.ea.com:443',
'ios': 'https://utas.fut.ea.com:443',
'and': 'https://utas.fut.ea.com:443',
},

'shards': 'http://www.easports.com/iframe/fut/p/ut/shards?_=%s' % time(),
'acc_info': 'http://www.easports.com/iframe/fut/p/ut/game/fifa14/user/accountinfo?_=%s' % time(),
'card_info': 'http://cdn.content.easports.com/fifa/fltOnlineAssets/C74DDF38-0B11-49b0-B199-2E2A11D1CC13/2014/fut/items/web/',
}

if platform in ('pc', 'ps3'):
urls['fut_host'] = 'https://utas.s2.fut.ea.com:443'
elif platform in ('xbox', 'ios', 'and'):
urls['fut_host'] = 'https://utas.fut.ea.com:443'
else:
raise Fut14Error('Invalid platform. (Valid ones are pc/ps3/xbox/and/ios).')

return __updateUrls__(urls)

0 comments on commit 308f8ed

Please sign in to comment.