Jump to content
Korean Random
Sign in to follow this  
Heliomalt

[Fixed] Problem with wn8exp.json

Recommended Posts

Since today, retrieving the wn8exp.json via a statistic Python script no longer works. Only the following entry is generated locally in the file:

 

<html><body><h1>403 Forbidden</h1>
Request forbidden by administrative rules.
</body></html>

 

The browser displays the wn8exp.json.

 

What has changed in retrieving the data when it is done with a script?

Edited by Heliomalt

Share this post


Link to post

Short link
Share on other sites

Are you sure what you're talking about wn8exp.json?

There is no any restrictions in access to this file on our side.
Could you show us an example of your request script so we could investigate this problem further?

Share this post


Link to post

Short link
Share on other sites

Its not my script, its from Tomonik. see http://forum.worldoftanks.eu/index.php?/topic/572771-1610-session-statistics-and-battle-result-messages/#topmost

 

# Python bytecode 2.7 (decompiled from Python 2.7)
# Embedded file name: D:\Mods\World_of_Tanks\SessionStatistics\mod_stat.py
# Compiled at: 2019-10-09 17:47:22
import BigWorld
import AccountCommands
import ArenaType
import codecs
import datetime
import json
import math
import os
import re
import ResMgr
import threading
import nations
import urllib
from Account import Account
from account_helpers import BattleResultsCache
from items import vehicles as vehiclesWG
from helpers import i18n
from notification.NotificationListView import NotificationListView
from notification.NotificationPopUpViewer import NotificationPopUpViewer
from messenger import MessengerEntry
from messenger.formatters.service_channel import BattleResultsFormatter
from Queue import Queue
from debug_utils import *
from gui.Scaleform.daapi.view.meta.NotificationsListMeta import NotificationsListMeta
from gui import SystemMessages
from constants import ARENA_GUI_TYPE
from gui.battle_results.components.common import RegularArenaFullNameItem
from gui.battle_results.components.common import _ARENA_FULL_NAME_FORMAT, _ARENA_TYPE_FORMAT, _ARENA_TYPE_EXT_FORMAT
from urllib2 import urlopen
GENERAL = 0
BY_TANK_1 = 11
BY_TANK_2 = 12
BY_TANK_3 = 13
BY_TANK_4 = 14
BY_TANK_5 = 15
BY_TANK_6 = 16
BY_TANK_7 = 17
BY_TANK_8 = 18
BY_TANK_9 = 19
BY_TANK_10 = 20
BY_MAP_1 = 21
BY_MAP_2 = 22
BY_MAP_3 = 23
BY_MAP_4 = 24
BY_MAP_5 = 25
RESET = 30

def hexToRgb(hex):
    return [ int(hex[i:i + 2], 16) for i in range(1, 6, 2) ]


def gradColor(startColor, endColor, val):
    start = hexToRgb(startColor)
    end = hexToRgb(endColor)
    grad = []
    for i in [0, 1, 2]:
        grad.append(start[i] * (1.0 - val) + end[i] * val)

    return '#%02x%02x%02x' % (grad[0], grad[1], grad[2])


class SessionStatistic(object):

    def __init__(self):
        self.page = GENERAL
        self.cacheVersion = 7
        self.queue = Queue()
        self.loaded = False
        self.configIsValid = True
        self.battleStats = {}
        self.cache = {}
        self.gradient = {}
        self.palette = {}
        self.config = {}
        self.arenaUniqueID = None
        self.expectedValues = {}
        self.expectedKTTCValues = {}
        self.xvmscales = {}
        self.values = {}
        self.battles = []
        self.battleStatPatterns = []
        self.messageGeneral = ''
        self.messageByTank = ''
        self.messageByMap = ''
        self.messageReset = ''
        self.tankSortingMacros = {}
        self.mapSortingMacros = {}
        self.playerName = ''
        self.bgIcon = ''
        self.startDate = None
        self.battleResultsAvailable = threading.Event()
        self.battleResultsAvailable.clear()
        self.battleResultsBusy = threading.Lock()
        self.thread = threading.Thread(target=self.mainLoop)
        self.thread.setDaemon(True)
        self.thread.start()
        self.path = '/'.join(['..',
         'res_mods',
         'configs',
         'wotstat'])
        self.readConfig()
        return

    def load(self):
        if self.loaded and self.playerName == BigWorld.player().name:
            return
        self.loaded = True
        self.battles = []
        self.playerName = BigWorld.player().name
        self.statCacheFilePath = '/'.join([self.path, 'cache.json'])
        invalidCache = True
        if os.path.isfile(self.statCacheFilePath):
            with open(self.statCacheFilePath) as jsonCache:
                self.cache = json.load(jsonCache)
                self.startDate = self.cache.get('date', self.getWorkDate())
                if self.cache.get('version', 0) == self.cacheVersion and (self.startDate == self.getWorkDate() or not self.config.get('dailyAutoReset', True)) and not self.config.get('clientReloadReset', False):
                    if self.cache.get('players', {}).has_key(self.playerName):
                        self.battles = self.cache['players'][self.playerName]['battles']
                    invalidCache = False
        if invalidCache:
            self.cache = {}
        expectedValuesPath = '/'.join([self.path, 'data', 'expected_tank_values.json'])
        if self.config.get('onlineUpdateData', True):
            expectedTankValuesURL = self.config.get('expectedTankValuesURL', 'https://static.modxvm.com/wn8-data-exp/json/wn8exp.json')
            localExpectedTankValues = json.load(open(expectedValuesPath))
            localVersionExpectedTankValues = json.dumps(localExpectedTankValues['header']['version'])
            updateExpectedOK = False
            try:
                newExpectedValuesPath = urlopen(expectedTankValuesURL).read()
                newExpectedTankValues = json.loads(newExpectedValuesPath)
                newVersionExpectedTankValues = json.dumps(newExpectedTankValues['header']['version'])
                if newVersionExpectedTankValues != localVersionExpectedTankValues:
                    urllib.urlretrieve(expectedTankValuesURL, expectedValuesPath)
                    updateExpectedOK = True
            except:
                pass

        if self.config.get('showExpectedTankValuesMessage', True):
            if updateExpectedOK:
                SystemMessages.pushMessage("<font color='#FFE6B3'>Session Statistics</font>\nExpected Tank Values updated to version: " + newVersionExpectedTankValues, type=SystemMessages.SM_TYPE.Information)
            else:
                SystemMessages.pushMessage("<font color='#FFE6B3'>Session Statistics</font>\nExpected Tank Values\nversion: " + localVersionExpectedTankValues, type=SystemMessages.SM_TYPE.Information)
        with open(expectedValuesPath) as origExpectedValuesJson:
            origExpectedValues = json.load(origExpectedValuesJson)
            for tankValues in origExpectedValues['data']:
                idNum = int(tankValues.pop('IDNum'))
                self.expectedValues[idNum] = {}
                for key in ['expDamage',
                 'expFrag',
                 'expSpot',
                 'expDef',
                 'expWinRate']:
                    self.expectedValues[idNum][key] = float(tankValues[key])

        expectedKTTCValuesPath = '/'.join([self.path, 'data', 'expected_kttc_tank_values.json'])
        with open(expectedKTTCValuesPath) as origExpectedKTTCValuesJson:
            origExpectedKTTCValues = json.load(origExpectedKTTCValuesJson)
            for tankValues in origExpectedKTTCValues['data']:
                idNum = int(tankValues.pop('IDNum'))
                self.expectedKTTCValues[idNum] = {}
                for key in ['expDamage',
                 'expFrag',
                 'expSpot',
                 'expDef',
                 'expWinRate']:
                    self.expectedKTTCValues[idNum][key] = float(tankValues[key])

        xvmScalesPath = '/'.join([self.path, 'data', 'xvmscales.json'])
        if self.config.get('onlineUpdateData', True):
            xvmScalesURL = 'https://static.modxvm.com/xvmscales.json'
            try:
                urllib.urlretrieve(xvmScalesURL, xvmScalesPath)
            except:
                pass

        with open(xvmScalesPath) as origXVMScaleJson:
            origXVMScale = json.load(origXVMScaleJson)
            for key in ['xeff', 'xwn8']:
                self.xvmscales[key] = origXVMScale[key]

        self.updateMessage()

    def readConfig(self):
        with codecs.open('/'.join([self.path, 'config.json']), 'r', 'utf-8-sig') as configFileJson:
            try:
                self.config = json.load(configFileJson)
                self.battleStatPatterns = []
                for pattern in self.config.get('battleStatPatterns', []):
                    try:
                        condition = pattern.get('if', 'True')
                        condition = re.sub('{{(\\w+)}}', "values['\\1']", condition)
                    except:
                        print '[wotstat] Invalid condition ' + pattern.get('if', '')
                        continue

                    try:
                        compiled = re.compile(pattern.get('pattern', ''))
                        self.battleStatPatterns.append({'condition': condition,
                         'pattern': compiled,
                         'repl': pattern.get('repl', '')})
                    except:
                        print '[wotstat] Invalid pattern ' + pattern.get('pattern', '')
                        continue

                self.configIsValid = True
            except:
                print '[wotstat] load stat_config.json has failed'
                self.config = {}
                self.configIsValid = False

    def getWorkDate(self):
        return datetime.date.today().strftime('%Y-%m-%d') if datetime.datetime.now().hour >= self.config.get('dailyAutoResetHour', 4) else (datetime.date.today() - datetime.timedelta(days=1)).strftime('%Y-%m-%d')

    def save(self):
        statCache = open(self.statCacheFilePath, 'w')
        self.cache['version'] = self.cacheVersion
        self.cache['date'] = self.startDate
        if not self.cache.has_key('players'):
            self.cache['players'] = {}
        if not self.cache['players'].has_key(self.playerName):
            self.cache['players'][self.playerName] = {}
        self.cache['players'][self.playerName]['battles'] = self.battles
        statCache.write(json.dumps(self.cache, sort_keys=True, indent=4, separators=(',', ': ')))
        statCache.close()

    def createMessage(self):
        messages = {GENERAL: self.messageGeneral,
         BY_TANK_1: self.messageByTank,
         BY_TANK_2: self.messageByTank,
         BY_TANK_3: self.messageByTank,
         BY_TANK_4: self.messageByTank,
         BY_TANK_5: self.messageByTank,
         BY_TANK_6: self.messageByTank,
         BY_TANK_7: self.messageByTank,
         BY_TANK_8: self.messageByTank,
         BY_TANK_9: self.messageByTank,
         BY_TANK_10: self.messageByTank,
         BY_MAP_1: self.messageByMap,
         BY_MAP_2: self.messageByMap,
         BY_MAP_3: self.messageByMap,
         BY_MAP_4: self.messageByMap,
         BY_MAP_5: self.messageByMap,
         RESET: self.messageReset}
        msg = messages[self.page]
        message = {'typeID': 1,
         'message': {'bgIcon': self.bgIcon,
                     'defaultIcon': '',
                     'savedData': 0,
                     'timestamp': -1,
                     'filters': [],
                     'buttonsLayout': [],
                     'message': msg,
                     'type': 'cancel',
                     'icon': self.config.get('icon', '../maps/icons/library/BattleResultIcon-1.png')},
         'entityID': 99999,
         'auxData': ['GameGreeting']}
        return message

    def battleResultsCallback(self, arenaUniqueID, responseCode, value=None, revision=0):
        if responseCode == AccountCommands.RES_NON_PLAYER or responseCode == AccountCommands.RES_COOLDOWN:
            BigWorld.callback(1.0, lambda : self.queue.put(arenaUniqueID))
            self.battleResultsBusy.release()
            return
        elif responseCode < 0:
            self.battleResultsBusy.release()
            return
        else:
            arenaTypeID = value['common']['arenaTypeID']
            arenaType = ArenaType.g_cache[arenaTypeID]
            personal = value['personal'].itervalues().next()
            vehicleCompDesc = personal['typeCompDescr']
            arenaName = i18n.makeString(arenaType.name)
            vt = vehiclesWG.getVehicleType(vehicleCompDesc)
            countryID = vehicleCompDesc >> 4 & 15
            result = 1 if int(personal['team']) == int(value['common']['winnerTeam']) else (0 if not int(value['common']['winnerTeam']) else -1)
            death = 1 if int(personal['deathReason']) > -1 else 0
            if death == 0:
                survive = str(self.config.get('textSurvived', ''))
            else:
                survive = str(self.config.get('textDestroyed', ''))
            place = 1
            arenaUniqueID = value['arenaUniqueID']
            self.arenaUniqueID = value['arenaUniqueID']
            squadsTier = {}
            vehicles = value['vehicles']
            for vehicle in vehicles.values():
                pTypeCompDescr = vehicle[0]['typeCompDescr']
                if pTypeCompDescr is not None:
                    pvt = vehiclesWG.getVehicleType(pTypeCompDescr)
                    tier = pvt.level
                    if set(vehiclesWG.VEHICLE_CLASS_TAGS.intersection(pvt.tags)).pop() == 'lightTank' and tier > 5:
                        tier += 1
                    try:
                        squadId = value['players'][vehicle[0]['accountDBID']]['prebattleID']
                    except:
                        squadID = 0

                    squadsTier[squadId] = max(squadsTier.get(squadId, 0), tier)
                if personal['team'] == vehicle[0]['team'] and personal['originalXP'] < vehicle[0]['xp']:
                    place += 1

            battleTier = max(squadsTier.values())
            proceeds = personal['credits'] - personal['autoRepairCost'] - personal['autoEquipCost'][0] - personal['autoLoadCost'][0]
            tmenXP = personal['tmenXP']
            if 'premium' in vt.tags:
                tmenXP = int(1.5 * tmenXP)
            battle = {'idNum': vehicleCompDesc,
             'arenaTypeID': arenaTypeID,
             'map': arenaType.geometryName,
             'vehicle': vt.name.replace(':', '-'),
             'tier': vt.level,
             'result': result,
             'dailyXPFactor': personal['dailyXPFactor10'] / 10,
             'damage': personal['damageDealt'],
             'damageRec': personal['damageReceived'],
             'potDamageRec': personal['potentialDamageReceived'],
             'damageBlocked': personal['damageBlockedByArmor'],
             'death': death,
             'frag': personal['kills'],
             'spot': personal['spotted'],
             'def': personal['droppedCapturePoints'],
             'cap': personal['capturePoints'],
             'shots': personal['shots'],
             'hits': personal['directHits'],
             'pierced': personal['piercings'],
             'xp': personal['xp'],
             'originalXP': personal['originalXP'],
             'freeXP': personal['freeXP'],
             'place': place,
             'autoRepairCost': personal['autoRepairCost'],
             'autoLoadCost': personal['autoLoadCost'][0],
             'autoEquipCost': personal['autoEquipCost'][0],
             'autoServiceCost': personal['autoEquipCost'][0] + personal['autoLoadCost'][0] + personal['autoRepairCost'],
             'grossCredits': personal['credits'],
             'netCredits': proceeds,
             'bonds': personal['crystal'],
             'gold': personal['gold'] - personal['autoEquipCost'][1] - personal['autoLoadCost'][1],
             'battleTier': battleTier,
             'assist': personal['damageAssistedRadio'] + personal['damageAssistedTrack'],
             'assistRadio': personal['damageAssistedRadio'],
             'assistTrack': personal['damageAssistedTrack']}
            extended = {'vehicle-raw': battle['vehicle'],
             'vehicle-short': vt.shortUserString,
             'vehicle-long': vt.userString,
             'vehicle-country': nations.NAMES[countryID],
             'map-raw': battle['map'],
             'map': arenaName,
             'result': result,
             'survive': survive,
             'place': battle['place'],
             'dailyXPFactor': battle['dailyXPFactor'],
             'tmenXP': tmenXP}
            if self.config.get('dailyAutoReset', True) and self.startDate != stat.getWorkDate():
                self.reset()
            if value['common']['bonusType'] not in self.config.get('ignoreBattleType', []):
                self.battles.append(battle)
                self.save()
                self.updateMessage()
            battleStat, gradient, palette = self.calcWN8([battle])
            extGradient, extPalette = self.refreshColorMacros(extended)
            gradient.update(extGradient)
            palette.update(extPalette)
            self.battleStats[arenaUniqueID] = {}
            self.battleStats[arenaUniqueID]['values'] = battleStat
            self.battleStats[arenaUniqueID]['extendedValues'] = extended
            self.battleStats[arenaUniqueID]['gradient'] = gradient
            self.battleStats[arenaUniqueID]['palette'] = palette
            self.battleResultsBusy.release()
            return

    def reset(self):
        self.page = GENERAL
        self.startDate = self.getWorkDate()
        self.battles = []
        self.save()
        self.updateMessage()

    def mainLoop(self):
        while True:
            arenaUniqueID = self.queue.get()
            self.battleResultsAvailable.wait()
            self.battleResultsBusy.acquire()
            BigWorld.player().battleResultsCache.get(arenaUniqueID, lambda resID, value: self.battleResultsCallback(arenaUniqueID, resID, value, None))

    def refreshColorMacros(self, values):
        gradient = {}
        palette = {}
        if values.get('battlesCount', 1) == 0:
            for key in values.keys():
                gradient[key] = '#FFFFFF'
                palette[key] = '#FFFFFF'

            return (gradient, palette)
        for key in values.keys():
            if self.config.get('gradient', {}).has_key(key):
                colors = self.config.get('gradient', {})[key]
                if values[key] <= colors[0]['value']:
                    gradient[key] = colors[0]['color']
                elif values[key] >= colors[-1]['value']:
                    gradient[key] = colors[-1]['color']
                else:
                    sVal = colors[0]['value']
                    eVal = colors[1]['value']
                    i = 1
                    while eVal < values[key]:
                        sVal = colors[i]['value']
                        i += 1
                        eVal = colors[i]['value']

                    val = float(values[key] - sVal) / (eVal - sVal)
                    gradient[key] = gradColor(colors[i - 1]['color'], colors[i]['color'], val)
            else:
                gradient[key] = '#FFFFFF'
            if self.config.get('palette', {}).has_key(key):
                colors = self.config.get('palette', {})[key]
                palette[key] = colors[-1]['color']
                for item in reversed(colors):
                    if values[key] < item['value']:
                        palette[key] = item['color']
                    break

            palette[key] = '#FFFFFF'

        return (gradient, palette)

    def calcExpected(self, newIdNum):
        v = vehiclesWG.getVehicleType(newIdNum)
        newTier = v.level
        newType = set(vehiclesWG.VEHICLE_CLASS_TAGS.intersection(v.tags)).pop()
        if newTier < 1 or newTier > 10:
            newTier = 10
        tierExpected = {}
        tierExpectedCount = 0.0
        typeExpected = {}
        typeExpectedCount = 0.0
        for idNum in self.expectedValues:
            try:
                vt = vehiclesWG.getVehicleType(idNum)
            except:
                continue

            if vt.level == newTier:
                tierExpectedCount += 1
                vType = set(vehiclesWG.VEHICLE_CLASS_TAGS.intersection(vt.tags)).pop()
                if vType == newType:
                    typeExpectedCount += 1
                for key in self.expectedValues[idNum]:
                    tierExpected[key] = tierExpected.get(key, 0) + self.expectedValues[idNum].get(key, 0.0)
                    if vType == newType:
                        typeExpected[key] = typeExpected.get(key, 0) + self.expectedValues[idNum].get(key, 0.0)

        if typeExpectedCount > 0:
            for key in typeExpected:
                typeExpected[key] /= typeExpectedCount

            self.expectedValues[newIdNum] = typeExpected.copy()
            return
        for key in tierExpected:
            tierExpected[key] /= tierExpectedCount

        self.expectedValues[newIdNum] = tierExpected.copy()

    def calcExpectedKTTC(self, newIdNum):
        v = vehiclesWG.getVehicleType(newIdNum)
        newTier = v.level
        newType = set(vehiclesWG.VEHICLE_CLASS_TAGS.intersection(v.tags)).pop()
        if newTier < 1 or newTier > 10:
            newTier = 10
        tierExpected = {}
        tierExpectedCount = 0.0
        typeExpected = {}
        typeExpectedCount = 0.0
        for idNum in self.expectedKTTCValues:
            try:
                vt = vehiclesWG.getVehicleType(idNum)
            except:
                continue

            if vt.level == newTier:
                tierExpectedCount += 1
                vType = set(vehiclesWG.VEHICLE_CLASS_TAGS.intersection(vt.tags)).pop()
                if vType == newType:
                    typeExpectedCount += 1
                for key in self.expectedKTTCValues[idNum]:
                    tierExpected[key] = tierExpected.get(key, 0) + self.expectedKTTCValues[idNum].get(key, 0.0)
                    if vType == newType:
                        typeExpected[key] = typeExpected.get(key, 0) + self.expectedKTTCValues[idNum].get(key, 0.0)

        if typeExpectedCount > 0:
            for key in typeExpected:
                typeExpected[key] /= typeExpectedCount

            self.expectedKTTCValues[newIdNum] = typeExpected.copy()
            return
        for key in tierExpected:
            tierExpected[key] /= tierExpectedCount

        self.expectedKTTCValues[newIdNum] = tierExpected.copy()

    def calcWN8(self, battles):
        values = {}
        values['battlesCount'] = len(battles)
        totalTier = 0
        totalPlace = 0
        places = []
        totalBattleTier = 0
        valuesKeys = ['winsCount',
         'defeatsCount',
         'drawsCount',
         'totalDmg',
         'totalDmgRec',
         'totalPotDmgRec',
         'totalDamageBlocked',
         'totalDeathsCount',
         'totalSurvivesCount',
         'totalFrag',
         'totalSpot',
         'totalDef',
         'totalCap',
         'totalShots',
         'totalHits',
         'totalPierced',
         'totalAssist',
         'totalXP',
         'totalOriginalXP',
         'totalFreeXP',
         'autoRepairCost',
         'autoLoadCost',
         'autoEquipCost',
         'autoServiceCost',
         'grossCredits',
         'netCredits',
         'bonds',
         'gold',
         'totalAssistRadio',
         'totalAssistTrack']
        for key in valuesKeys:
            values[key] = 0

        expKeys = ['expDamage',
         'expFrag',
         'expSpot',
         'expDef',
         'expWinRate']
        expKTTCKeys = ['expKTTCDamage',
         'expKTTCFrag',
         'expKTTCSpot',
         'expKTTCDef',
         'expKTTCWinRate']
        expValues = {}
        for key in expKeys:
            expValues['total_' + key] = 0.0

        expKTTCValues = {}
        for key in expKTTCKeys:
            expKTTCValues['total_' + key] = 0.0

        resCounters = {-1: 'defeatsCount',
         0: 'drawsCount',
         1: 'winsCount'}
        for battle in battles:
            values[resCounters[battle['result']]] += 1
            values['winsToBattles'] = str(values['winsCount']) + '/' + str(values['battlesCount'])
            values['totalDmg'] += battle['damage']
            values['totalDmgRec'] += battle['damageRec']
            values['totalPotDmgRec'] += battle['potDamageRec']
            values['totalDamageBlocked'] += battle['damageBlocked']
            values['totalDeathsCount'] += battle['death']
            values['totalSurvivesCount'] = values['battlesCount'] - values['totalDeathsCount']
            values['totalFrag'] += battle['frag']
            values['totalSpot'] += battle['spot']
            values['totalDef'] += battle['def']
            values['totalCap'] += battle['cap']
            values['totalShots'] += battle['shots']
            values['totalHits'] += battle['hits']
            values['totalPierced'] += battle['pierced']
            values['totalAssist'] += battle['assist']
            values['totalAssistRadio'] += battle['assistRadio']
            values['totalAssistTrack'] += battle['assistTrack']
            values['totalXP'] += battle['xp']
            values['totalOriginalXP'] += battle['originalXP']
            values['totalFreeXP'] += battle['freeXP']
            values['autoRepairCost'] += battle['autoRepairCost']
            values['autoLoadCost'] += battle['autoLoadCost']
            values['autoEquipCost'] += battle['autoEquipCost']
            values['autoServiceCost'] += battle['autoServiceCost']
            values['grossCredits'] += battle['grossCredits']
            values['netCredits'] += battle['netCredits']
            values['bonds'] += battle['bonds']
            values['gold'] += battle['gold']
            totalTier += battle['tier']
            totalBattleTier += battle['battleTier']
            totalPlace += battle['place']
            places.append(battle['place'])
            idNum = battle['idNum']
            if not self.expectedValues.has_key(idNum):
                self.calcExpected(idNum)
            expValues['total_expDamage'] += self.expectedValues[idNum]['expDamage']
            expValues['total_expFrag'] += self.expectedValues[idNum]['expFrag']
            expValues['total_expSpot'] += self.expectedValues[idNum]['expSpot']
            expValues['total_expDef'] += self.expectedValues[idNum]['expDef']
            expValues['total_expWinRate'] += self.expectedValues[idNum]['expWinRate']
            if not self.expectedKTTCValues.has_key(idNum):
                self.calcExpectedKTTC(idNum)
            expKTTCValues['total_expKTTCDamage'] += self.expectedKTTCValues[idNum]['expDamage']
            expKTTCValues['total_expKTTCFrag'] += self.expectedKTTCValues[idNum]['expFrag']
            expKTTCValues['total_expKTTCSpot'] += self.expectedKTTCValues[idNum]['expSpot']
            expKTTCValues['total_expKTTCDef'] += self.expectedKTTCValues[idNum]['expDef']
            expKTTCValues['total_expKTTCWinRate'] += self.expectedKTTCValues[idNum]['expWinRate']

        if values['battlesCount'] > 0:
            values['winRate'] = float(values['winsCount']) / values['battlesCount'] * 100
            values['avgDamage'] = float(values['totalDmg']) / values['battlesCount']
            values['avgDamageRec'] = int(values['totalDmgRec'] / values['battlesCount'])
            values['avgPotDmgRec'] = int(values['totalPotDmgRec'] / values['battlesCount'])
            values['avgDamageBlocked'] = int(values['totalDamageBlocked'] / values['battlesCount'])
            if values['totalDmgRec'] == 0:
                if values['totalDmg'] == 0:
                    values['dmgDltRecRatio'] = 1
                else:
                    values['dmgDltRecRatio'] = min(1000, values['totalDmg'])
            else:
                values['dmgDltRecRatio'] = float(values['totalDmg']) / float(values['totalDmgRec'])
            values['deathsRate'] = 0 if values['totalDeathsCount'] < 1 else float(values['totalDeathsCount']) / values['battlesCount'] * 100
            values['survivalRate'] = 100 if values['totalDeathsCount'] < 1 else abs(float(values['totalDeathsCount']) / values['battlesCount'] * 100 - 100)
            values['avgFrag'] = float(values['totalFrag']) / values['battlesCount']
            values['avgSpot'] = float(values['totalSpot']) / values['battlesCount']
            values['avgDef'] = float(values['totalDef']) / values['battlesCount']
            values['avgCap'] = float(values['totalCap']) / values['battlesCount']
            values['avgHitsRate'] = float(values['totalHits']) / max(1, values['totalShots']) * 100
            values['avgEffHitsRate'] = float(values['totalPierced']) / max(1, values['totalHits']) * 100
            values['avgAssist'] = int(values['totalAssist']) / values['battlesCount']
            values['avgAssistRadio'] = int(values['totalAssistRadio']) / values['battlesCount']
            values['avgAssistTrack'] = int(values['totalAssistTrack']) / values['battlesCount']
            values['avgXP'] = int(values['totalXP'] / values['battlesCount'])
            values['avgOriginalXP'] = int(values['totalOriginalXP'] / values['battlesCount'])
            values['avgFreeXP'] = int(values['totalFreeXP'] / values['battlesCount'])
            values['avgPremXP'] = int(1.5 * values['avgOriginalXP'])
            values['avgGrossCredits'] = int(values['grossCredits'] / values['battlesCount'])
            values['avgNetCredits'] = int(values['netCredits'] / values['battlesCount'])
            values['avgBonds'] = int(values['bonds'] / values['battlesCount'])
            values['avgAutoServiceCost'] = int(values['autoServiceCost'] / values['battlesCount'])
            values['avgAutoRepairCost'] = int(values['autoRepairCost'] / values['battlesCount'])
            values['avgAutoLoadCost'] = int(values['autoLoadCost'] / values['battlesCount'])
            values['avgAutoEquipCost'] = int(values['autoEquipCost'] / values['battlesCount'])
            values['avgTier'] = float(totalTier) / values['battlesCount']
            values['avgBattleTier'] = float(totalBattleTier) / values['battlesCount']
            values['avgPlace'] = round(float(totalPlace) / values['battlesCount'], 1)
            places = sorted(places)
            length = len(places)
            values['medPlace'] = (places[length / 2] + places[length / 2 - 1]) / 2.0 if not length % 2 else float(places[length / 2])
            for key in expKeys:
                values[key] = expValues['total_' + key] / values['battlesCount']

            for key in expKTTCKeys:
                values[key] = expKTTCValues['total_' + key] / values['battlesCount']

            values['EFF'] = max(0, int(values['avgDamage'] * (10 / (values['avgTier'] + 2)) * (0.23 + 2 * values['avgTier'] / 100) + values['avgFrag'] * 250 + values['avgSpot'] * 150 + math.log(values['avgCap'] + 1, 1.732) * 150 + values['avgDef'] * 150))
            xeffScale = self.xvmscales.get('xeff', None)
            values['XEFF'] = next((i for i, v in enumerate(xeffScale) if v > values['EFF']), 100)
            values['BR'] = max(0, int(values['avgDamage'] * (0.2 + 1.5 / values['avgTier']) + values['avgFrag'] * (350 - values['avgTier'] * 20) + values['avgAssistRadio'] / 2 * (0.2 + 1.5 / values['avgTier']) + values['avgAssistTrack'] / 2 * (0.2 + 1.5 / values['avgTier']) + values['avgSpot'] * 200 + values['avgCap'] * 15 + values['avgDef'] * 15))
            values['WN6'] = max(0, int((1240 - 1040 / min(values['avgTier'], 6) ** 0.164) * values['avgFrag'] + values['avgDamage'] * 530 / (184 * math.exp(0.24 * values['avgTier']) + 130) + values['avgSpot'] * 125 + min(values['avgDef'], 2.2) * 100 + (185 / (0.17 + math.exp((values['winRate'] - 35) * -0.134)) - 500) * 0.45 + (6 - min(values['avgTier'], 6)) * -60))
            values['XWN6'] = 100 if values['WN6'] > 2350 else int(round(max(0, min(100, values['WN6'] * (values['WN6'] * (values['WN6'] * (values['WN6'] * (values['WN6'] * (-values['WN6'] * 8.52e-19 + 8.649e-15) - 3.9744e-11) + 8.406e-08) - 7.446e-05) + 0.06904) - 6.19))))
            values['WN7'] = max(0, int((1240 - 1040 / min(values['avgTier'], 6) ** 0.164) * values['avgFrag'] + values['avgDamage'] * 530 / (184 * math.exp(0.24 * values['avgTier']) + 130) + values['avgSpot'] * 125 * min(values['avgTier'], 3) / 3 + min(values['avgDef'], 2.2) * 100 + (185 / (0.17 + math.exp((values['winRate'] - 35) * -0.134)) - 500) * 0.45 - (5 - min(values['avgTier'], 5)) * 125 / (1 + math.exp((values['avgTier'] - (values['battlesCount'] / 220) ** (3 / values['avgTier'])) * 1.5))))
            values['XWN7'] = 100 if values['WN7'] > 2350 else int(round(max(0, min(100, values['WN7'] * (values['WN7'] * (values['WN7'] * (values['WN7'] * (values['WN7'] * (values['WN7'] * 1.641e-18 - 1.26e-14) + 3.223e-11) - 3.793e-08) + 3.139e-05) + 0.02747) - 1.92))))
        else:
            for key in ['winRate',
             'avgDamage',
             'avgDamageRec',
             'avgPotDmgRec',
             'avgDamageBlocked',
             'dmgDltRecRatio',
             'deathsRate',
             'survivalRate',
             'avgFrag',
             'avgSpot',
             'avgDef',
             'avgCap',
             'avgHitsRate',
             'avgEffHitsRate',
             'avgAssist',
             'avgXP',
             'avgOriginalXP',
             'avgFreeXP',
             'avgPremXP',
             'avgGrossCredits',
             'avgNetCredits',
             'avgBonds',
             'avgAutoServiceCost',
             'avgAutoRepairCost',
             'avgAutoLoadCost',
             'avgAutoEquipCost',
             'avgTier',
             'avgBattleTier',
             'avgPlace',
             'medPlace',
             'WN6',
             'XWN6',
             'EFF',
             'XEFF',
             'BR',
             'WN7',
             'XWN7']:
                values[key] = 0

        for key in expKeys:
            values[key] = 1

        for key in expKTTCKeys:
            values[key] = 1

        values['rDAMAGE'] = values['avgDamage'] / values['expDamage']
        values['rSPOT'] = values['avgSpot'] / values['expSpot']
        values['rFRAG'] = values['avgFrag'] / values['expFrag']
        values['rDEF'] = values['avgDef'] / values['expDef']
        values['rWIN'] = values['winRate'] / values['expWinRate']
        values['rWINc'] = max(0, (values['rWIN'] - 0.71) / 0.29000000000000004)
        values['rDAMAGEc'] = max(0, (values['rDAMAGE'] - 0.22) / 0.78)
        values['rFRAGc'] = max(0, min(values['rDAMAGEc'] + 0.2, (values['rFRAG'] - 0.12) / 0.88))
        values['rSPOTc'] = max(0, min(values['rDAMAGEc'] + 0.1, (values['rSPOT'] - 0.38) / 0.62))
        values['rDEFc'] = max(0, min(values['rDAMAGEc'] + 0.1, (values['rDEF'] - 0.1) / 0.9))
        values['WN8'] = 980 * values['rDAMAGEc'] + 210 * values['rDAMAGEc'] * values['rFRAGc'] + 155 * values['rFRAGc'] * values['rSPOTc'] + 75 * values['rDEFc'] * values['rFRAGc'] + 145 * min(1.8, values['rWINc'])
        xwn8Scale = self.xvmscales.get('xwn8', None)
        values['XWN8'] = next((i for i, v in enumerate(xwn8Scale) if v > values['WN8']), 100)
        values['rKTTCDAMAGE'] = values['avgDamage'] / values['expKTTCDamage']
        values['rKTTCSPOT'] = values['avgSpot'] / values['expKTTCSpot']
        values['rKTTCFRAG'] = values['avgFrag'] / values['expKTTCFrag']
        values['rKTTCDEF'] = values['avgDef'] / values['expKTTCDef']
        values['rKTTCWIN'] = values['winRate'] / values['expKTTCWinRate']
        values['rKTTCWINc'] = max(0, (values['rKTTCWIN'] - 0.71) / 0.29000000000000004)
        values['rKTTCDAMAGEc'] = max(0, (values['rKTTCDAMAGE'] - 0.22) / 0.78)
        values['rKTTCFRAGc'] = max(0, min(values['rKTTCDAMAGEc'] + 0.2, (values['rKTTCFRAG'] - 0.12) / 0.88))
        values['rKTTCSPOTc'] = max(0, min(values['rKTTCDAMAGEc'] + 0.1, (values['rKTTCSPOT'] - 0.38) / 0.62))
        values['rKTTCDEFc'] = max(0, min(values['rKTTCDAMAGEc'] + 0.1, (values['rKTTCDEF'] - 0.1) / 0.9))
        values['WN8KTTC'] = 980 * values['rKTTCDAMAGEc'] + 210 * values['rKTTCDAMAGEc'] * values['rKTTCFRAGc'] + 155 * values['rKTTCFRAGc'] * values['rKTTCSPOTc'] + 75 * values['rKTTCDEFc'] * values['rKTTCFRAGc'] + 145 * min(1.8, values['rKTTCWINc'])
        values['XWN8KTTC'] = next((i for i, v in enumerate(xwn8Scale) if v > values['WN8KTTC']), 100)
        xtosup = ['1.2',
         '1.5',
         '1.9',
         '2.5',
         '3.1',
         '3.8',
         '4.6',
         '5.5',
         '6.6',
         '7.7',
         '9.0',
         '10',
         '12',
         '14',
         '15',
         '17',
         '19',
         '21',
         '24',
         '26',
         '28',
         '31',
         '33',
         '36',
         '38',
         '41',
         '43',
         '46',
         '48',
         '51',
         '53',
         '56',
         '58',
         '60',
         '63',
         '65',
         '67',
         '69',
         '71',
         '73',
         '74',
         '76',
         '78',
         '79',
         '80.8',
         '82.2',
         '83.6',
         '84.8',
         '86.0',
         '87.1',
         '88.1',
         '89.0',
         '89.9',
         '90.8',
         '91.6',
         '92.3',
         '92.9',
         '93.6',
         '94.1',
         '94.7',
         '95.1',
         '95.6',
         '96.0',
         '96.4',
         '96.7',
         '97.0',
         '97.3',
         '97.6',
         '97.8',
         '98.0',
         '98.2',
         '98.4',
         '98.6',
         '98.7',
         '98.9',
         '99.0',
         '99.1',
         '99.2',
         '99.3',
         '99.37',
         '99.44',
         '99.51',
         '99.57',
         '99.62',
         '99.67',
         '99.71',
         '99.75',
         '99.78',
         '99.81',
         '99.84',
         '99.86',
         '99.88',
         '99.90',
         '99.92',
         '99.93',
         '99.95',
         '99.96',
         '99.97',
         '99.98',
         '99.99']
        values['SUP'] = xtosup[max(0, min(100, values['XWN8'] - 1))]
        values['avgBattleTierDiff'] = values['avgBattleTier'] - values['avgTier']
        values['WN8'] = int(round(values['WN8']))
        values['WN8KTTC'] = int(round(values['WN8KTTC']))
        values['avgDamage'] = int(values['avgDamage'])
        gradient, palette = self.refreshColorMacros(values)
        return (values, gradient, palette)

    def formatMacros(self, val, width=1, prec=2, sign=''):
        if type(val) == str:
            sStr = '{:>{w}}'.format(val, w=width)
            return sStr
        if sign != '+':
            sVal = '{:>{w},.{pr}f}'.format(val, w=width, pr=prec) if type(val) is float else '{:>{w},d}'.format(int(round(val)), w=width)
        else:
            sVal = '{:>+{w},.{pr}f}'.format(val, w=width, pr=prec) if type(val) is float else '{:>+{w},d}'.format(int(round(val)), w=width)
        separator = self.config.get('thousandSeparator', ' ')
        sVal = sVal.replace(',', separator)
        return sVal

    def applyMacros(self, text, values, gradient, palette):
        for key in values.keys():
            text = text.replace('{{%s}}' % key, self.formatMacros(values[key]))
            text = text.replace('{{%s:d}}' % key, self.formatMacros(values[key], 1, 0))
            text = text.replace('{{%s:+d}}' % key, self.formatMacros(values[key], 1, 0, '+'))
            for xx in xrange(1, 21):
                text = text.replace('{{%s:%d}}' % (key, xx), self.formatMacros(values[key], xx, 0))
                text = text.replace('{{%s:+%d}}' % (key, xx), self.formatMacros(values[key], xx, 0, '+'))
                text = text.replace('{{%s:%d.1f}}' % (key, xx), self.formatMacros(values[key], xx, 1))
                text = text.replace('{{%s:+%d.1f}}' % (key, xx), self.formatMacros(values[key], xx, 1, '+'))
                text = text.replace('{{%s:%d.2f}}' % (key, xx), self.formatMacros(values[key], xx, 2))
                text = text.replace('{{%s:+%d.2f}}' % (key, xx), self.formatMacros(values[key], xx, 2, '+'))

            text = text.replace('{{g:%s}}' % key, gradient[key])
            text = text.replace('{{c:%s}}' % key, palette[key])

        return text

    def applyUserMacros(self, text):
        """Applying User Macros"""
        userMacros = self.config.get('userMacros', {})
        for key in userMacros.keys():
            text = text.replace('{{%s}}' % key, userMacros[key])

        return text

    def formatButtons(self, msg, msgPage=''):
        if msgPage == 'Tanks':
            msg = msg.replace('{{ButtonGeneral}}', self.config['Button']['General']['format'] if self.config['Button']['General']['enable'] else '')
            msg = msg.replace('{{ButtonTanks}}', self.config['ButtonON']['Tanks'] if self.config['Button']['Tanks']['enable'] else '')
            msg = msg.replace('{{ButtonMaps}}', self.config['Button']['Maps']['format'] if self.config['enableStatisticsByMap'] and self.config['Button']['Maps']['enable'] else '')
            msg = msg.replace('{{ButtonReset}}', self.config['Button']['Reset']['format'] if self.config['Button']['Reset']['enable'] else '')
            msg = msg.replace('{{ButtonTankSortByMacro1}}', self.config['Button']['TankSortByMacro1']['format'])
            msg = msg.replace('{{ButtonTankSortByMacro2}}', self.config['Button']['TankSortByMacro2']['format'])
            msg = msg.replace('{{ButtonTankSortByMacro3}}', self.config['Button']['TankSortByMacro3']['format'])
            msg = msg.replace('{{ButtonTankSortByMacro4}}', self.config['Button']['TankSortByMacro4']['format'])
            msg = msg.replace('{{ButtonTankSortByMacro5}}', self.config['Button']['TankSortByMacro5']['format'])
            msg = msg.replace('{{ButtonTankSortByMacro6}}', self.config['Button']['TankSortByMacro6']['format'])
            msg = msg.replace('{{ButtonTankSortByMacro7}}', self.config['Button']['TankSortByMacro7']['format'])
            msg = msg.replace('{{ButtonTankSortByMacro8}}', self.config['Button']['TankSortByMacro8']['format'])
            msg = msg.replace('{{ButtonTankSortByMacro9}}', self.config['Button']['TankSortByMacro9']['format'])
            msg = msg.replace('{{ButtonTankSortByMacro10}}', self.config['Button']['TankSortByMacro10']['format'])
        elif msgPage == 'Maps':
            msg = msg.replace('{{ButtonGeneral}}', self.config['Button']['General']['format'] if self.config['Button']['General']['enable'] else '')
            msg = msg.replace('{{ButtonTanks}}', self.config['Button']['Tanks']['format'] if self.config['enableStatisticsByTank'] and self.config['Button']['Tanks']['enable'] else '')
            msg = msg.replace('{{ButtonMaps}}', self.config['ButtonON']['Maps'] if self.config['Button']['Maps']['enable'] else '')
            msg = msg.replace('{{ButtonReset}}', self.config['Button']['Reset']['format'] if self.config['Button']['Reset']['enable'] else '')
            msg = msg.replace('{{ButtonMapSortByMacro1}}', self.config['Button']['MapSortByMacro1']['format'])
            msg = msg.replace('{{ButtonMapSortByMacro2}}', self.config['Button']['MapSortByMacro2']['format'])
            msg = msg.replace('{{ButtonMapSortByMacro3}}', self.config['Button']['MapSortByMacro3']['format'])
            msg = msg.replace('{{ButtonMapSortByMacro4}}', self.config['Button']['MapSortByMacro4']['format'])
            msg = msg.replace('{{ButtonMapSortByMacro5}}', self.config['Button']['MapSortByMacro5']['format'])
        elif msgPage == 'Reset':
            msg = msg.replace('{{ButtonResetYes}}', self.config['Button']['ResetYes']['format'])
            msg = msg.replace('{{ButtonResetNo}}', self.config['Button']['ResetNo']['format'])
        else:
            msg = msg.replace('{{ButtonGeneral}}', self.config['ButtonON']['General'] if self.config['Button']['General']['enable'] and (self.config['enableStatisticsByTank'] or self.config['enableStatisticsByMap']) else '')
            msg = msg.replace('{{ButtonTanks}}', self.config['Button']['Tanks']['format'] if self.config['enableStatisticsByTank'] and self.config['Button']['Tanks']['enable'] else '')
            msg = msg.replace('{{ButtonMaps}}', self.config['Button']['Maps']['format'] if self.config['enableStatisticsByMap'] and self.config['Button']['Maps']['enable'] else '')
            msg = msg.replace('{{ButtonReset}}', self.config['Button']['Reset']['format'] if self.config['Button']['Reset']['enable'] else '')
        return msg

    def updateMessage(self):
        if not self.configIsValid:
            self.message = 'stat_config.json is not valid'
            return
        defaultTankSortingMacros = ({'tankSortingMacro1': 'battlesCount',
          'tankSortingMacro2': 'WN8',
          'tankSortingMacro3': 'winRate',
          'tankSortingMacro4': 'netCredits',
          'tankSortingMacro5': 'vehicle-short',
          'tankSortingMacro6': 'avgDamage',
          'tankSortingMacro7': 'avgFrag',
          'tankSortingMacro8': 'totalDmg',
          'tankSortingMacro9': 'totalFrag',
          'tankSortingMacro10': 'totalXP'},)
        defaultMapSortingMacros = ({'mapSortingMacro1': 'map',
          'mapSortingMacro2': 'winRate',
          'mapSortingMacro3': 'WN8',
          'mapSortingMacro4': 'battlesCount',
          'mapSortingMacro5': 'netCredits'},)
        self.tankSortingMacros = self.config.get('tankSortingMacros', defaultTankSortingMacros)
        self.mapSortingMacros = self.config.get('mapSortingMacros', defaultMapSortingMacros)
        self.values, self.gradient, self.palette = self.calcWN8(self.battles)
        bg = self.config.get('bgIcon', '')
        self.bgIcon = self.applyMacros(bg, self.values, self.gradient, self.palette)
        if len(self.battles) < 1 and self.config.get('info', True):
            msg = '\n'.join(self.config.get('text', ''))
        else:
            msg = '\n'.join(self.config.get('template', ''))
        msg = self.applyUserMacros(msg)
        msg = self.applyMacros(msg, self.values, self.gradient, self.palette)
        msg = self.formatButtons(msg)
        self.messageGeneral = msg
        self.updateMessageTanks(self.tankSortingMacros['tankSortingMacro1'])
        self.updateMessageMaps(self.mapSortingMacros['mapSortingMacro1'])

    def updateMessageTanks(self, tankSortingMacro):
        """Statistics massage 'By Tank'."""
        if self.config['enableStatisticsByTank']:
            msgPage = 'Tanks'
            rows = ''
            tankStat = {}
            for battle in self.battles:
                idNum = battle['idNum']
                if tankStat.has_key(idNum):
                    tankStat[idNum].append(battle)
                tankStat[idNum] = [battle]

            tankValues = {}
            tankGradient = {}
            tankPalette = {}
            tankSortReverse = self.config.get('tankSortReverse', True)
            for idNum in tankStat.keys():
                values, gradient, palette = self.calcWN8(tankStat[idNum])
                tankValues[idNum] = values
                tankGradient[idNum] = gradient
                tankPalette[idNum] = palette

            sortedTankIDs = []
            if tankSortingMacro == 'vehicle-short':
                idTankNames = {}
                for idNum in tankStat.keys():
                    vt = vehiclesWG.getVehicleType(idNum)
                    idTankNames[idNum] = vt.userString
                    sortedTankIDs = sorted(idTankNames.keys(), key=lambda value: idTankNames[value])

            else:
                sortedTankIDs = sorted(tankValues.keys(), key=lambda value: tankValues[value][tankSortingMacro], reverse=tankSortReverse)
            for idNum in sortedTankIDs:
                row = '\n'.join(self.config.get('ByTankRows', ''))
                row = self.applyUserMacros(row)
                vt = vehiclesWG.getVehicleType(idNum)
                countryID = idNum >> 4 & 15
                row = row.replace('{{vehicle-long}}', vt.userString)
                row = row.replace('{{vehicle-short}}', vt.shortUserString)
                row = row.replace('{{vehicle-raw}}', vt.name.replace(':', '-'))
                row = row.replace('{{vehicle-country}}', str(nations.NAMES[countryID]))
                row = self.applyMacros(row, tankValues[idNum], tankGradient[idNum], tankPalette[idNum])
                rows += row + '\n'

            msg = '\n'.join(self.config.get('byTankTemplate', ''))
            msg = msg.replace('{{ByTankRows}}', rows)
            msg = self.formatButtons(msg, msgPage)
            self.messageByTank = msg

    def updateMessageMaps(self, mapSortingMacro):
        """Statistics massage 'By Map'."""
        if self.config['enableStatisticsByMap']:
            msgPage = 'Maps'
            rows = ''
            mapStat = {}
            for battle in self.battles:
                mapID = battle['arenaTypeID'] & 32767
                if mapStat.has_key(mapID):
                    mapStat[mapID].append(battle)
                mapStat[mapID] = [battle]

            mapValues = {}
            mapGradient = {}
            mapPalette = {}
            mapSortReverse = self.config.get('mapSortReverse', True)
            for mapID in mapStat.keys():
                values, gradient, palette = self.calcWN8(mapStat[mapID])
                mapValues[mapID] = values
                mapGradient[mapID] = gradient
                mapPalette[mapID] = palette

            sortedMapIDs = []
            if mapSortingMacro == 'map':
                idMapNames = {}
                for mapID in mapStat.keys():
                    arenaTypeID = mapStat[mapID][0]['arenaTypeID']
                    arenaType = ArenaType.g_cache[arenaTypeID]
                    arenaName = i18n.makeString(arenaType.name)
                    idMapNames[mapID] = arenaName
                    sortedMapIDs = sorted(idMapNames.keys(), key=lambda value: idMapNames[value])

            else:
                sortedMapIDs = sorted(mapValues.keys(), key=lambda value: mapValues[value][mapSortingMacro], reverse=mapSortReverse)
            for mapID in sortedMapIDs:
                row = '\n'.join(self.config.get('ByMapRows', ''))
                row = self.applyUserMacros(row)
                arenaTypeID = mapStat[mapID][0]['arenaTypeID']
                arenaType = ArenaType.g_cache[arenaTypeID]
                arenaName = i18n.makeString(arenaType.name)
                arenaNameDecoded = arenaName.decode('utf-8')
                row = row.replace('{{map}}', arenaName)
                row = row.replace('{{map-raw}}', arenaType.geometryName)
                for xx in xrange(5, 31):
                    xxcut = xx - 2
                    row = row.replace('{{map:%d}}' % xx, '{:.{w}}'.format(arenaNameDecoded, w=xx) if len(arenaNameDecoded) <= xx else '{:.{w}}'.format(arenaNameDecoded, w=xxcut) + '..')

                row = row.encode('utf-8')
                row = self.applyMacros(row, mapValues[mapID], mapGradient[mapID], mapPalette[mapID])
                rows += row + '\n'

            msg = '\n'.join(self.config.get('byMapTemplate', ''))
            msg = msg.replace('{{ByMapRows}}', rows)
            msg = self.formatButtons(msg, msgPage)
            self.messageByMap = msg

    def confirmResetMessage(self):
        """Reset confirmation message."""
        if self.config.get('enableResetConfirmation', True):
            msgPage = 'Reset'
            msg = '\n'.join(self.config.get('resetConfirmationText', ''))
            msg = self.formatButtons(msg, msgPage)
            self.messageReset = msg
        else:
            self.reset()

    def replaceBattleResultMessage(self, message, arenaUniqueID):
        message = unicode(message, 'utf-8')
        if self.config.get('debugBattleResultMessage', False):
            LOG_NOTE(message)
        basicValues = self.battleStats[arenaUniqueID]['values']
        extendedValues = self.battleStats[arenaUniqueID]['extendedValues']
        values = basicValues
        values.update(extendedValues)
        for pattern in self.battleStatPatterns:
            try:
                if not eval(pattern.get('condition')):
                    continue
            except:
                print '[wotstat] Invalid calculation condition ' + pattern.get('condition')
                continue

            message = re.sub(pattern.get('pattern', ''), pattern.get('repl', ''), message)

        battleStatText = '\n'.join(self.config.get('battleStatText', ''))
        gradient = self.battleStats[arenaUniqueID]['gradient']
        palette = self.battleStats[arenaUniqueID]['palette']
        battleStatText = self.applyUserMacros(battleStatText)
        message = message + "\n<font color='#929290'>" + battleStatText + '</font>'
        message = self.applyMacros(message, values, gradient, palette)
        return message

    def hideMessagePatterns(self, message):
        for element in stat.config.get('hideMessagePatterns', []):
            element = element.decode('utf-8').lower().encode('utf-8')
            if message.decode('utf-8').lower().encode('utf-8').find(element) != -1:
                return False

        return True

    def mod_popUpListViewer(self):
        try:
            import gui.mods.mod_PopUpListViewer
            return False
        except:
            return True

    def expandStatNotificationList(self, item):
        savedData = item['message'].get('savedData', -1)
        arenaUniqueID = -1
        if isinstance(savedData, long):
            arenaUniqueID = int(savedData)
        elif isinstance(savedData, tuple):
            arenaUniqueID = int(savedData[0])
        message = item['message'].get('message', '')
        self.arenaUniqueIDsss = arenaUniqueID
        if arenaUniqueID > 0 and self.battleStats.has_key(arenaUniqueID) and type(message) == str:
            message = self.replaceBattleResultMessage(message, arenaUniqueID)
            item['message']['message'] = message
            if self.config.get('overwriteBattleResultBgIcon', False):
                result = self.battleStats[arenaUniqueID]['extendedValues']['result']
                bgIconKeys = {-1: 'bgIconDefeat',
                 0: 'bgIconDraw',
                 1: 'bgIconWin'}
                bgIconKey = bgIconKeys[result]
                bgIcon = self.config.get(bgIconKey, item['message']['bgIcon'])
                item['message']['bgIcon'] = bgIcon
        return item


stat = SessionStatistic()

def new_onBecomePlayer(self):
    old_onBecomePlayer(self)
    stat.battleResultsAvailable.set()
    stat.load()


old_onBecomePlayer = Account.onBecomePlayer
Account.onBecomePlayer = new_onBecomePlayer

def new_onBecomeNonPlayer(self):
    stat.battleResultsAvailable.clear()
    old_onBecomeNonPlayer(self)


old_onBecomeNonPlayer = Account.onBecomeNonPlayer
Account.onBecomeNonPlayer = new_onBecomeNonPlayer

def new_nlv_getMessagesList(self):
    result = old_nlv_getMessagesList(self)
    if stat.config.get('onlineReloadConfig', False):
        stat.readConfig()
        stat.updateMessage()
        stat.config['onlineReloadConfig'] = True
    if self._NotificationListView__currentGroup in 'info':
        result.append(stat.createMessage())
    return result


old_nlv_getMessagesList = NotificationListView._NotificationListView__getMessagesList
NotificationListView._NotificationListView__getMessagesList = new_nlv_getMessagesList

def new_notificationsListMeta_as_setMessagesListS(self, value):
    if stat.mod_popUpListViewer() and len(stat.config.get('hideMessagePatterns', [])):
        formed = []
        for item in value['messages']:
            text = item['message']['message']
            if stat.hideMessagePatterns(text):
                formed.append(item)
                value['messages'] = formed

    if stat.config.get('showStatForBattle', True):
        value['messages'] = map(stat.expandStatNotificationList, value['messages'])
    old_notificationsListMeta_as_setMessagesListS(self, {'messages': value['messages'],
     'emptyListText': value['emptyListText'],
     'btnBarSelectedIdx': value['btnBarSelectedIdx']})


old_notificationsListMeta_as_setMessagesListS = NotificationsListMeta.as_setMessagesListS
NotificationsListMeta.as_setMessagesListS = new_notificationsListMeta_as_setMessagesListS

def new_onClickAction(self, typeID, entityID, action):
    if 'statPage:' in action:
        if action.split(':')[1] == 'General':
            stat.page = 0
        elif action.split(':')[1] == 'Tanks':
            stat.page = 11
        elif action.split(':')[1] == 'Maps':
            stat.page = 21
        elif action.split(':')[1] == 'TankSortByMacro1':
            stat.page = 11
            stat.updateMessageTanks(stat.tankSortingMacros['tankSortingMacro1'])
        elif action.split(':')[1] == 'TankSortByMacro2':
            stat.page = 12
            stat.updateMessageTanks(stat.tankSortingMacros['tankSortingMacro2'])
        elif action.split(':')[1] == 'TankSortByMacro3':
            stat.page = 13
            stat.updateMessageTanks(stat.tankSortingMacros['tankSortingMacro3'])
        elif action.split(':')[1] == 'TankSortByMacro4':
            stat.page = 14
            stat.updateMessageTanks(stat.tankSortingMacros['tankSortingMacro4'])
        elif action.split(':')[1] == 'TankSortByMacro5':
            stat.page = 15
            stat.updateMessageTanks(stat.tankSortingMacros['tankSortingMacro5'])
        elif action.split(':')[1] == 'TankSortByMacro6':
            stat.page = 16
            stat.updateMessageTanks(stat.tankSortingMacros['tankSortingMacro6'])
        elif action.split(':')[1] == 'TankSortByMacro7':
            stat.page = 17
            stat.updateMessageTanks(stat.tankSortingMacros['tankSortingMacro7'])
        elif action.split(':')[1] == 'TankSortByMacro8':
            stat.page = 18
            stat.updateMessageTanks(stat.tankSortingMacros['tankSortingMacro8'])
        elif action.split(':')[1] == 'TankSortByMacro9':
            stat.page = 19
            stat.updateMessageTanks(stat.tankSortingMacros['tankSortingMacro9'])
        elif action.split(':')[1] == 'TankSortByMacro10':
            stat.page = 20
            stat.updateMessageTanks(stat.tankSortingMacros['tankSortingMacro10'])
        elif action.split(':')[1] == 'MapSortByMacro1':
            stat.page = 21
            stat.updateMessageMaps(stat.mapSortingMacros['mapSortingMacro1'])
        elif action.split(':')[1] == 'MapSortByMacro2':
            stat.page = 22
            stat.updateMessageMaps(stat.mapSortingMacros['mapSortingMacro2'])
        elif action.split(':')[1] == 'MapSortByMacro3':
            stat.page = 23
            stat.updateMessageMaps(stat.mapSortingMacros['mapSortingMacro3'])
        elif action.split(':')[1] == 'MapSortByMacro4':
            stat.page = 24
            stat.updateMessageMaps(stat.mapSortingMacros['mapSortingMacro4'])
        elif action.split(':')[1] == 'MapSortByMacro5':
            stat.page = 25
            stat.updateMessageMaps(stat.mapSortingMacros['mapSortingMacro5'])
        elif action.split(':')[1] == 'Reset':
            stat.page = 30
            stat.confirmResetMessage()
        elif action.split(':')[1] == 'ResetYes':
            stat.reset()
        elif action.split(':')[1] == 'ResetNo':
            stat.page = 0
        self.as_updateMessageS(stat.createMessage())
    else:
        old_nlv_onClickAction(self, typeID, entityID, action)


old_nlv_onClickAction = NotificationListView.onClickAction
NotificationListView.onClickAction = new_onClickAction

def new_npuv_sendMessageForDisplay(self, notification):
    if stat.config.get('showPopUp', True):
        old_npuv_sendMessageForDisplay(self, notification)


old_npuv_sendMessageForDisplay = NotificationPopUpViewer._NotificationPopUpViewer__sendMessageForDisplay
NotificationPopUpViewer._NotificationPopUpViewer__sendMessageForDisplay = new_npuv_sendMessageForDisplay

def new_brf_format(self, message, *args):
    result = old_brf_format(self, message, *args)
    arenaUniqueID = message.data.get('arenaUniqueID', 0)
    stat.queue.put(arenaUniqueID)
    if stat.config.get('enableBattleEndedMessage', True) and hasattr(BigWorld.player(), 'arena'):
        if BigWorld.player().arena.arenaUniqueID != arenaUniqueID:
            isWinner = message.data.get('isWinner', 0)
            battleEndedMessage = ''
            if isWinner < 0:
                battleEndedMessage = stat.config.get('battleEndedMessageDefeat', '')
            elif isWinner > 0:
                battleEndedMessage = stat.config.get('battleEndedMessageWin', '')
            else:
                battleEndedMessage = stat.config.get('battleEndedMessageDraw', '')
            battleEndedMessage = battleEndedMessage.encode('utf-8')
            playerVehicles = message.data['playerVehicles'].itervalues().next()
            vehicleCompDesc = playerVehicles['vehTypeCompDescr']
            vt = vehiclesWG.getVehicleType(vehicleCompDesc)
            battleEndedMessage = battleEndedMessage.replace('{{vehicle-long}}', vt.userString)
            battleEndedMessage = battleEndedMessage.replace('{{vehicle-short}}', vt.shortUserString)
            name = vt.name.replace(':', '-')
            battleEndedMessage = battleEndedMessage.replace('{{vehicle-raw}}', name)
            arenaTypeID = message.data.get('arenaTypeID', 0)
            arenaType = ArenaType.g_cache[arenaTypeID]
            arenaName = i18n.makeString(arenaType.name)
            xp = message.data.get('xp', 0)
            credits = message.data.get('credits', 0)
            separator = stat.config.get('thousandSeparator', ' ')
            sXp = format(xp, ',d')
            sCredits = format(credits, ',d')
            sXp = sXp.replace(',', separator)
            sCredits = sCredits.replace(',', separator)
            battleEndedMessage = battleEndedMessage.replace('{{map}}', arenaName)
            battleEndedMessage = battleEndedMessage.replace('{{map-raw}}', arenaType.geometryName)
            battleEndedMessage = battleEndedMessage.replace('{{totalXP}}', sXp)
            battleEndedMessage = battleEndedMessage.replace('{{grossCredits}}', sCredits)
            MessengerEntry.g_instance.gui.addClientMessage(battleEndedMessage)
    return result


old_brf_format = BattleResultsFormatter.format
BattleResultsFormatter.format = new_brf_format

def new_regularArenaFullNameItem(self, record, reusable):
    arenaGuiType = reusable.common.arenaGuiType
    arenaType = reusable.common.arenaType
    if arenaGuiType == ARENA_GUI_TYPE.RANDOM:
        i18nKey = _ARENA_TYPE_FORMAT.format(arenaType.getGamePlayName())
    else:
        i18nKey = _ARENA_TYPE_EXT_FORMAT.format(arenaGuiType)
    if stat.config['battleResultsWindow']['enable'] and stat.battleStats.has_key(reusable._ReusableInfo__arenaUniqueID):
        values = stat.battleStats[reusable._ReusableInfo__arenaUniqueID]['values']
        values.update(stat.battleStats[reusable._ReusableInfo__arenaUniqueID]['extendedValues'])
        gradient = stat.battleStats[reusable._ReusableInfo__arenaUniqueID]['gradient']
        palette = stat.battleStats[reusable._ReusableInfo__arenaUniqueID]['palette']
        return stat.applyMacros(stat.config['battleResultsWindow']['format'].replace('{{arenaType}}', i18n.makeString(arenaType.getName())).replace('{{arenaGuiType}}', i18n.makeString(i18nKey)), values, gradient, palette)
    old_regularArenaFullNameItem(self, record, reusable)


old_regularArenaFullNameItem = RegularArenaFullNameItem._convert
RegularArenaFullNameItem._convert = new_regularArenaFullNameItem

 

error in python.log

 

2019-12-09 19:18:22.276: INFO: =============================
2019-12-09 19:18:22.276: INFO: 2019-12-09 19:18:22: [ERROR] mods/xfw_libraries/xfw/events.pyc
2019-12-09 19:18:22.276: ERROR: Traceback (most recent call last):
2019-12-09 19:18:22.276: ERROR:   File "./xfw/events.py", line 56, in __event_handler
2019-12-09 19:18:22.276: ERROR:   File "D:\Mods\World_of_Tanks\SessionStatistics\mod_stat.py", line 1169, in new_onBecomePlayer
2019-12-09 19:18:22.276: ERROR:   File "D:\Mods\World_of_Tanks\SessionStatistics\mod_stat.py", line 128, in load
2019-12-09 19:18:22.276: ERROR:   File "scripts/common/Lib/json/__init__.py", line 290, in load
2019-12-09 19:18:22.276: ERROR:   File "scripts/common/Lib/json/__init__.py", line 338, in loads
2019-12-09 19:18:22.277: ERROR:   File "scripts/common/Lib/json/decoder.py", line 366, in decode
2019-12-09 19:18:22.277: ERROR:   File "scripts/common/Lib/json/decoder.py", line 384, in raw_decode
2019-12-09 19:18:22.277: ERROR: ValueError: No JSON object could be decoded
2019-12-09 19:18:22.277: INFO: =============================

 

mod_stat.pyc

SessionStatistics&BattleResults_1.6.1.0_r01x.zip

Edited by Heliomalt
error in python.log

Share this post


Link to post

Short link
Share on other sites

@sirmax , проблема не массовая?

По крайней мере с ней не столкнулся.

Edited by night_dragon_on

Share this post


Link to post

Short link
Share on other sites

  The wn8exp.json data download is working, but the xvmscales.json download is an empty file locally, no error, no data.

Share this post


Link to post

Short link
Share on other sites

@sirmax Yes, link in browser is working. Maybe something to do with that?

 

Edit: Seems fixed now.

 

Thanks for your effort!

Edited by Heliomalt
Fixed
  • Upvote 1

Share this post


Link to post

Short link
Share on other sites
Guest
This topic is now closed to further replies.
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...