2015-07-20 12:16:39 -05:00
|
|
|
# CLEANUP IMPORTS
|
|
|
|
|
|
|
|
from direct.directnotify import DirectNotifyGlobal
|
|
|
|
from direct.fsm.FSM import FSM
|
|
|
|
from direct.showbase.DirectObject import *
|
|
|
|
from toontown.toon.ToonDNA import ToonDNA, getSpeciesName
|
|
|
|
import TopToonsGlobals
|
2015-07-25 00:15:10 -05:00
|
|
|
import time, random
|
2015-07-20 12:16:39 -05:00
|
|
|
import datetime, json
|
|
|
|
import urllib
|
|
|
|
import urllib2
|
|
|
|
import hashlib
|
|
|
|
|
|
|
|
def getCurrentMonth():
|
|
|
|
dt = datetime.date.today()
|
|
|
|
month = dt.month
|
|
|
|
year = dt.year
|
|
|
|
return year * 100 + month
|
|
|
|
|
|
|
|
def getPrevMonth():
|
|
|
|
current = getCurrentMonth()
|
|
|
|
year, month = divmod(current, 100)
|
|
|
|
month -= 1
|
|
|
|
if not month:
|
|
|
|
month = 12
|
|
|
|
year -= 1
|
|
|
|
|
|
|
|
return year * 100 + month
|
|
|
|
|
|
|
|
def getNextMonth():
|
|
|
|
current = getCurrentMonth()
|
|
|
|
year, month = divmod(current, 100)
|
|
|
|
month += 1
|
|
|
|
if month > 12:
|
|
|
|
month = 1
|
|
|
|
year += 1
|
|
|
|
|
|
|
|
return year * 100 + month
|
|
|
|
|
|
|
|
def timeToNextMonth():
|
|
|
|
now = datetime.datetime.now()
|
|
|
|
year, month = divmod(getNextMonth(), 100)
|
|
|
|
return (datetime.datetime(year, month, 1) - now).total_seconds()
|
|
|
|
|
|
|
|
def getEmptySiteToonsColl(month):
|
|
|
|
coll = {}
|
|
|
|
|
|
|
|
start = TopToonsGlobals._CAT_BEGIN
|
|
|
|
end = TopToonsGlobals._CAT_END
|
|
|
|
while start <= end:
|
|
|
|
coll[str(start)] = {}
|
|
|
|
start *= 2
|
|
|
|
|
|
|
|
coll['month'] = month
|
|
|
|
return coll
|
|
|
|
|
|
|
|
class SiteUploadFSM(FSM):
|
|
|
|
notify = DirectNotifyGlobal.directNotify.newCategory('SiteUploadFSM')
|
|
|
|
URL = config.GetString('toptoons-api-endpoint', 'http://toontownstride.com/toptoons/post/') # Let's hope jumbleweed hasn't changed this
|
|
|
|
|
|
|
|
def __init__(self, mgr, data):
|
|
|
|
FSM.__init__(self, 'SiteUploadFSM')
|
|
|
|
|
|
|
|
self.mgr = mgr
|
|
|
|
self.data = {}
|
|
|
|
self.month = data.pop('month')
|
|
|
|
for category, avs in data.items():
|
|
|
|
self.data[int(category)] = sorted(avs.items(), key=lambda x: -x[1])
|
|
|
|
|
|
|
|
self.__cat = TopToonsGlobals._CAT_BEGIN
|
|
|
|
self.__responses = {}
|
|
|
|
self.__cache = {}
|
|
|
|
self.__waiting = {}
|
|
|
|
self.__dataToSend = {}
|
|
|
|
self.__failures = -1
|
|
|
|
|
|
|
|
self.demand('QueryAvatars')
|
|
|
|
|
|
|
|
def enterQueryAvatars(self):
|
|
|
|
avs = self.data[self.__cat]
|
|
|
|
cutoff = self.__failures
|
|
|
|
if cutoff == -1:
|
|
|
|
cutoff = 5
|
|
|
|
selected, remaining = avs[:cutoff], avs[cutoff:]
|
|
|
|
self.data[self.__cat] = remaining
|
|
|
|
|
|
|
|
self.__waiting = {int(x[0]): x[1] for x in selected}
|
|
|
|
avIds = self.__waiting.keys()
|
|
|
|
for avId in avIds:
|
|
|
|
if avId in self.__cache:
|
|
|
|
self.__responses[avId] = (self.__cache[avId][0], self.__waiting.pop(avId))
|
|
|
|
|
|
|
|
self.__failures = 0
|
|
|
|
for avId in self.__waiting:
|
|
|
|
def response(x, y, avId=avId):
|
|
|
|
self.__handleToon(avId, x, y)
|
|
|
|
|
|
|
|
self.mgr.air.dbInterface.queryObject(self.mgr.air.dbId, avId, response)
|
|
|
|
|
|
|
|
if not self.__waiting:
|
|
|
|
self.demand('SortResults')
|
|
|
|
|
|
|
|
def __handleToon(self, avId, dclass, fields):
|
|
|
|
if avId not in self.__waiting:
|
|
|
|
return
|
|
|
|
|
|
|
|
if dclass != self.mgr.air.dclassesByName['DistributedToonUD']:
|
|
|
|
self.__failures += 1
|
|
|
|
self.notify.warning('%d query failed!' % avId)
|
|
|
|
del self.__waiting[avId]
|
|
|
|
if not self.__waiting:
|
|
|
|
self.demand('QueryAvatars')
|
|
|
|
return
|
|
|
|
|
|
|
|
name = fields['setName'][0]
|
|
|
|
hp = fields['setMaxHp'][0]
|
|
|
|
|
|
|
|
dna = ToonDNA(fields['setDNAString'][0])
|
|
|
|
species = getSpeciesName(dna.head)
|
|
|
|
color = dna.headColor
|
|
|
|
|
|
|
|
if species == 'pig':
|
|
|
|
dna = 'pig'
|
|
|
|
|
|
|
|
else:
|
|
|
|
if species == 'cat' and color == 26:
|
|
|
|
dna = 'blackcat'
|
|
|
|
|
|
|
|
else:
|
|
|
|
if color > 23:
|
|
|
|
color = 0
|
|
|
|
|
|
|
|
dna = '%s_%s_%d' % (species, dna.head[1:], color)
|
|
|
|
|
|
|
|
self.__responses[avId] = ((name, dna, hp), self.__waiting.pop(avId))
|
|
|
|
|
|
|
|
if not self.__waiting:
|
|
|
|
self.demand('QueryAvatars')
|
|
|
|
|
|
|
|
def enterSortResults(self):
|
|
|
|
responses = sorted(self.__responses.values(), key=lambda x: -x[-1])
|
|
|
|
self.__dataToSend[self.__cat] = responses
|
|
|
|
self.__cache.update(self.__responses)
|
|
|
|
self.__failures = -1
|
|
|
|
self.__responses = {}
|
|
|
|
self.__cat *= 2
|
|
|
|
if self.__cat * 2 == TopToonsGlobals._CAT_END:
|
|
|
|
self.demand('Upload')
|
|
|
|
return
|
|
|
|
|
|
|
|
self.demand('QueryAvatars')
|
|
|
|
|
|
|
|
def enterUpload(self):
|
|
|
|
self.__dataToSend['month'] = self.month
|
|
|
|
|
|
|
|
(success, error), res = self.post(self.URL, self.__dataToSend)
|
|
|
|
print (success, error), res
|
|
|
|
|
|
|
|
def post(self, url, data):
|
|
|
|
headers = {'User-Agent' : 'TTUberAgent'}
|
|
|
|
|
|
|
|
innerData = json.dumps(data)
|
|
|
|
hmac = hashlib.sha512(innerData + self.mgr.air.getApiKey()).hexdigest() # XXX PROVIDE THE KEY HERE
|
|
|
|
|
|
|
|
data = 'data=%s' % urllib.quote(innerData)
|
|
|
|
data += '&hmac=%s' % urllib.quote(hmac)
|
|
|
|
|
|
|
|
success = True
|
|
|
|
error = None
|
|
|
|
res = {}
|
|
|
|
|
|
|
|
try:
|
|
|
|
req = urllib2.Request(url, data, headers)
|
|
|
|
res = json.loads(urllib2.urlopen(req).read())
|
|
|
|
success = res['success']
|
|
|
|
error = res.get('error')
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
if hasattr(e, 'read'):
|
|
|
|
with open('../e.html', 'wb') as f:
|
|
|
|
f.write(e.read())
|
|
|
|
|
|
|
|
success = False
|
|
|
|
error = str(e)
|
|
|
|
|
|
|
|
return (success, error), res
|
|
|
|
|
2015-07-21 11:04:01 -05:00
|
|
|
class TopToonsManagerUD(DirectObject):
|
|
|
|
notify = DirectNotifyGlobal.directNotify.newCategory('TopToonsManagerUD')
|
2015-07-20 12:16:39 -05:00
|
|
|
|
|
|
|
def __init__(self, air):
|
|
|
|
self.air = air
|
|
|
|
|
|
|
|
self.__curMonth = getCurrentMonth()
|
|
|
|
coll = None
|
|
|
|
if self.air.dbConn:
|
|
|
|
coll = self.air.dbGlobalCursor.strideToons.find_one({'month': self.__curMonth})
|
|
|
|
if not coll:
|
|
|
|
lastMonthColl = self.air.dbGlobalCursor.strideToons.find_one({'month': getPrevMonth()})
|
|
|
|
if lastMonthColl:
|
|
|
|
self.__uploadLastMonth(lastMonthColl)
|
|
|
|
|
|
|
|
if not coll:
|
|
|
|
coll = getEmptySiteToonsColl(self.__curMonth)
|
|
|
|
|
|
|
|
self.__topToonsData = coll
|
|
|
|
self.__topToonsData.pop('_id', None)
|
|
|
|
|
|
|
|
self.accept('topToonsManager-AI-score-site', self.__topToonsScore)
|
|
|
|
self.waitForNextMonth()
|
|
|
|
|
|
|
|
def __uploadLastMonth(self, data):
|
|
|
|
self.notify.info('Sending last month result to site...')
|
|
|
|
SiteUploadFSM(self, data)
|
|
|
|
|
|
|
|
def waitForNextMonth(self):
|
|
|
|
def _nm(task):
|
|
|
|
self.__uploadLastMonth(self.__topToonsData)
|
|
|
|
|
|
|
|
self.__curMonth = getCurrentMonth()
|
|
|
|
self.__topToonsData = getEmptySiteToonsColl(self.__curMonth)
|
|
|
|
|
|
|
|
self.waitForNextMonth()
|
|
|
|
|
|
|
|
return task.done
|
|
|
|
|
2015-07-21 11:04:01 -05:00
|
|
|
taskMgr.doMethodLater(timeToNextMonth() + 1, _nm, 'TopToonsManagerUD-nextMonth')
|
2015-07-20 12:16:39 -05:00
|
|
|
|
|
|
|
def saveSite(self):
|
|
|
|
if self.air.dbConn:
|
|
|
|
self.air.dbGlobalCursor.strideToons.update({'month': self.__curMonth}, {'$set': self.__topToonsData}, upsert=True)
|
|
|
|
|
|
|
|
def __topToonsScore(self, avId, categories, score):
|
|
|
|
def _add(cat):
|
|
|
|
cd = self.__topToonsData[str(cat)]
|
|
|
|
cd[str(avId)] = cd.get(str(avId), 0) + score
|
|
|
|
|
|
|
|
start = TopToonsGlobals._CAT_BEGIN
|
|
|
|
end = TopToonsGlobals._CAT_END
|
|
|
|
while start <= end:
|
|
|
|
if categories & start:
|
|
|
|
_add(start)
|
|
|
|
|
|
|
|
start *= 2
|
|
|
|
|
|
|
|
self.saveSite()
|