Author SHA1 Message Date
Jylpah bca474218e WoTinspector.read_replay_id(): added back 2022-10-29 22:21:14 +03:00
Jylpah e48759ce79 ThrottledClientSession(), WoTinspector(): updated 2022-10-29 22:14:33 +03:00
Jylpah 5c694fd721 Typing + remove unsed imports 2022-10-29 22:13:52 +03:00
Jylpah 1f41901042 update 9.2 2022-10-09 00:18:30 +03:00
Jylpah d275914b32 Update .gitignore 2022-08-18 09:44:39 +03:00
Jylpah 9f8c82f434 Stream client support 2022-08-18 09:42:55 +03:00
Jylpah 3a8f065f35 Update 9.2 2022-08-18 09:42:32 +03:00
Jylpah 0cac200f06 Update 9.1 2022-07-14 11:25:38 +03:00
Jylpah 342b90bcf9 SSL -> TLS 2022-06-09 20:48:50 +03:00
Jylpah a9845b6fbb Update 9.0 2022-06-08 17:25:06 +03:00
7 changed files with 661 additions and 262 deletions

No files matched your search

Executable
+15
View File
@@ -0,0 +1,15 @@
__pycache__/
BlitzApp
tmp/
ssl/
*.ini
*.swp
*.code-workspace
*.sqlite3
.vscode/
*.sqlite3-journal
.gitattributes
export*
*.csv
!/stats/*.csv
!*.py
+11 -11
View File
@@ -1446,8 +1446,8 @@ async def main(argv):
## VERY unlikely you have a DB set up ## VERY unlikely you have a DB set up
DB_SERVER = 'localhost' DB_SERVER = 'localhost'
DB_PORT = 27017 DB_PORT = 27017
DB_SSL = False DB_TLS = False
DB_CERT_REQ = ssl.CERT_NONE DB_CERT_REQ = False
DB_AUTH = 'admin' DB_AUTH = 'admin'
DB_NAME = 'BlitzStats' DB_NAME = 'BlitzStats'
DB_USER = 'mongouser' DB_USER = 'mongouser'
@@ -1499,14 +1499,14 @@ async def main(argv):
OPT_DB = configDB.getboolean('opt_DB', OPT_DB) OPT_DB = configDB.getboolean('opt_DB', OPT_DB)
DB_SERVER = configDB.get('db_server', DB_SERVER) DB_SERVER = configDB.get('db_server', DB_SERVER)
DB_PORT = configDB.getint('db_port', DB_PORT) DB_PORT = configDB.getint('db_port', DB_PORT)
DB_SSL = configDB.getboolean('db_ssl', DB_SSL) DB_TLS = configDB.getboolean('db_tls', DB_TLS)
DB_CERT_REQ = configDB.getint('db_ssl_req', DB_CERT_REQ) DB_CERT_REQ = configDB.getboolean('db_tls_req', DB_CERT_REQ)
DB_AUTH = configDB.get('db_auth', DB_AUTH) DB_AUTH = configDB.get('db_auth', DB_AUTH)
DB_NAME = configDB.get('db_name', DB_NAME) DB_NAME = configDB.get('db_name', DB_NAME)
DB_USER = configDB.get('db_user', DB_USER) DB_USER = configDB.get('db_user', DB_USER)
DB_PASSWD = configDB.get('db_password', DB_PASSWD) DB_PASSWD = configDB.get('db_password', DB_PASSWD)
DB_CERT = configDB.get('db_ssl_cert_file', DB_CERT) DB_CERT = configDB.get('db_tls_cert_file', DB_CERT)
DB_CA = configDB.get('db_ssl_ca_file', DB_CA) DB_CA = configDB.get('db_tls_ca_file', DB_CA)
except (KeyError, configparser.NoSectionError) as err: except (KeyError, configparser.NoSectionError) as err:
bu.error(exception=err) bu.error(exception=err)
@@ -1552,7 +1552,7 @@ async def main(argv):
#### Connect to MongoDB. You are unlikely to have this set up... #### Connect to MongoDB. You are unlikely to have this set up...
bu.debug('DB_SERVER: ' + DB_SERVER) bu.debug('DB_SERVER: ' + DB_SERVER)
bu.debug('DB_PORT: ' + str(DB_PORT)) bu.debug('DB_PORT: ' + str(DB_PORT))
bu.debug('DB_SSL: ' + "True" if DB_SSL else "False") bu.debug('DB_TLS: ' + "True" if DB_TLS else "False")
bu.debug('DB_AUTH: ' + DB_AUTH) bu.debug('DB_AUTH: ' + DB_AUTH)
bu.debug('DB_NAME: ' + DB_NAME) bu.debug('DB_NAME: ' + DB_NAME)
@@ -1560,7 +1560,7 @@ async def main(argv):
db = None db = None
if args.db: if args.db:
try: try:
client = motor.motor_asyncio.AsyncIOMotorClient(DB_SERVER,DB_PORT, authSource=DB_AUTH, username=DB_USER, password=DB_PASSWD, ssl=DB_SSL, ssl_cert_reqs=DB_CERT_REQ, ssl_certfile=DB_CERT, tlsCAFile=DB_CA) client = motor.motor_asyncio.AsyncIOMotorClient(DB_SERVER,DB_PORT, authSource=DB_AUTH, username=DB_USER, password=DB_PASSWD, tls=DB_TLS, tlsAllowInvalidCertificates=DB_CERT_REQ, tlsCertificateKeyFile=DB_CERT, tlsCAFile=DB_CA)
db = client[DB_NAME] db = client[DB_NAME]
bu.debug('Database connection initiated') bu.debug('Database connection initiated')
except Exception as err: except Exception as err:
@@ -2021,9 +2021,9 @@ async def process_player_stats(players, N_workers: int, args : argparse.Namespac
def remap_stat_id(stat_id_map: dict, stat_id_remap: dict): def remap_stat_id(stat_id_map: dict, stat_id_remap: dict):
bu.debug('Remapping stat_ids: stat_id_map: ' + str(len(stat_id_map)) + ' remap needed: ' + str(len(stat_id_remap))) bu.debug('Remapping stat_ids: stat_id_map: ' + str(len(stat_id_map)) + ' remap needed: ' + str(len(stat_id_remap)))
for stat_id in stat_id_map: for stat_id in stat_id_map:
# bu.debug(stat_id) # bu.debug(stat_id)
if stat_id_map[stat_id] in stat_id_remap.keys(): if stat_id_map[stat_id] in stat_id_remap.keys():
stat_id_map[stat_id] = stat_id_remap[stat_id_map[stat_id]] stat_id_map[stat_id] = stat_id_remap[stat_id_map[stat_id]]
return stat_id_map return stat_id_map
+152 -104
View File
@@ -36,132 +36,163 @@ _progress_obj = None
UMASK = os.umask(0) UMASK = os.umask(0)
os.umask(UMASK) os.umask(UMASK)
# ----------------------------------------------------------- ## -----------------------------------------------------------
# Class ThrottledClientSession(aiohttp.ClientSession) #### Class ThrottledClientSession(aiohttp.ClientSession)
# ----------------------------------------------------------- #
# Rate-limited async http client session
#
# Inherits aiohttp.ClientSession
## -----------------------------------------------------------
from typing import Optional, Union
import aiohttp
import asyncio
import time
import logging
import re
class ThrottledClientSession(aiohttp.ClientSession): class ThrottledClientSession(aiohttp.ClientSession):
"""Rate-throttled client session class inherited from aiohttp.ClientSession)""" """Rate-throttled client session class inherited from aiohttp.ClientSession)"""
# MIN_SLEEP = 0.1
def __init__(self, rate_limit: float = None, *args, **kwargs) -> None: def __init__(self, rate_limit: float = 0, filters: list[str] = list() ,
super().__init__(*args, **kwargs) limit_filtered: bool = False, re_filter: bool = False, *args,**kwargs) -> None:
self.rate_limit = None assert isinstance(rate_limit, (int, float)), "rate_limit has to be float"
self._fillerTask = None assert isinstance(filters, list), "filters has to be list"
self._queue = None assert isinstance(limit_filtered, bool),"limit_filtered has to be bool"
self._start_time = None assert isinstance(re_filter, bool), "re_filter has to be bool"
self._count = 0
self._start_time = time.time()
if rate_limit != None: super().__init__(*args,**kwargs)
if rate_limit <= 0:
raise ValueError('rate_limit must be positive') self.rate_limit : float
self.rate_limit = rate_limit self._fillerTask : Optional[asyncio.Task] = None
self._queue = asyncio.Queue(min(2, int(rate_limit)+1)) self._queue : Optional[asyncio.Queue] = None
self._fillerTask = asyncio.create_task(self._filler()) self._start_time : float = time.time()
self._count : int = 0
self._limit_filtered: bool = limit_filtered
self._re_filter : bool = re_filter
self._filters : list[Union[str, re.Pattern]] = list()
def _get_sleep(self) -> list: if re_filter:
if self.rate_limit == None: for filter in filters:
return None self._filters.append(re.compile(filter))
else: else:
for filter in filters:
self._filters.append(filter)
self.set_rate_limit(rate_limit)
def _get_sleep(self) -> float:
if self.rate_limit > 0:
return 1/self.rate_limit return 1/self.rate_limit
return 0
def get_rate(self) -> float: def get_rate(self) -> float:
"""Return rate of requests""" """Return rate of requests"""
return self._count / (time.time() - self._start_time) return self._count / (time.time() - self._start_time)
def get_stats(self):
def get_stats(self) -> dict[str, float]:
"""Get session statistics""" """Get session statistics"""
res = {'rate': self.get_rate(), 'rate_limit': self.rate_limit, res = {'rate' : self.get_rate(), 'rate_limit': self.rate_limit, 'count' : self._count }
'count': self._count}
return res return res
def get_stats_str(self): def get_stats_str(self) -> str:
"""Print session statistics""" """Print session statistics"""
return 'rate limit: ' + str(self.rate_limit if self.rate_limit != None else '-') + \ return f"rate limit: {str(self.rate_limit if self.rate_limit != None else '-')} \
' rate: ' + \ rate: {0:.1f}.format(self.get_rate()) requests: {str(self._count)}"
"{0:.1f}".format(self.get_rate()) + \
' requests: ' + str(self._count)
def reset_counters(self):
def reset_counters(self) -> dict[str, float]:
"""Reset rate counters and return current results""" """Reset rate counters and return current results"""
res = self.get_stats() res = self.get_stats()
self._start_time = time.time() self._start_time = time.time()
self._count = 0 self._count = 0
return res return res
def set_rate_limit(self, rate_limit: float = 1):
if rate_limit >= 0: def set_rate_limit(self, rate_limit: float = 0) -> float:
self.rate_limit = rate_limit assert rate_limit is not None, "rate_limit must not be None"
return self.rate_limit assert isinstance(rate_limit, (int,float)) and rate_limit >= 0, "rate_limit has to be type of 'float' >= 0"
return None
self.rate_limit = rate_limit
if rate_limit > 0:
self._queue = asyncio.Queue(int(rate_limit)+1)
if self._fillerTask is not None:
self._fillerTask.cancel()
self._fillerTask = asyncio.create_task(self._filler())
return self.rate_limit
async def close(self) -> None: async def close(self) -> None:
"""Close rate-limiter's "bucket filler" task""" """Close rate-limiter's "bucket filler" task"""
# DEBUG # DEBUG
debug(self.get_stats_str()) logging.debug(self.get_stats_str())
try: try:
if self._fillerTask != None: if self._fillerTask is not None:
self._fillerTask.cancel() self._fillerTask.cancel()
await asyncio.wait_for(self._fillerTask, timeout=0.5) await asyncio.wait_for(self._fillerTask, timeout=0.5)
except (asyncio.CancelledError, asyncio.TimeoutError): except asyncio.TimeoutError as err:
pass logging.error(str(err))
await super().close() await super().close()
async def _filler_simple(self):
async def _filler(self) -> None:
"""Filler task to fill the leaky bucket algo""" """Filler task to fill the leaky bucket algo"""
try: try:
if self._queue == None: if self._queue is None:
return return None
sleep = self._get_sleep() logging.debug('SLEEP: ' + str(self._get_sleep()))
while True:
self._queue.put(None)
await asyncio.sleep(sleep)
except asyncio.CancelledError:
debug('Cancelled')
except Exception as err:
error(exception=err)
async def _filler(self):
"""Filler task to fill the leaky bucket algo"""
try:
if self._queue == None:
return
sleep = self._get_sleep()
# debug('SLEEP: ' + str(sleep))
updated_at = time.monotonic() updated_at = time.monotonic()
fraction = 0 extra_increment : float = 0
extra_increment = 0
for i in range(0, self._queue.maxsize): for i in range(0, self._queue.maxsize):
self._queue.put_nowait(i) await self._queue.put(i)
while True: while True:
if not self._queue.full(): if not self._queue.full():
now = time.monotonic() now = time.monotonic()
increment = self.rate_limit * (now - updated_at) increment = self.rate_limit * (now - updated_at)
fraction += increment % 1 items_2_add = int(min(self._queue.maxsize - self._queue.qsize(), int(increment + extra_increment)))
extra_increment = fraction // 1 extra_increment = (increment + extra_increment) % 1
items_2_add = int( for i in range(0,items_2_add):
min(self._queue.maxsize - self._queue.qsize(), int(increment) + extra_increment))
fraction = fraction % 1
for i in range(0, items_2_add):
self._queue.put_nowait(i) self._queue.put_nowait(i)
updated_at = now updated_at = now
await asyncio.sleep(sleep) await asyncio.sleep(self._get_sleep())
except asyncio.CancelledError: except asyncio.CancelledError:
debug('Cancelled') logging.debug('Cancelled')
except Exception as err: except Exception as err:
error(exception=err) logging.error(str(err))
return None
async def _request(self, *args, **kwargs):
async def _request(self, *args,**kwargs) -> aiohttp.ClientResponse:
"""Throttled _request()""" """Throttled _request()"""
if self._queue != None: if self._queue is not None and self.is_limited(*args):
await self._queue.get() await self._queue.get()
self._queue.task_done() self._queue.task_done()
self._count += 1 self._count += 1
return await super()._request(*args, **kwargs) return await super()._request(*args,**kwargs)
def is_limited(self, *args: str) -> bool:
"""Check wether the rate limit should be applied"""
try:
url: str = args[1]
for filter in self._filters:
if isinstance(filter, re.Pattern) and filter.match(url) is not None:
return self._limit_filtered
elif isinstance(filter, str) and url.startswith(filter):
return self._limit_filtered
return not self._limit_filtered
except Exception as err:
logging.error(str(err))
return True
# -----------------------------------------------------------
# Class AsyncLogger()
# -----------------------------------------------------------
class AsyncLogger(): class AsyncLogger():
"""Async file logger""" """Async file logger"""
@@ -496,18 +527,17 @@ def get_date_str(timestamp:int = NOW(), date_format = '%Y%m%d_%H%M%S'):
return None return None
def rebase_file_args(current_dir, files): def rebase_file_args(current_dir : str, files: list[str]) -> list[str]:
"""REbase file command line params after moving working dir to the script's dir""" """Rebase file command line params after moving working dir to the script's dir"""
if isinstance(files, list): assert isinstance(files, list), "files has to be a list"
if (files[0] == '-') or (files[0] == 'db:'):
return files if (files[0] == '-') or (files[0] == 'db:'):
else: return [ files[0] ]
return [ os.path.join(current_dir, fn) for fn in files ] else:
elif isinstance(files, str): return [ os.path.join(current_dir, fn) for fn in files ]
return os.path.join(current_dir, files)
async def read_int_list(filename: str) -> list(): async def read_int_list(filename: str) -> list[int]:
"""Read file to a list and return list of integers in the input file""" """Read file to a list and return list of integers in the input file"""
input_list = [] input_list = []
@@ -1596,6 +1626,8 @@ class WG:
async def store_tank_stats(self, key: list , stats_data: list, update_time: int): async def store_tank_stats(self, key: list , stats_data: list, update_time: int):
"""Save tank stats into cache""" """Save tank stats into cache"""
assert self.cache is not None, "cache must be initiated"
try: try:
account_id = key[0] account_id = key[0]
tank_ids = set(key[1]) tank_ids = set(key[1])
@@ -1617,6 +1649,8 @@ class WG:
async def store_player_stats(self, key: list , stats_data: list, update_time: int): async def store_player_stats(self, key: list , stats_data: list, update_time: int):
"""Save player stats into cache""" """Save player stats into cache"""
assert self.cache is not None, "cache must be initiated"
try: try:
account_id = key[0] account_id = key[0]
if stats_data != None: if stats_data != None:
@@ -1633,6 +1667,8 @@ class WG:
async def store_player_achievements(self, key: list , stats_data: list, update_time: int): async def store_player_achievements(self, key: list , stats_data: list, update_time: int):
"""Save player stats into cache""" """Save player stats into cache"""
assert self.cache is not None, "cache must be initiated"
try: try:
account_id = key[0] account_id = key[0]
if stats_data != None: if stats_data != None:
@@ -1648,6 +1684,8 @@ class WG:
async def get_cached_tank_stats(self, account_id: int, tank_ids: list, fields: list ): async def get_cached_tank_stats(self, account_id: int, tank_ids: list, fields: list ):
assert self.cache is not None, "cache must be initiated"
try: try:
# test for cacheDB existence # test for cacheDB existence
debug('Trying cached stats first') debug('Trying cached stats first')
@@ -1761,17 +1799,27 @@ class WoTinspector:
URL_REPLAY_VIEW = URL_WI +'/en/view/' URL_REPLAY_VIEW = URL_WI +'/en/view/'
URL_REPLAY_UL = 'https://api.wotinspector.com/replay/upload?' URL_REPLAY_UL = 'https://api.wotinspector.com/replay/upload?'
URL_REPLAY_INFO = 'https://api.wotinspector.com/replay/upload?details=full&key=' URL_REPLAY_INFO = 'https://api.wotinspector.com/replay/upload?details=full&key='
URL_TANK_DB = "https://wotinspector.com/static/armorinspector/tank_db_blitz.js" URL_TANK_DB = "https://wotinspector.com/static/armorinspector/tank_db_blitz.js"
REPLAY_N = 1 REPLAY_N = 1
DEFAULT_RATE_LIMIT : float = 10/60 # 10 requests / min
DEFAULT_TOKEN : str = 'a5428af49bf7495986577d59b0e5bcff'
def __init__(self, rate_limit: int = 30): def __init__(self, rate_limit: float = DEFAULT_RATE_LIMIT, auth_token: Optional[str] = None):
self.session = ThrottledClientSession(rate_limit=rate_limit)
headers : Optional[dict[str, str]] = None
if auth_token is None:
auth_token = self.DEFAULT_TOKEN
headers = dict()
headers['Authorization'] = 'Token ' + auth_token
self.session = ThrottledClientSession(rate_limit=rate_limit,
filters=[self.URL_REPLAY_LIST, self.URL_REPLAY_INFO],
re_filter=False, limit_filtered=True, headers = headers)
async def close(self): async def close(self) -> None:
if self.session != None: if self.session is not None:
debug('Closing aiohttp session') debug('Closing aiohttp session')
await self.session.close() await self.session.close()
@@ -1835,7 +1883,7 @@ class WoTinspector:
hash.update(data) hash.update(data)
replay_id = hash.hexdigest() replay_id = hash.hexdigest()
# Testing if the replay has already been posted ## Testing if the replay has already been posted
json_resp = await self.get_replay_JSON(replay_id) json_resp = await self.get_replay_JSON(replay_id)
if json_resp != None: if json_resp != None:
debug('Already uploaded: ' + title, id=N) debug('Already uploaded: ' + title, id=N)
@@ -1850,7 +1898,7 @@ class WoTinspector:
} }
url = self.URL_REPLAY_UL + urllib.parse.urlencode(params, quote_via=urllib.parse.quote) url = self.URL_REPLAY_UL + urllib.parse.urlencode(params, quote_via=urllib.parse.quote)
# debug('URL: ' + url) #debug('URL: ' + url)
headers ={'Content-type': 'application/x-www-form-urlencoded'} headers ={'Content-type': 'application/x-www-form-urlencoded'}
payload = { 'file' : (filename, base64.b64encode(data)) } payload = { 'file' : (filename, base64.b64encode(data)) }
except Exception as err: except Exception as err:
@@ -1875,8 +1923,6 @@ class WoTinspector:
except Exception as err: except Exception as err:
debug(exception=err, id=N) debug(exception=err, id=N)
await asyncio.sleep(SLEEP) await asyncio.sleep(SLEEP)
_id = self.read_replay_id(json_resp)
json_resp['_id'] = _id
debug(' Could not post replay: ' + title, id=N) debug(' Could not post replay: ' + title, id=N)
return json_resp return json_resp
@@ -1914,11 +1960,6 @@ class WoTinspector:
return replay_links return replay_links
@classmethod
def get_replay_id(cls, url):
return url.rsplit('/', 1)[-1]
@classmethod @classmethod
def read_replay_id(cls, json_replay): def read_replay_id(cls, json_replay):
"""Read replay_id from replay JSON file""" """Read replay_id from replay JSON file"""
@@ -1929,14 +1970,18 @@ class WoTinspector:
error(exception=err) error(exception=err)
return None return None
@classmethod @classmethod
def chk_JSON_replay(cls, json_resp): def get_replay_id(cls, url):
return url.rsplit('/', 1)[-1]
@classmethod
def chk_JSON_replay(cls, json_resp) -> bool:
""""Check String for being a valid JSON file""" """"Check String for being a valid JSON file"""
try: try:
# FIX The replay can have allies/enemies i.e. be useful for
# team composition analysis, but does not have player contribution
if ('status' in json_resp) and json_resp['status'] == 'ok' and \ if ('status' in json_resp) and json_resp['status'] == 'ok' and \
(get_JSON_value(json_resp, key='data.summary.winner_team') != None) : (get_JSON_value(json_resp, key='data.summary.exp_base') != None) :
debug("JSON check OK") debug("JSON check OK")
return True return True
except KeyError as err: except KeyError as err:
@@ -1945,6 +1990,9 @@ class WoTinspector:
debug("Replay JSON check failed: " + str(json_resp)) debug("Replay JSON check failed: " + str(json_resp))
return False return False
# -----------------------------------------------------------
# Class BlitzStars
# -----------------------------------------------------------
class BlitzStars: class BlitzStars:
+12 -6
View File
@@ -3,6 +3,7 @@
# Extract tankopedia data in WG API JSON format from Blitz app files (Android APK unzipped) # Extract tankopedia data in WG API JSON format from Blitz app files (Android APK unzipped)
from os import path
import sys, argparse, json, os, inspect, aiohttp, asyncio, aiofiles, re, logging, time, xmltodict, collections, configparser import sys, argparse, json, os, inspect, aiohttp, asyncio, aiofiles, re, logging, time, xmltodict, collections, configparser
import blitzutils as bu import blitzutils as bu
from blitzutils import WG from blitzutils import WG
@@ -11,9 +12,11 @@ logging.getLogger("asyncio").setLevel(logging.DEBUG)
FILE_CONFIG = 'blitzstats.ini' FILE_CONFIG = 'blitzstats.ini'
BLITZAPP_STRINGS='/assets/Data/Strings/en.yaml' # BLITZAPP_STRINGS='/assets/Data/Strings/en.yaml'
BLITZAPP_VEHICLES_DIR='/assets/Data/XML/item_defs/vehicles/' # BLITZAPP_VEHICLES_DIR='/assets/Data/XML/item_defs/vehicles/'
BLITZAPP_VEHICLE_FILE='/list.xml' BLITZAPP_STRINGS='Data/Strings/en.yaml'
BLITZAPP_VEHICLES_DIR='Data/XML/item_defs/vehicles/'
BLITZAPP_VEHICLE_FILE='list.xml'
wg = None wg = None
@@ -104,8 +107,11 @@ async def main(argv):
async def extract_tanks(blitz_app_base : str, nation: str): async def extract_tanks(blitz_app_base : str, nation: str):
tanks = [] tanks = []
list_xml_file = blitz_app_base + BLITZAPP_VEHICLES_DIR + nation + BLITZAPP_VEHICLE_FILE # WG has changed the location of Data directory - at least in steam client
if not os.path.isfile(list_xml_file): if path.isdir(path.join(blitz_app_base, 'assets')):
blitz_app_base = path.join(blitz_app_base, 'assets')
list_xml_file = path.join(blitz_app_base, BLITZAPP_VEHICLES_DIR ,nation ,BLITZAPP_VEHICLE_FILE)
if not path.isfile(list_xml_file):
print('ERROR: cannot open ' + list_xml_file) print('ERROR: cannot open ' + list_xml_file)
return None return None
bu.debug('Opening file: ' + list_xml_file + ' (Nation: ' + nation + ')') bu.debug('Opening file: ' + list_xml_file + ' (Nation: ' + nation + ')')
@@ -132,7 +138,7 @@ async def read_user_strs(blitz_app_base : str) -> dict:
"""Read user strings to convert map and tank names""" """Read user strings to convert map and tank names"""
tank_strs = {} tank_strs = {}
map_strs = {} map_strs = {}
filename = blitz_app_base + BLITZAPP_STRINGS filename = path.join(blitz_app_base, BLITZAPP_STRINGS)
bu.debug('Opening file: ' + filename + ' for reading UserStrings') bu.debug('Opening file: ' + filename + ' for reading UserStrings')
try: try:
async with aiofiles.open(filename, 'r', encoding="utf8") as f: async with aiofiles.open(filename, 'r', encoding="utf8") as f:
+94 -91
View File
@@ -1,93 +1,96 @@
# WoT Blitz releases # WoT Blitz releases
| Release | Date | Notes | Inaccurate? | | Release | Date | Notes |
|:-------:|:--------:|-------------|-----| |:-------:|:--------:|-------------|
| 8.9 | 2022-03-23 | "Free" camo resupply, VK 72.01 supplies | | | 9.1 | 2022-07-14 | Releases updated via DLC, graphics updates for Object 140, Patton 46 |
| 8.8 | 2022-02-16 | Molendijk map | | | 9.0 | 2022-06-08 | Japanies heavies, Castilla graphics update, new garage |
| 8.7 | 2022-01-19 | Soviet Light Tanks | | | 8.10 | 2022-04-28 | New battle results screen, new "Operation" mechanics |
| 8.6 | 2021-12-15 | Joining Light and Medium Tank classes in MM | | | 8.9 | 2022-03-23 | "Free" camo resupply, VK 72.01 supplies |
| 8.5 | 2021-11-17 | New national crew voices, economy update | | | 8.8 | 2022-02-16 | Molendijk map |
| 8.4 | 2021-10-20 | Polish heavy tanks | | | 8.7 | 2022-01-19 | Soviet Light Tanks |
| 8.3 | 2021-09-16 | FV4005, FV4022, VK 91.01 (P) and WZ-121 legendary camos, -5 deg gun depression to WZ-120 | | | 8.6 | 2021-12-15 | Joining Light and Medium Tank classes in MM |
| 8.2 | 2021-08-18 | Ghost Factory map, XP/credits nerf for inactive players, new store UI | | | 8.5 | 2021-11-17 | New national crew voices, economy update |
| 8.1 | 2021-07-15 | Armor highlighting updated | | | 8.4 | 2021-10-20 | Polish heavy tanks |
| 8.0 | 2021-06-08 | American experimental heavies (Yoh-line), PBR camos | | | 8.3 | 2021-09-16 | FV4005, FV4022, VK 91.01 (P) and WZ-121 legendary camos, -5 deg gun depression to WZ-120 |
| 7.9 | 2021-04-27 | Mines rework, new tutorial, colorblind mode, JgPz E100 legendary camo | | | 8.2 | 2021-08-18 | Ghost Factory map, XP/credits nerf for inactive players, new store UI |
| 7.8 | 2021-03-24 | Mid-tier balance changes, Grille line new consumables | | | 8.1 | 2021-07-15 | Armor highlighting updated |
| 7.7 | 2021-02-24 | Czechoslovakian medium tanks | | | 8.0 | 2021-06-08 | American experimental heavies (Yoh-line), PBR camos |
| 7.6 | 2021-01-27 | AMC 13 90, TOG II buffs, free replay camera | | | 7.9 | 2021-04-27 | Mines rework, new tutorial, colorblind mode, JgPz E100 legendary camo |
| 7.5 | 2020-12-09 | ATGM removal, Batchat, Kpz70, T-2020 buff | | | 7.8 | 2021-03-24 | Mid-tier balance changes, Grille line new consumables |
| 7.4 | 2020-11-12 | TD buffs, Clan missions | | | 7.7 | 2021-02-24 | Czechoslovakian medium tanks |
| 7.3 | 2020-10-08 | Swedish HT line | | | 7.6 | 2021-01-27 | AMC 13 90, TOG II buffs, free replay camera |
| 7.2 | 2020-08-19 | Balance changes, added tier X ammo capa, new aiming reticles | | | 7.5 | 2020-12-09 | ATGM removal, Batchat, Kpz70, T-2020 buff |
| 7.1 | 2020-07-15 | New Yukon map, IS-6, KV-4, Patton 48 armor buff, Grille DPM buff, SP IC, AMZ 13 75, M41 Bulldog mobility buff | | | 7.4 | 2020-11-12 | TD buffs, Clan missions |
| 7.0 | 2020-06-17 | British LTs, graphics improvements, ATGM nerf, Sheridan nerf, map fixes | | | 7.3 | 2020-10-08 | Swedish HT line |
| 6.10 | 2020-04-29 | T92E1 nerf, Tier V-IX heavy HP buff, Battle Pass, map fixes | | | 7.2 | 2020-08-19 | Balance changes, added tier X ammo capa, new aiming reticles |
| 6.9 | 2020-03-25 | Japanese TDs, Tier X heavy HP buff | | | 7.1 | 2020-07-15 | New Yukon map, IS-6, KV-4, Patton 48 armor buff, Grille DPM buff, SP IC, AMZ 13 75, M41 Bulldog mobility buff |
| 6.8 | 2020-02-19 | ATGM nerf, Hellas map | | | 7.0 | 2020-06-17 | British LTs, graphics improvements, ATGM nerf, Sheridan nerf, map fixes |
| 6.7 | 2020-01-15 | ATGM US Lights | | | 6.10 | 2020-04-29 | T92E1 nerf, Tier V-IX heavy HP buff, Battle Pass, map fixes |
| 6.6 | 2019-12-18 | T-62A + Leo buffs | | | 6.9 | 2020-03-25 | Japanese TDs, Tier X heavy HP buff |
| 6.5 | 2019-11-21 | New UI | | | 6.8 | 2020-02-19 | ATGM nerf, Hellas map |
| 6.4 | 2019-10-16 | Halloween map, mid-tier HT buff... | | | 6.7 | 2020-01-15 | ATGM US Lights |
| 6.3 | 2019-09-18 | European line | | | 6.6 | 2019-12-18 | T-62A + Leo buffs |
| 6.2 | 2019-08-14 | New MM | | | 6.5 | 2019-11-21 | New UI |
| 6.1 | 2019-07-10 | Autoloader buff | | | 6.4 | 2019-10-16 | Halloween map, mid-tier HT buff... |
| 6.0 | 2019-06-05 | UK TD line #2 | | | 6.3 | 2019-09-18 | European line |
| 5.10 | 2019-04-24 | new missions | | | 6.2 | 2019-08-14 | New MM |
| 5.9 | 2019-03-27 | New shell tracers, minor fixes | | | 6.1 | 2019-07-10 | Autoloader buff |
| 5.8 | 2019-02-21 | Chinese TD line | | | 6.0 | 2019-06-05 | UK TD line #2 |
| 5.7 | 2019-01-16 | Avatars | | | 5.10 | 2019-04-24 | new missions |
| 5.6 | 2018-12-05 | Normandy map | | | 5.9 | 2019-03-27 | New shell tracers, minor fixes |
| 5.5 | 2018-11-13 | Low-tier clean-up | | | 5.8 | 2019-02-21 | Chinese TD line |
| 5.4 | 2018-10-10 | MM "rigging" adjusted | | | 5.7 | 2019-01-16 | Avatars |
| 5.3 | 2018-09-12 | MM "rigging" started, Newbie queue added | | | 5.6 | 2018-12-05 | Normandy map |
| 5.2 | 2018-08-22 | Faust map | | | 5.5 | 2018-11-13 | Low-tier clean-up :-( |
| 5.1 | 2018-07-31 | New MM: vehicle class matching | | | 5.4 | 2018-10-10 | MM "rigging" adjusted |
| 5.0 | 2018-06-30 | Vineyeards map update, Spare Parts killed | | | 5.3 | 2018-09-12 | MM "rigging" started, Newbie queue added |
| 4.10 | 2018-05-24 | Alpenstadt map | | | 5.2 | 2018-08-22 | Faust map |
| 4.9 | 2018-04-25 | Chinese heavy tanks | | | 5.1 | 2018-07-31 | New MM: vehicle class matching |
| 4.8 | 2018-03-28 | Choice of battle modes, crew skills accelerator | | | 5.0 | 2018-06-30 | Vineyeards map update, Spare Parts removed ! |
| 4.7 | 2018-02-28 | New minimap | | | 4.10 | 2018-05-24 | Alpenstadt map |
| 4.6 | 2018-01-31 | Chinese nation | | | 4.9 | 2018-04-25 | Chinese heavy tanks |
| 4.5 | 2017-12-19 | Dynasty's Pearl map | | | 4.8 | 2018-03-28 | Choice of battle modes, crew skills accelerator |
| 4.4 | 2017-11-09 | New British tanks | | | 4.7 | 2018-02-28 | New minimap |
| 4.3 | 2017-10-10 | Level 7 screw skills | | | 4.6 | 2018-01-31 | Chinese nation |
| 4.2 | 2017-09-12 | French LTs, Naval Frontier map | | | 4.5 | 2017-12-19 | Dynasty's Pearl map |
| 4.1 | 2017-08-16 | New markers | | | 4.4 | 2017-11-09 | New British tanks |
| 4.0 | 2017-07-13 | Rating battles | Y | | 4.3 | 2017-10-10 | Level 7 screw skills |
| 3.10 | 2017-06-15 | Fixes | Y | | 4.2 | 2017-09-12 | French LTs, Naval Frontier map |
| 3.9 | 2017-05-19 | Military Honor, Mayan Ruins | Y | | 4.1 | 2017-08-16 | New markers |
| 3.8 | 2017-04-24 | Spare Parts, New 9-slot Equipment system | Y | | 4.0 | 2017-07-13 | Rating battles (date uncertain) |
| 3.7 | 2017-03-22 | Balance changes, Tournaments | Y | | 3.10 | 2017-06-15 | Fixes (date uncertain) |
| 3.6 | 2017-02-16 | Vehicle Enrichment | Y | | 3.9 | 2017-05-19 | Military Honor, Mayan Ruins (date uncertain) |
| 3.5 | 2017-01-13 | Boosters, Dead rail update | Y | | 3.8 | 2017-04-24 | Spare Parts, New 9-slot Equipment system (date uncertain) |
| 3.4 | 2016-11-28 | Canyon map, USA T57 line | | | 3.7 | 2017-03-22 | Balance changes, Tournaments (date uncertain) |
| 3.3 | 2016-11-01 | Updated Mines map | | | 3.6 | 2017-02-16 | Vehicle Enrichment (date uncertain) |
| 3.2 | 2016-10-04 | Copperfield update | | | 3.5 | 2017-01-13 | Boosters, Dead rail update (date uncertain) |
| 3.1 | 2016-09-03 | Clan tournaments, Yamato Harbor map | Y | | 3.4 | 2016-11-28 | Canyon map, USA T57 line |
| 3.0 | 2016-07-20 | Supremacy mode, Falls Creek update | Y | | 3.3 | 2016-11-01 | Updated Mines map |
| 2.11 | 2016-06-28 | Vineyeards map, Oasis Palms update, German paper TDs | | | 3.2 | 2016-10-04 | Copperfield update |
| 2.10 | 2016-06-07 | Rockfield map update, Tank birthdays, high-pen gun for IS-6 | | | 3.1 | 2016-09-03 | Clan tournaments, Yamato Harbor map (date uncertain) |
| 2.9 | 2016-05-03 | Training rooms, +/-1 Matchmaker | Y | | 3.0 | 2016-07-20 | Supremacy mode, Falls Creek update (date uncertain) |
| 2.8 | 2016-04-04 | New Russian TD line, new consumables system | Y | | 2.11 | 2016-06-28 | Vineyeards map, Oasis Palms update, German paper TDs |
| 2.7 | 2016-03-07 | Mirage map | Y | | 2.10 | 2016-06-07 | Rockfield map update, Tank birthdays, high-pen gun for IS-6 |
| 2.6 | 2016-02-10 | German LTs, shooting through obstacles | Y | | 2.9 | 2016-05-03 | Training rooms, +/-1 Matchmaker (date uncertain) |
| 2.5 | 2016-01-11 | Camouflages, Clan supply | Y | | 2.8 | 2016-04-04 | New Russian TD line, new consumables system (date uncertain) |
| 2.4 | 2015-12-09 | Additional Japanese tanks, windows version | Y | | 2.7 | 2016-03-07 | Mirage map (date uncertain) |
| 2.3 | 2015-11-12 | Canal map | Y | | 2.6 | 2016-02-10 | German LTs, shooting through obstacles (date uncertain) |
| 2.2 | 2015-10-05 | Desert Sands map update | Y | | 2.5 | 2016-01-11 | Camouflages, Clan supply (date uncertain) |
| 2.1 | 2015-09-07 | UK TDs, Clan Chat | Y | | 2.4 | 2015-12-09 | Additional Japanese tanks, windows version (date uncertain) |
| 2.0 | 2015-08-05 | Clans, Oasis Palms updated | | | 2.3 | 2015-11-12 | Canal map (date uncertain) |
| 1.11 | 2015-07-13 | KV-2, KV-13, Sherman Jumbo | Y | | 2.2 | 2015-10-05 | Desert Sands map update (date uncertain) |
| 1.10 | 2015-06-11 | Port Bay map | Y | | 2.1 | 2015-09-07 | UK TDs, Clan Chat (date uncertain) |
| 1.9 | 2015-05-13 | New German medium tanks (Leo) | Y | | 2.0 | 2015-08-05 | Clans, Oasis Palms updated |
| 1.8 | 2015-04-14 | Game tutorial, Bot map for new players | Y | | 1.11 | 2015-07-13 | KV-2, KV-13, Sherman Jumbo (date uncertain) |
| 1.7 | 2015-02-16 | US Turreted TDs | Y | | 1.10 | 2015-06-11 | Port Bay map (date uncertain) |
| 1.6 | 2015-01-19 | Castilla map, UK HT line | Y | | 1.9 | 2015-05-13 | New German medium tanks (Leo) (date uncertain) |
| 1.5 | 2014-12-01 | UK Nation | Y | | 1.8 | 2015-04-14 | Game tutorial, Bot map for new players (date uncertain) |
| 1.4 | 2014-10-29 | German HT line (Maus) | Y | | 1.7 | 2015-02-16 | US Turreted TDs (date uncertain) |
| 1.3 | 2014-10-02 | USSR IS-4 line, Lost Temple, Black Goldville maps | Y | | 1.6 | 2015-01-19 | Castilla map, UK HT line (date uncertain) |
| 1.2 | 2014-08-20 | Oasis Palms map | Y | | 1.5 | 2014-12-01 | UK Nation (date uncertain) |
| 1.1.1 | 2014-07-23 | Fixes :-) | | | 1.4 | 2014-10-29 | German HT line (Maus) (date uncertain) |
| 1.0 | 2014-06-26 | Worldwide launch | | | 1.3 | 2014-10-02 | USSR IS-4 line, Lost Temple, Black Goldville maps (date uncertain) |
| 0.9 | 2014-05-05 | Nordic soft launch | | | 1.2 | 2014-08-20 | Oasis Palms map (date uncertain) |
| 1.1.1 | 2014-07-23 | Fixes :-) |
| 1.0 | 2014-06-26 | Worldwide launch |
| 0.9 | 2014-05-05 | Nordic soft launch |
+349 -22
View File
@@ -1,7 +1,7 @@
{ {
"status": "ok", "status": "ok",
"meta": { "meta": {
"count": 540 "count": 564
}, },
"data": { "data": {
"1": { "1": {
@@ -1252,6 +1252,14 @@
"tier": 7, "tier": 7,
"type": "mediumTank" "type": "mediumTank"
}, },
"5473": {
"is_premium": false,
"name": "Mitsu 108",
"nation": "japan",
"tank_id": 5473,
"tier": 5,
"type": "heavyTank"
},
"5489": { "5489": {
"is_premium": true, "is_premium": true,
"name": "Y5 T-34", "name": "Y5 T-34",
@@ -1292,6 +1300,14 @@
"tier": 9, "tier": 9,
"type": "mediumTank" "type": "mediumTank"
}, },
"5729": {
"is_premium": false,
"name": "Ju-Nu",
"nation": "japan",
"tank_id": 5729,
"tier": 6,
"type": "heavyTank"
},
"5745": { "5745": {
"is_premium": true, "is_premium": true,
"name": "Y5 Firefly", "name": "Y5 Firefly",
@@ -1348,6 +1364,14 @@
"tier": 8, "tier": 8,
"type": "mediumTank" "type": "mediumTank"
}, },
"5985": {
"is_premium": false,
"name": "Ju-To",
"nation": "japan",
"tank_id": 5985,
"tier": 7,
"type": "heavyTank"
},
"6001": { "6001": {
"is_premium": true, "is_premium": true,
"name": "Y5 ELC bis", "name": "Y5 ELC bis",
@@ -1412,6 +1436,14 @@
"tier": 10, "tier": 10,
"type": "heavyTank" "type": "heavyTank"
}, },
"6241": {
"is_premium": false,
"name": "Chi-Se",
"nation": "japan",
"tank_id": 6241,
"tier": 8,
"type": "heavyTank"
},
"6257": { "6257": {
"is_premium": true, "is_premium": true,
"name": "M4/FL10", "name": "M4/FL10",
@@ -1476,6 +1508,14 @@
"tier": 4, "tier": 4,
"type": "lightTank" "type": "lightTank"
}, },
"6497": {
"is_premium": false,
"name": "Type 68",
"nation": "japan",
"tank_id": 6497,
"tier": 9,
"type": "heavyTank"
},
"6529": { "6529": {
"is_premium": false, "is_premium": false,
"name": "Škoda T 25", "name": "Škoda T 25",
@@ -1525,7 +1565,7 @@
"type": "heavyTank" "type": "heavyTank"
}, },
"6753": { "6753": {
"is_premium": true, "is_premium": false,
"name": "Type 71", "name": "Type 71",
"nation": "japan", "nation": "japan",
"tank_id": 6753, "tank_id": 6753,
@@ -2148,6 +2188,14 @@
"tier": 6, "tier": 6,
"type": "AT-SPG" "type": "AT-SPG"
}, },
"9841": {
"is_premium": true,
"name": "Rover",
"nation": "other",
"tank_id": 9841,
"tier": 6,
"type": "lightTank"
},
"9857": { "9857": {
"is_premium": true, "is_premium": true,
"name": "Škoda T 45", "name": "Škoda T 45",
@@ -2196,6 +2244,22 @@
"tier": 7, "tier": 7,
"type": "AT-SPG" "type": "AT-SPG"
}, },
"10097": {
"is_premium": true,
"name": "Medjay",
"nation": "other",
"tank_id": 10097,
"tier": 5,
"type": "heavyTank"
},
"10113": {
"is_premium": true,
"name": "Carro da Combattimento 45t",
"nation": "european",
"tank_id": 10113,
"tier": 10,
"type": "mediumTank"
},
"10241": { "10241": {
"is_premium": false, "is_premium": false,
"name": "SU-100M1", "name": "SU-100M1",
@@ -2220,6 +2284,22 @@
"tier": 4, "tier": 4,
"type": "AT-SPG" "type": "AT-SPG"
}, },
"10353": {
"is_premium": true,
"name": "Pharaoh",
"nation": "other",
"tank_id": 10353,
"tier": 7,
"type": "heavyTank"
},
"10369": {
"is_premium": true,
"name": "Controcarro 3 Minotauro",
"nation": "european",
"tank_id": 10369,
"tier": 10,
"type": "AT-SPG"
},
"10497": { "10497": {
"is_premium": false, "is_premium": false,
"name": "KV-2", "name": "KV-2",
@@ -2388,6 +2468,14 @@
"tier": 7, "tier": 7,
"type": "mediumTank" "type": "mediumTank"
}, },
"11905": {
"is_premium": true,
"name": "Shadowhunter",
"nation": "european",
"tank_id": 11905,
"tier": 6,
"type": "mediumTank"
},
"12033": { "12033": {
"is_premium": false, "is_premium": false,
"name": "SU-122-54", "name": "SU-122-54",
@@ -2420,6 +2508,14 @@
"tier": 8, "tier": 8,
"type": "AT-SPG" "type": "AT-SPG"
}, },
"12161": {
"is_premium": true,
"name": "Strv K",
"nation": "european",
"tank_id": 12161,
"tier": 10,
"type": "heavyTank"
},
"12305": { "12305": {
"is_premium": false, "is_premium": false,
"name": "E 50 Ausf. M", "name": "E 50 Ausf. M",
@@ -2622,7 +2718,7 @@
}, },
"15697": { "15697": {
"is_premium": true, "is_premium": true,
"name": "Chieftain Mk.6", "name": "Chieftain Mk. 6",
"nation": "uk", "nation": "uk",
"tank_id": 15697, "tank_id": 15697,
"tier": 10, "tier": 10,
@@ -3076,6 +3172,14 @@
"tier": 8, "tier": 8,
"type": "heavyTank" "type": "heavyTank"
}, },
"19777": {
"is_premium": true,
"name": "AltProto AMX 30",
"nation": "france",
"tank_id": 19777,
"tier": 8,
"type": "mediumTank"
},
"19793": { "19793": {
"is_premium": false, "is_premium": false,
"name": "Vickers Cruiser", "name": "Vickers Cruiser",
@@ -3292,6 +3396,14 @@
"tier": 6, "tier": 6,
"type": "mediumTank" "type": "mediumTank"
}, },
"21585": {
"is_premium": true,
"name": "Blasteroid",
"nation": "uk",
"tank_id": 21585,
"tier": 6,
"type": "heavyTank"
},
"21761": { "21761": {
"is_premium": true, "is_premium": true,
"name": "STG", "name": "STG",
@@ -3316,6 +3428,14 @@
"tier": 10, "tier": 10,
"type": "lightTank" "type": "lightTank"
}, },
"21841": {
"is_premium": true,
"name": "Caliban",
"nation": "uk",
"tank_id": 21841,
"tier": 8,
"type": "heavyTank"
},
"22017": { "22017": {
"is_premium": true, "is_premium": true,
"name": "T-34-85", "name": "T-34-85",
@@ -3340,6 +3460,14 @@
"tier": 6, "tier": 6,
"type": "heavyTank" "type": "heavyTank"
}, },
"22097": {
"is_premium": true,
"name": "Churchill VIII",
"nation": "uk",
"tank_id": 22097,
"tier": 6,
"type": "heavyTank"
},
"22273": { "22273": {
"is_premium": true, "is_premium": true,
"name": "Object 260", "name": "Object 260",
@@ -3364,6 +3492,14 @@
"tier": 9, "tier": 9,
"type": "heavyTank" "type": "heavyTank"
}, },
"22353": {
"is_premium": true,
"name": "Churchill W",
"nation": "uk",
"tank_id": 22353,
"tier": 6,
"type": "heavyTank"
},
"22529": { "22529": {
"is_premium": true, "is_premium": true,
"name": "LT-432", "name": "LT-432",
@@ -3612,6 +3748,14 @@
"tier": 8, "tier": 8,
"type": "mediumTank" "type": "mediumTank"
}, },
"25361": {
"is_premium": true,
"name": "Waffenträger Camelot",
"nation": "germany",
"tank_id": 25361,
"tier": 9,
"type": "AT-SPG"
},
"25377": { "25377": {
"is_premium": true, "is_premium": true,
"name": "T77", "name": "T77",
@@ -3620,6 +3764,54 @@
"tier": 8, "tier": 8,
"type": "heavyTank" "type": "heavyTank"
}, },
"25601": {
"is_premium": true,
"name": "IS-2 shielded",
"nation": "ussr",
"tank_id": 25601,
"tier": 7,
"type": "heavyTank"
},
"25633": {
"is_premium": true,
"name": "M-IV-Y",
"nation": "usa",
"tank_id": 25633,
"tier": 8,
"type": "heavyTank"
},
"25889": {
"is_premium": true,
"name": "Ranger",
"nation": "usa",
"tank_id": 25889,
"tier": 6,
"type": "lightTank"
},
"26145": {
"is_premium": true,
"name": "High Score",
"nation": "usa",
"tank_id": 26145,
"tier": 5,
"type": "mediumTank"
},
"26401": {
"is_premium": true,
"name": "Enforcer",
"nation": "usa",
"tank_id": 26401,
"tier": 6,
"type": "AT-SPG"
},
"26657": {
"is_premium": true,
"name": "ASTRON Rex 105 mm",
"nation": "usa",
"tank_id": 26657,
"tier": 8,
"type": "mediumTank"
},
"51201": { "51201": {
"is_premium": true, "is_premium": true,
"name": "KV-220 Beta-Test", "name": "KV-220 Beta-Test",
@@ -4335,6 +4527,8 @@
"A-20bis": "SP-3", "A-20bis": "SP-3",
"A-32": "A-32", "A-32": "A-32",
"A-32_mod_1939": "A-32", "A-32_mod_1939": "A-32",
"A05_M4_105BD": "High Score",
"A05_M4_105BD_short": "High Score",
"A100_T49": "T49", "A100_T49": "T49",
"A100_T49_Custom": "T49 Hornet", "A100_T49_Custom": "T49 Hornet",
"A100_T49_Custom_short": "T49 Hornet", "A100_T49_Custom_short": "T49 Hornet",
@@ -4385,7 +4579,15 @@
"A130_Super_Hellcat_Custom_short": "Super Hellcat Delta", "A130_Super_Hellcat_Custom_short": "Super Hellcat Delta",
"A130_Super_Hellcat_short": "Super Hellcat", "A130_Super_Hellcat_short": "Super Hellcat",
"A132_T77": "T77", "A132_T77": "T77",
"A132_T77_Custom": "T77 Cannonball",
"A132_T77_Custom_short": "T77 Cannonball",
"A132_T77_short": "T77", "A132_T77_short": "T77",
"A140_ASTRON_REX_105": "ASTRON Rex 105 mm",
"A140_ASTRON_REX_105_short": "ASTRON Rex",
"A141_M-4-Y": "M-IV-Y",
"A141_M-4-Y_Custom": "M-IV-Y Stalker",
"A141_M-4-Y_Custom_short": "M-IV-Y Stalker",
"A141_M-4-Y_short": "M-IV-Y",
"A68_T28D": "T28 Defender", "A68_T28D": "T28 Defender",
"A68_T28D_short": "T28 Defender", "A68_T28D_short": "T28 Defender",
"A77_T6M": "T6E1 Grizzly", "A77_T6M": "T6E1 Grizzly",
@@ -4395,6 +4597,8 @@
"A95_T95E6_Custom_short": "T95E6 Piranha", "A95_T95E6_Custom_short": "T95E6 Piranha",
"A95_T95E6_short": "T95E6", "A95_T95E6_short": "T95E6",
"A99_T92E1": "T92E1", "A99_T92E1": "T92E1",
"A99_T92E1_Custom": "T92E1 Poison",
"A99_T92E1_Custom_short": "T92E1 Poison",
"A99_T92E1_short": "T92E1", "A99_T92E1_short": "T92E1",
"AEC": "2x AEC", "AEC": "2x AEC",
"AEC3": "AEC3", "AEC3": "AEC3",
@@ -4420,6 +4624,8 @@
"AMX_50Fosh_155": "AMX 50 Foch (155)", "AMX_50Fosh_155": "AMX 50 Foch (155)",
"AMX_50Fosh_155_short": "Foch 155", "AMX_50Fosh_155_short": "Foch 155",
"AMX_50_100": "AMX 50 100", "AMX_50_100": "AMX 50 100",
"AMX_50_100_Custom": "AMX 50 100 Basher",
"AMX_50_100_Custom_short": "AMX 50 100 Basher",
"AMX_50_100_short": "AMX 50 100", "AMX_50_100_short": "AMX 50 100",
"AMX_50_120": "AMX 50 120", "AMX_50_120": "AMX 50 120",
"AMX_50_120_short": "AMX 50 120", "AMX_50_120_short": "AMX 50 120",
@@ -4464,6 +4670,7 @@
"AT-1": "AT-1", "AT-1": "AT-1",
"AT-1bis": "AT-1 Bis", "AT-1bis": "AT-1 Bis",
"AVCR_1100_1": "AVCR 1100 1", "AVCR_1100_1": "AVCR 1100 1",
"AVS-1195A": "AVS-1195A",
"AVS_1195": "AVS-1195", "AVS_1195": "AVS-1195",
"AV_1195": "AV-1195", "AV_1195": "AV-1195",
"AV_1790": "AV 1790", "AV_1790": "AV 1790",
@@ -4518,6 +4725,8 @@
"Bison_I_short": "Bison", "Bison_I_short": "Bison",
"Buick_series_60": "Buick Series 60", "Buick_series_60": "Buick Series 60",
"CH38_WZ113G-FT": "WZ-113G FT", "CH38_WZ113G-FT": "WZ-113G FT",
"CH38_WZ113G-FT_Custom_short_v2": "WZ-113G FT Cute Baby",
"CH38_WZ113G-FT_Custom_v2": "WZ-113G FT Cute Baby",
"CH38_WZ113G-FT_short": "WZ-113G FT", "CH38_WZ113G-FT_short": "WZ-113G FT",
"CH38_WZ113G_FT_Custom": "WZ-113G FT Lucky", "CH38_WZ113G_FT_Custom": "WZ-113G FT Lucky",
"CH38_WZ113G_FT_Custom_short": "WZ-113G FT Lucky", "CH38_WZ113G_FT_Custom_short": "WZ-113G FT Lucky",
@@ -4612,6 +4821,20 @@
"Ch_WZ-112v2_short": "WZ-112-2", "Ch_WZ-112v2_short": "WZ-112-2",
"Ch_WZ_112v2_Custom": "WZ-112-2 Celestial", "Ch_WZ_112v2_Custom": "WZ-112-2 Celestial",
"Ch_WZ_112v2_Custom_short": "WZ-112-2 Celestial", "Ch_WZ_112v2_Custom_short": "WZ-112-2 Celestial",
"Chassis1_A140_ASTRON_REX_105": "Rex",
"Chassis1_A140_ASTRON_REX_105_short": "Rex",
"Chassis1_F117_Alt_Proto_AMX_30": "AltProto AMX 30",
"Chassis1_F117_Alt_Proto_AMX_30_short": "A.P. AMX 30",
"Chassis1_GB09_Churchill_VII_BP": "Churchill W",
"Chassis1_GB09_Churchill_VII_BP_short": "Churchill W",
"Chassis1_It10_P43_bis_BP": "Shadowhunter",
"Chassis1_It10_P43_bis_BP_short": "Shadowhunter",
"Chassis1_Oth34_PanzerMummy": "Royal Tread",
"Chassis1_Oth34_PanzerMummy_short": "Royal Tread",
"Chassis1_Oth35_PanzerMejai": "Desert Wanderer",
"Chassis1_Oth35_PanzerMejai_short": "Desert Wanderer",
"Chassis1_S31_Strv_K": "Stridsvagn KRV",
"Chassis1_S31_Strv_K_short": "Stridsvagn KRV",
"Chi_Ha": "Type 97 Chi-Ha", "Chi_Ha": "Type 97 Chi-Ha",
"Chi_Ha_short": "Chi-Ha", "Chi_Ha_short": "Chi-Ha",
"Chi_He": "Type 1 Chi-He", "Chi_He": "Type 1 Chi-He",
@@ -4641,17 +4864,20 @@
"Continental_AOI-628-1": "Continental AOI-628-1", "Continental_AOI-628-1": "Continental AOI-628-1",
"Continental_AOI-628-1_short": "Continental\\nAOI-628-1", "Continental_AOI-628-1_short": "Continental\\nAOI-628-1",
"Continental_AOI-628-2": "Continental AOI-628-2", "Continental_AOI-628-2": "Continental AOI-628-2",
"Continental_AOI-628-2A": "Continental AOI-628-2A",
"Continental_AOI-628-2_short": "Continental\\nAOI-628-2", "Continental_AOI-628-2_short": "Continental\\nAOI-628-2",
"Continental_AOI_1490_1": "Continental AOI-1490-1", "Continental_AOI_1490_1": "Continental AOI-1490-1",
"Continental_AOS-895-1": "Continental AOS-895-1", "Continental_AOS-895-1": "Continental AOS-895-1",
"Continental_AOS-895-1_short": "Continental\\nAOS-895-1", "Continental_AOS-895-1_short": "Continental\\nAOS-895-1",
"Continental_AOS-895-3": "AN/GRC-8", "Continental_AOS-895-3": "AN/GRC-8",
"Continental_AOS-895-3V2": "Continental AOS-895-3-2",
"Continental_AOS-895-3_short": "AN/GRC-8", "Continental_AOS-895-3_short": "AN/GRC-8",
"Continental_AOSI-1195-1": "Continental AOSI 1195-1", "Continental_AOSI-1195-1": "Continental AOSI 1195-1",
"Continental_AOSI-1195-1A": "Continental AOSI-1195-1A", "Continental_AOSI-1195-1A": "Continental AOSI-1195-1A",
"Continental_AOSI-1195-5": "Continental AOSI-1195-5", "Continental_AOSI-1195-5": "Continental AOSI-1195-5",
"Continental_AOSI-895-5": "Continental AOSI-895-5", "Continental_AOSI-895-5": "Continental AOSI-895-5",
"Continental_AOSI-895-5_short": "Continental\\nAOSI-895-5", "Continental_AOSI-895-5_short": "Continental\\nAOSI-895-5",
"Continental_AOSI_1195_5A": "Continental AOSI-1195-5 Y IV",
"Continental_AOSI_895_5_Prem": "Continental AOSI-895-5", "Continental_AOSI_895_5_Prem": "Continental AOSI-895-5",
"Continental_AV-1790-1": "Continental AV-1790-1", "Continental_AV-1790-1": "Continental AV-1790-1",
"Continental_AV-1790-1A": "Continental AV-1790-1", "Continental_AV-1790-1A": "Continental AV-1790-1",
@@ -4727,6 +4953,8 @@
"Cz14_Skoda_T-56_Custom_short": "Škoda T 56 Golem", "Cz14_Skoda_T-56_Custom_short": "Škoda T 56 Golem",
"Cz14_Skoda_T-56_short": "Škoda T 56", "Cz14_Skoda_T-56_short": "Škoda T 56",
"Cz15_Skoda_T-45": "Škoda T 45", "Cz15_Skoda_T-45": "Škoda T 45",
"Cz15_Skoda_T-45_Custom": "Škoda T 45 Skvělý",
"Cz15_Skoda_T-45_Custom_short": "Škoda T 45 Skvělý",
"Cz15_Skoda_T-45_short": "Škoda T 45", "Cz15_Skoda_T-45_short": "Škoda T 45",
"D-744": "D-744", "D-744": "D-744",
"D1": "D1", "D1": "D1",
@@ -4763,12 +4991,16 @@
"E-100": "E 100", "E-100": "E 100",
"E-100_Custom": "E 100 Stalwart", "E-100_Custom": "E 100 Stalwart",
"E-100_Custom_short": "E 100 Stalwart", "E-100_Custom_short": "E 100 Stalwart",
"E-100_Custom_short_v2": "E 100 Jötunn",
"E-100_Custom_v2": "E 100 Jötunn",
"E-100_short": "E 100", "E-100_short": "E 100",
"E-25": "E 25", "E-25": "E 25",
"E-25_Custom": "E 25 \\\"#1\\\"", "E-25_Custom": "E 25 \\\"#1\\\"",
"E-50": "E 50", "E-50": "E 50",
"E-50_short": "E 50", "E-50_short": "E 50",
"E-75": "E 75", "E-75": "E 75",
"E-75_Custom": "E 75 Garm",
"E-75_Custom_short": "E 75 Garm",
"E-75_short": "E 75", "E-75_short": "E 75",
"E50_Ausf_M": "E 50 Ausf. M", "E50_Ausf_M": "E 50 Ausf. M",
"E50_Ausf_M_Custom": "E 50 M Roaring", "E50_Ausf_M_Custom": "E 50 M Roaring",
@@ -4788,6 +5020,8 @@
"F116_Bat_Chat_Bourrasque_Custom": "Bourrasque Noir", "F116_Bat_Chat_Bourrasque_Custom": "Bourrasque Noir",
"F116_Bat_Chat_Bourrasque_Custom_short": "Bourrasque Noir", "F116_Bat_Chat_Bourrasque_Custom_short": "Bourrasque Noir",
"F116_Bat_Chat_Bourrasque_short": "B-C Bourrasque", "F116_Bat_Chat_Bourrasque_short": "B-C Bourrasque",
"F117_Alt_Proto_AMX_30": "AltProto AMX 30",
"F117_Alt_Proto_AMX_30_short": "A.P. AMX 30",
"F19_Lorraine40t": "Lorraine 40 t", "F19_Lorraine40t": "Lorraine 40 t",
"F19_Lorraine40t_Com": "Lorraine 40 t Fearless", "F19_Lorraine40t_Com": "Lorraine 40 t Fearless",
"F19_Lorraine40t_Com_short": "Lorraine Fearless", "F19_Lorraine40t_Com_short": "Lorraine Fearless",
@@ -4862,6 +5096,7 @@
"Ford_GAF_500": "Ford GAF (500)", "Ford_GAF_500": "Ford GAF (500)",
"Ford_GAF_V8": "Ford GAF V8", "Ford_GAF_V8": "Ford GAF V8",
"Ford_GAN": "Ford GAN", "Ford_GAN": "Ford GAN",
"Ford_GAN_BP": "Ford GAN",
"Ford_GAN_T": "Ford GAN No. 17", "Ford_GAN_T": "Ford GAN No. 17",
"Ford_GAN_T23": "None", "Ford_GAN_T23": "None",
"Ford_V8-221": "Ford V8-221", "Ford_V8-221": "Ford V8-221",
@@ -4913,9 +5148,9 @@
"G142_M48RPz_Custom_short": "Räumpanzer Rhino", "G142_M48RPz_Custom_short": "Räumpanzer Rhino",
"G142_M48RPz_short": "M48A2 Räumpanzer", "G142_M48RPz_short": "M48A2 Räumpanzer",
"G144_Kpz_50t": "Kampfpanzer 50 t", "G144_Kpz_50t": "Kampfpanzer 50 t",
"G144_Kpz_50t_Custom": "KpfPz 50 t König", "G144_Kpz_50t_Custom": "Kpz 50 t König",
"G144_Kpz_50t_Custom_short": "KpfPz 50 t König", "G144_Kpz_50t_Custom_short": "Kpz 50 t König",
"G144_Kpz_50t_short": "KpfPz 50 t", "G144_Kpz_50t_short": "Kpz 50 t",
"G147_mPanzer_64": "Kunze Panzer", "G147_mPanzer_64": "Kunze Panzer",
"G147_mPanzer_64_Custom": "Kunze Pz. Daredevil", "G147_mPanzer_64_Custom": "Kunze Pz. Daredevil",
"G147_mPanzer_64_Custom_short": "Kunze Pz. Daredevil", "G147_mPanzer_64_Custom_short": "Kunze Pz. Daredevil",
@@ -4951,10 +5186,20 @@
"GB08_Churchill_I": "Churchill I", "GB08_Churchill_I": "Churchill I",
"GB08_Churchill_I_short": "Churchill I", "GB08_Churchill_I_short": "Churchill I",
"GB09_Churchill_VII": "Churchill VII", "GB09_Churchill_VII": "Churchill VII",
"GB09_Churchill_VII_BP": "Churchill W",
"GB09_Churchill_VII_BP_Custom": "Churchill W Defender",
"GB09_Churchill_VII_BP_Custom_short": "Churchill W Defender",
"GB09_Churchill_VII_BP_short": "Churchill W",
"GB109_GSOR_1008": "GSOR 1008", "GB109_GSOR_1008": "GSOR 1008",
"GB109_GSOR_1008_Custom": "GSOR 1008 Excalibur",
"GB109_GSOR_1008_Custom_short": "GSOR 1008 Excalibur",
"GB109_GSOR_1008_short": "GSOR 1008", "GB109_GSOR_1008_short": "GSOR 1008",
"GB10_Black_Prince": "Black Prince", "GB10_Black_Prince": "Black Prince",
"GB10_Black_Prince_short": "Black Prince", "GB10_Black_Prince_short": "Black Prince",
"GB112_Caliban": "Caliban",
"GB112_Caliban_Custom": "Caliban Hugo",
"GB112_Caliban_Custom_short": "Caliban Hugo",
"GB112_Caliban_short": "Caliban",
"GB11_Caernarvon": "Caernarvon", "GB11_Caernarvon": "Caernarvon",
"GB11_Caernarvon_short": "Caernarvon", "GB11_Caernarvon_short": "Caernarvon",
"GB12_Conqueror": "Conqueror", "GB12_Conqueror": "Conqueror",
@@ -5008,8 +5253,10 @@
"GB60_Covenanter": "Covenanter", "GB60_Covenanter": "Covenanter",
"GB60_Covenanter_short": "Covenanter", "GB60_Covenanter_short": "Covenanter",
"GB63_TOG_II": "TOG II*", "GB63_TOG_II": "TOG II*",
"GB63_TOG_II_BP": "#uk_vehicles:GB63_TOG_II_BP", "GB63_TOG_II_BP": "Blasteroid",
"GB63_TOG_II_BP_short": "#uk_vehicles:GB63_TOG_II_BP_short", "GB63_TOG_II_BP_Custom": "Blasteroid Neo",
"GB63_TOG_II_BP_Custom_short": "Blasteroid Neo",
"GB63_TOG_II_BP_short": "Blasteroid",
"GB68_Matilda_Black_Prince": "Matilda Black Prince", "GB68_Matilda_Black_Prince": "Matilda Black Prince",
"GB68_Matilda_Black_Prince_short": "Matilda BP", "GB68_Matilda_Black_Prince_short": "Matilda BP",
"GB69_Cruiser_Mk_II": "Cruiser Mk. II", "GB69_Cruiser_Mk_II": "Cruiser Mk. II",
@@ -5033,10 +5280,10 @@
"GB83_4005_Custom": "FV4005 Overkill", "GB83_4005_Custom": "FV4005 Overkill",
"GB83_4005_Custom_short": "FV4005 Overkill", "GB83_4005_Custom_short": "FV4005 Overkill",
"GB83_4005_short": "FV4005", "GB83_4005_short": "FV4005",
"GB84_Chieftain_Mk6": "Chieftain Mk.6", "GB84_Chieftain_Mk6": "Chieftain Mk. 6",
"GB84_Chieftain_Mk6_Custom": "Chieftain Mk.6 Royal", "GB84_Chieftain_Mk6_Custom": "Chieftain Mk. 6 Royal",
"GB84_Chieftain_Mk6_Custom_short": "Chieftain Mk.6 Royal", "GB84_Chieftain_Mk6_Custom_short": "Chieftain Mk. 6 Royal",
"GB84_Chieftain_Mk6_short": "Chieftain Mk.6", "GB84_Chieftain_Mk6_short": "Chieftain Mk. 6",
"GB85_Cromwell_Berlin": "Cromwell B", "GB85_Cromwell_Berlin": "Cromwell B",
"GB85_Cromwell_Berlin_Custom": "Cromwell B Berlin", "GB85_Cromwell_Berlin_Custom": "Cromwell B Berlin",
"GB85_Cromwell_Berlin_Custom_short": "Cromwell B Berlin", "GB85_Cromwell_Berlin_Custom_short": "Cromwell B Berlin",
@@ -5074,6 +5321,8 @@
"GB_Centurion_MkDF": "Defender Mk. 1", "GB_Centurion_MkDF": "Defender Mk. 1",
"GB_Centurion_MkDF_short": "Defender Mk. 1", "GB_Centurion_MkDF_short": "Defender Mk. 1",
"GB_Churchill_MkVI": "Churchill Mk. VI", "GB_Churchill_MkVI": "Churchill Mk. VI",
"GB_Churchill_MkVIII": "Churchill VIII",
"GB_Churchill_MkVIII_short": "Churchill VIII",
"GB_Churchill_MkVI_short": "Churchill Mk. VI", "GB_Churchill_MkVI_short": "Churchill Mk. VI",
"GB_FV301": "FV301", "GB_FV301": "FV301",
"GB_FV301_short": "FV301", "GB_FV301_short": "FV301",
@@ -5093,6 +5342,7 @@
"GMC_6004": "GMC 6004T", "GMC_6004": "GMC 6004T",
"GMC_6046": "GMC 6046", "GMC_6046": "GMC 6046",
"GMC_6046A": "GMC 6046A", "GMC_6046A": "GMC 6046A",
"GMC_6046_BP": "GMC 6046B",
"GMC_Allison_V1710-E32": "GMC Allison V1710-E32", "GMC_Allison_V1710-E32": "GMC Allison V1710-E32",
"GMC_Allison_V1710-E32_short": "GMC Allison\\nV1710-E32", "GMC_Allison_V1710-E32_short": "GMC Allison\\nV1710-E32",
"GMS-6046B": "GMS 6046B", "GMS-6046B": "GMS 6046B",
@@ -5110,9 +5360,9 @@
"G_E_75TS_Custom_short": "E 75 TS Eagle", "G_E_75TS_Custom_short": "E 75 TS Eagle",
"G_E_short": "G.W. E 100", "G_E_short": "G.W. E 100",
"G_KpfPz_70": "Kampfpanzer 70", "G_KpfPz_70": "Kampfpanzer 70",
"G_KpfPz_70_Custom": "KpfPz 70 Cavalry", "G_KpfPz_70_Custom": "Kpz 70 Cavalry",
"G_KpfPz_70_Custom_short": "KpfPz 70 Cavalry", "G_KpfPz_70_Custom_short": "Kpz 70 Cavalry",
"G_KpfPz_70_short": "KpfPz 70", "G_KpfPz_70_short": "Kpz 70",
"G_Panther": "G.W. Panther", "G_Panther": "G.W. Panther",
"G_Panther_short": "G.W. Panther", "G_Panther_short": "G.W. Panther",
"G_Tiger": "G.W. Tiger", "G_Tiger": "G.W. Tiger",
@@ -5198,6 +5448,8 @@
"IS8": "IS-8", "IS8": "IS-8",
"ISU-152": "ISU-152", "ISU-152": "ISU-152",
"ISU-152_01": "ISU-152", "ISU-152_01": "ISU-152",
"ISU-152_Custom_short_v2": "ISU-152 Tundra",
"ISU-152_Custom_v2": "ISU-152 Tundra",
"ISU-152_mod_1945": "ISU-152M", "ISU-152_mod_1945": "ISU-152M",
"ISU152_Custom": "ISU-152 Hunter", "ISU152_Custom": "ISU-152 Hunter",
"ISU152_Custom_short": "ISU-152 Hunter", "ISU152_Custom_short": "ISU-152 Hunter",
@@ -5218,6 +5470,10 @@
"It10_P43_BD": "P.43/06 anniversario", "It10_P43_BD": "P.43/06 anniversario",
"It10_P43_BD_short": "P.43/06 ann.", "It10_P43_BD_short": "P.43/06 ann.",
"It10_P43_bis": "P.43 bis", "It10_P43_bis": "P.43 bis",
"It10_P43_bis_BP": "Shadowhunter",
"It10_P43_bis_BP_Custom": "Shadowhunter Enchanted",
"It10_P43_bis_BP_Custom_short": "Shadowhunter Enchanted",
"It10_P43_bis_BP_short": "Shadowhunter",
"It10_P43_bis_short": "P.43 bis", "It10_P43_bis_short": "P.43 bis",
"It12_Prototipo_Standard_B": "Prototipo Standard B", "It12_Prototipo_Standard_B": "Prototipo Standard B",
"It12_Prototipo_Standard_B_short": "Standard B", "It12_Prototipo_Standard_B_short": "Standard B",
@@ -5229,6 +5485,12 @@
"It14_P44_Pantera_Custom": "P.44 Pantera Furious", "It14_P44_Pantera_Custom": "P.44 Pantera Furious",
"It14_P44_Pantera_Custom_short": "P.44 Pantera Furious", "It14_P44_Pantera_Custom_short": "P.44 Pantera Furious",
"It14_P44_Pantera_short": "P.44 Pantera", "It14_P44_Pantera_short": "P.44 Pantera",
"It20_Car_Comb_45t": "Carro da Combattimento 45t",
"It20_Car_Comb_45t_Custom": "Carro 45t Glitch",
"It20_Car_Comb_45t_Custom_short": "Carro 45t Glitch",
"It20_Car_Comb_45t_short": "Carro 45t",
"It21_Controcarro3_Minotauro": "Controcarro 3 Minotauro",
"It21_Controcarro3_Minotauro_short": "Minotauro",
"J12_Shinobi": "Shinobi", "J12_Shinobi": "Shinobi",
"J12_Shinobi_short": "Shinobi", "J12_Shinobi_short": "Shinobi",
"J20_Ho_Ri_type3": "Ho-Ri Type III", "J20_Ho_Ri_type3": "Ho-Ri Type III",
@@ -5242,11 +5504,23 @@
"J23_Chi_To_SPG": "Chi-To SPG", "J23_Chi_To_SPG": "Chi-To SPG",
"J23_Chi_To_SPG_short": "Chi-To SPG", "J23_Chi_To_SPG_short": "Chi-To SPG",
"J24_Type_57": "Type 57", "J24_Type_57": "Type 57",
"J24_Type_57_Custom": "Type 57 Mecha",
"J24_Type_57_Custom_short": "Type 57 Mecha",
"J24_Type_57_short": "Type 57", "J24_Type_57_short": "Type 57",
"J25_Type71": "Type 71", "J25_Type71": "Type 71",
"J25_Type71_Custom": "Type 71 Sankai",
"J25_Type71_Custom_short": "Type 71 Sankai",
"J25_Type71_short": "Type 71", "J25_Type71_short": "Type 71",
"J26_Type_68": "#japan_vehicles:J26_Type_68", "J26_Type_68": "Type 68",
"J26_Type_68_short": "#japan_vehicles:J26_Type_68_short", "J26_Type_68_short": "Type 68",
"J27_Chi_Se": "Chi-Se",
"J27_Chi_Se_short": "Chi-Se",
"J28_Ju_To": "Ju-To",
"J28_Ju_To_short": "Ju-To",
"J29_Ju_Nu": "Ju-Nu",
"J29_Ju_Nu_short": "Ju-Nu",
"J30_Mitsu_108": "Mitsu 108",
"J30_Mitsu_108_short": "Mitsu 108",
"J_V12_M": "American la France “J” V-12", "J_V12_M": "American la France “J” V-12",
"JagdPanther": "Jagdpanther", "JagdPanther": "Jagdpanther",
"JagdPantherII": "Jagdpanther II", "JagdPantherII": "Jagdpanther II",
@@ -5310,6 +5584,11 @@
"KV_1_Custom_short": "KV-1 Warrior", "KV_1_Custom_short": "KV-1 Warrior",
"KV_2": "KV-2", "KV_2": "KV-2",
"KZ-V8": "KZ V8", "KZ-V8": "KZ V8",
"Kawasaki_Ha60_IB": "Kawasaki Ha60 IB",
"Kawasaki_Ha60_Ic": "Kawasaki Ha60 Ic",
"Kawasaki_Ha8": "Kawasaki Ha8",
"Kawasaki_Ha9_IIC": "Kawasaki Ha9 IIC",
"Kawasaki_Ha9_IID": "Kawasaki Ha9 IID",
"Ke_Ho": "Type 5 Ke-Ho", "Ke_Ho": "Type 5 Ke-Ho",
"Ke_Ho_short": "Ke-Ho", "Ke_Ho_short": "Ke-Ho",
"Ke_Ni": "Type 98 Ke-Ni", "Ke_Ni": "Type 98 Ke-Ni",
@@ -5379,6 +5658,8 @@
"M18E2BP": "BLTZ9000", "M18E2BP": "BLTZ9000",
"M18E2BP_short": "BLTZ9000", "M18E2BP_short": "BLTZ9000",
"M18_Hellcat": "M18 Hellcat", "M18_Hellcat": "M18 Hellcat",
"M18_Hellcat_Custom": "Hellcat Arracourt",
"M18_Hellcat_Custom_short": "Hellcat Arracourt",
"M18_Hellcat_short": "Hellcat", "M18_Hellcat_short": "Hellcat",
"M22_Locust": "M22 Locust", "M22_Locust": "M22 Locust",
"M22_Locust_short": "Locust", "M22_Locust_short": "Locust",
@@ -5393,6 +5674,10 @@
"M2_lt_short": "M2 Light", "M2_lt_short": "M2 Light",
"M2_med": "M2 Medium Tank", "M2_med": "M2 Medium Tank",
"M2_med_short": "M2 Medium", "M2_med_short": "M2 Medium",
"M36_BP": "Enforcer",
"M36_BP_Custom": "Vandal",
"M36_BP_Custom_short": "Vandal",
"M36_BP_short": "Enforcer",
"M36_Slagger": "M36 Jackson", "M36_Slagger": "M36 Jackson",
"M36_Slagger_short": "Jackson", "M36_Slagger_short": "Jackson",
"M36_Slagger_skin_volar": "M36 Valor", "M36_Slagger_skin_volar": "M36 Valor",
@@ -5464,6 +5749,7 @@
"MB-838_CAM-500_short": "MB 838 CaM 500", "MB-838_CAM-500_short": "MB 838 CaM 500",
"MB507": "DB 603 A2", "MB507": "DB 603 A2",
"MB517": "MB 517", "MB517": "MB 517",
"MB_822_Ka_500": "MB 822 Ka-500",
"MB_837_Aa": "MB 837 Aa", "MB_837_Aa": "MB 837 Aa",
"MB_837_Ea": "MB 837 Ea", "MB_837_Ea": "MB 837 Ea",
"MB_837_F": "MB 837 F", "MB_837_F": "MB 837 F",
@@ -5479,6 +5765,7 @@
"MTLS-1G14": "MTLS-1G14", "MTLS-1G14": "MTLS-1G14",
"MTLS_D1": "MTLS D1", "MTLS_D1": "MTLS D1",
"MTLS_T1": "MTLS T1", "MTLS_T1": "MTLS T1",
"MTU_MB-838_Ca_M-500": "MTU MB 838 CaM 500B",
"MT_50_T": "MT-50T", "MT_50_T": "MT-50T",
"M_40": "M-40", "M_40": "M-40",
"MarderII": "Marder II", "MarderII": "Marder II",
@@ -5496,6 +5783,7 @@
"Mausketten": "Maus", "Mausketten": "Maus",
"Mausturm": "Maus", "Mausturm": "Maus",
"Maybach_HL100": "Maybach HL 100", "Maybach_HL100": "Maybach HL 100",
"Maybach_HL100V2": "Maybach HL 100 B",
"Maybach_HL45P": "Maybach HL 45 P", "Maybach_HL45P": "Maybach HL 45 P",
"Maybach_HL57TR": "Maybach HL 57 TR", "Maybach_HL57TR": "Maybach HL 57 TR",
"Maybach_HL62TR": "Maybach HL 62 TR", "Maybach_HL62TR": "Maybach HL 62 TR",
@@ -5545,6 +5833,7 @@
"Maybach_HL_210_TRM_P45_short": "Maybach HL 210\\nTRM P45", "Maybach_HL_210_TRM_P45_short": "Maybach HL 210\\nTRM P45",
"Maybach_HL_230": "Maybach HL 230", "Maybach_HL_230": "Maybach HL 230",
"Maybach_HL_230P30A": "Maybach HL 230 P30", "Maybach_HL_230P30A": "Maybach HL 230 P30",
"Maybach_HL_230P30A_Desert": "Flaming Heart",
"Maybach_HL_230P30_2500rpm": "Maybach HL 230 P30 2500 rpm", "Maybach_HL_230P30_2500rpm": "Maybach HL 230 P30 2500 rpm",
"Maybach_HL_230P45": "Maybach HL 230 P45F", "Maybach_HL_230P45": "Maybach HL 230 P45F",
"Maybach_HL_230P45C": "Maybach HL 230 P45C", "Maybach_HL_230P45C": "Maybach HL 230 P45C",
@@ -5585,12 +5874,13 @@
"Meadows_HOP_OC": "Meadows H.O.P. O.C.", "Meadows_HOP_OC": "Meadows H.O.P. O.C.",
"Medous_MAT": "Meadows MAT", "Medous_MAT": "Meadows MAT",
"Meteor_Mark_IV_Mod_271": "Meteor Mark IV Mod 271", "Meteor_Mark_IV_Mod_271": "Meteor Mark IV Mod 271",
"Meteorite_Mk_202": "Meteorite Mk. 202",
"Minkovsky_engine": "Minkovsky engine", "Minkovsky_engine": "Minkovsky engine",
"Mitsubishi": "Mitsubishi A475VD", "Mitsubishi": "Mitsubishi A475VD",
"Mitsubishi_10ZF": "Mitsubishi 10ZF", "Mitsubishi_10ZF": "Mitsubishi 10ZF",
"Mitsubishi_12ZF": "Mitsubishi 12ZF", "Mitsubishi_12ZF": "Mitsubishi 12ZF",
"Mitsubishi_12ZM": "#japan_vehicles:Mitsubishi_12ZM", "Mitsubishi_12ZM": "Mitsubishi 12ZM",
"Mitsubishi_12ZT": "#japan_vehicles:Mitsubishi_12ZT", "Mitsubishi_12ZT": "Mitsubishi 12ZT",
"Mitsubishi_MT11": "Mitsubishi MT11", "Mitsubishi_MT11": "Mitsubishi MT11",
"Mk.II_CS": "57mm Common Shell", "Mk.II_CS": "57mm Common Shell",
"Mod_T-10": "IS-3", "Mod_T-10": "IS-3",
@@ -5614,13 +5904,15 @@
"OQF_17pdr_Gun_Mk_VIIG_short": "OQF 17-pdr Gun Mk. VII G", "OQF_17pdr_Gun_Mk_VIIG_short": "OQF 17-pdr Gun Mk. VII G",
"OQF_17pdr_Gun_Mk_VIII": "OQF 17-pdr Gun Mk. VIII", "OQF_17pdr_Gun_Mk_VIII": "OQF 17-pdr Gun Mk. VIII",
"OQF_17pdr_Gun_Mk_VIII_short": "OQF 17-pdr Gun Mk. VIII", "OQF_17pdr_Gun_Mk_VIII_short": "OQF 17-pdr Gun Mk. VIII",
"OQF_17pdr_Gun_Mk_VII_BP": "#uk_vehicles:OQF_17pdr_Gun_Mk_VII_BP", "OQF_17pdr_Gun_Mk_VII_BP": "OQF 17-pdr Gun Mk. VII",
"OQF_17pdr_Gun_Mk_VII_BP_short": "#uk_vehicles:OQF_17pdr_Gun_Mk_VII_BP_short", "OQF_17pdr_Gun_Mk_VII_BP_short": "OQF 17-pdr Gun Mk. VII",
"OQF_3pdr_Gun": "OQF 3-pdr Gun Mk. II", "OQF_3pdr_Gun": "OQF 3-pdr Gun Mk. II",
"OQF_55_pdr_AT_Gun": "OQF 55-pdr AT Gun", "OQF_55_pdr_AT_Gun": "OQF 55-pdr AT Gun",
"OQF_55_pdr_AT_Gun_short": "OQF 55-pdr AT Gun", "OQF_55_pdr_AT_Gun_short": "OQF 55-pdr AT Gun",
"OQF_77mm_AT_Gun_MkII": "OQF 77 mm AT Gun Mk. II", "OQF_77mm_AT_Gun_MkII": "OQF 77 mm AT Gun Mk. II",
"OQF_77mm_Gun_MkII": "OQF 77 mm Gun Mk. II", "OQF_77mm_Gun_MkII": "OQF 77 mm Gun Mk. II",
"OQF_77mm_Gun_MkII_BP": "OQF 77 mm Gun Mk. II B",
"OQF_77mm_Gun_MkII_BP_short": "OQF 77 mm Gun Mk. II B",
"Object263": "Object 263", "Object263": "Object 263",
"Object263_Custom": "Obj. 263 Graywolf", "Object263_Custom": "Obj. 263 Graywolf",
"Object263_Custom_short": "Obj. 263 Graywolf", "Object263_Custom_short": "Obj. 263 Graywolf",
@@ -5713,6 +6005,16 @@
"Oth31_T150BP_Custom": "Titan-150 Trailblazer", "Oth31_T150BP_Custom": "Titan-150 Trailblazer",
"Oth31_T150BP_Custom_short": "Titan-150 Trailblazer", "Oth31_T150BP_Custom_short": "Titan-150 Trailblazer",
"Oth31_T150BP_short": "Titan-150", "Oth31_T150BP_short": "Titan-150",
"Oth33_Blizzard_BP": "Rover",
"Oth33_Blizzard_BP_Custom": "Rover LunarX",
"Oth33_Blizzard_BP_Custom_short": "Rover LunarX",
"Oth33_Blizzard_BP_short": "Rover",
"Oth34_PanzerMummy": "Pharaoh",
"Oth34_PanzerMummy_Custom": "Pharaoh All-Seeing",
"Oth34_PanzerMummy_Custom_short": "Pharaoh All-Seeing",
"Oth34_PanzerMummy_short": "Pharaoh",
"Oth35_PanzerMejai": "Medjay",
"Oth35_PanzerMejai_short": "Medjay",
"PLInz_RWA": "PLInz R.W.A.", "PLInz_RWA": "PLInz R.W.A.",
"PLT_27": "PLT-27", "PLT_27": "PLT-27",
"Paccar_120_325HT": "Gas Boiler", "Paccar_120_325HT": "Gas Boiler",
@@ -5725,7 +6027,7 @@
"PanzerJager_I": "Panzerjäger I", "PanzerJager_I": "Panzerjäger I",
"PanzerJager_I_short": "Pz.Jäg. I", "PanzerJager_I_short": "Pz.Jäg. I",
"Paxman_Ricardo": "Paxman 12TP", "Paxman_Ricardo": "Paxman 12TP",
"Paxman_Ricardo_BP": "#uk_vehicles:Paxman_Ricardo_BP", "Paxman_Ricardo_BP": "Paxman 12TP",
"Pershing": "M26 Pershing", "Pershing": "M26 Pershing",
"Pershing_short": "Pershing", "Pershing_short": "Pershing",
"Pershingchassis1": "M26T80E1", "Pershingchassis1": "M26T80E1",
@@ -5889,6 +6191,10 @@
"PzVI_AusfBP_short": "Icebreaker", "PzVI_AusfBP_short": "Icebreaker",
"PzVI_AusfTU": "Tiger I", "PzVI_AusfTU": "Tiger I",
"PzVI_AusfTU_short": "Tiger I", "PzVI_AusfTU_short": "Tiger I",
"PzVI_Custom_short_v2": "Tiger I Fox",
"PzVI_Custom_short_v3": "Tiger I Baron",
"PzVI_Custom_v2": "Tiger I Fox",
"PzVI_Custom_v3": "Tiger I Baron",
"PzVI_GuP": "Tiger Kuromorimine SP", "PzVI_GuP": "Tiger Kuromorimine SP",
"PzVI_GuP_short": "Tiger Kuromorimine SP", "PzVI_GuP_short": "Tiger Kuromorimine SP",
"PzVI_Porsche_Turm": "Pz.Kpfw. Tiger Ausf. E", "PzVI_Porsche_Turm": "Pz.Kpfw. Tiger Ausf. E",
@@ -5999,6 +6305,10 @@
"R172_Object_752_Custom": "Object 752 Stargazer", "R172_Object_752_Custom": "Object 752 Stargazer",
"R172_Object_752_Custom_short": "Obj. 752 Stargazer", "R172_Object_752_Custom_short": "Obj. 752 Stargazer",
"R172_Object_752_short": "Object 752", "R172_Object_752_short": "Object 752",
"R175_IS_2E": "IS-2 shielded",
"R175_IS_2E_Custom": "IS-2 shld Trapper",
"R175_IS_2E_Custom_short": "IS-2 shld Trapper",
"R175_IS_2E_short": "IS-2 shielded",
"R180_Object_274A": "Object 274a", "R180_Object_274A": "Object 274a",
"R180_Object_274A_Custom": "Object 274a Leshy", "R180_Object_274A_Custom": "Object 274a Leshy",
"R180_Object_274A_Custom_short": "Object 274a Leshy", "R180_Object_274A_Custom_short": "Object 274a Leshy",
@@ -6055,6 +6365,7 @@
"Rolls_Royce_Meteor_MkIV_B12": "Rolls-Royce Meteor Mk. IVB/12", "Rolls_Royce_Meteor_MkIV_B12": "Rolls-Royce Meteor Mk. IVB/12",
"Rolls_Royce_Meteor_P": "Rolls-Royce Meteor P", "Rolls_Royce_Meteor_P": "Rolls-Royce Meteor P",
"Rolls_Royse_Meteor": "Rolls-Royce Meteor", "Rolls_Royse_Meteor": "Rolls-Royce Meteor",
"Rolls_Royse_Meteor_Desert": "Ancient Spirit",
"Rolls_Royse_Meteor_M120": "Rolls-Royce Meteor M120", "Rolls_Royse_Meteor_M120": "Rolls-Royce Meteor M120",
"Rolls_Royse_Meteor_Mk12": "Rolls-Royce Meteor Mk. 12", "Rolls_Royse_Meteor_Mk12": "Rolls-Royce Meteor Mk. 12",
"Rolls_Royse_Meteor_MkIV": "Rolls-Royce Meteor Mk. IVB", "Rolls_Royse_Meteor_MkIV": "Rolls-Royce Meteor Mk. IVB",
@@ -6104,6 +6415,8 @@
"S26_Lancen_C_Custom": "Lansen Moose", "S26_Lancen_C_Custom": "Lansen Moose",
"S26_Lancen_C_Custom_short": "Lansen Moose", "S26_Lancen_C_Custom_short": "Lansen Moose",
"S26_Lancen_C_short": "Lansen C", "S26_Lancen_C_short": "Lansen C",
"S31_Strv_K": "Strv K",
"S31_Strv_K_short": "Strv K",
"S35_captured": "Pz.Kpfw. S35 739 (f)", "S35_captured": "Pz.Kpfw. S35 739 (f)",
"S35_captured_short": "Pz. S35", "S35_captured_short": "Pz. S35",
"SA35_4": "SA35 mle. 4", "SA35_4": "SA35 mle. 4",
@@ -6112,6 +6425,7 @@
"SFA_F12": "SFA F12", "SFA_F12": "SFA F12",
"SN-230TR": "SN-230TR", "SN-230TR": "SN-230TR",
"SN-230TRM": "SN-230TRM", "SN-230TRM": "SN-230TRM",
"SOFAM-12-GSds_B": "SOFAM 12 GSds B",
"SOFAM_12GSds_A": "SOFAM 12 GSds", "SOFAM_12GSds_A": "SOFAM 12 GSds",
"SOFAM_12_58": "SOFAM 12 58", "SOFAM_12_58": "SOFAM 12 58",
"SOFAM_8Gxb": "SOFAM 8 Gxb", "SOFAM_8Gxb": "SOFAM 8 Gxb",
@@ -6137,6 +6451,8 @@
"SU-14_1": "SU-14-2", "SU-14_1": "SU-14-2",
"SU-152": "SU-152", "SU-152": "SU-152",
"SU-152_01": "SU-152", "SU-152_01": "SU-152",
"SU-152_Custom": "SU-152 Taiga",
"SU-152_Custom_short": "SU-152 Тaiga",
"SU-152_mod_1943": "SU-152", "SU-152_mod_1943": "SU-152",
"SU-152_mod_1944": "SU-152M", "SU-152_mod_1944": "SU-152M",
"SU-18": "SU-18", "SU-18": "SU-18",
@@ -6301,6 +6617,10 @@
"T20D1": "T20D1", "T20D1": "T20D1",
"T20D2": "T20D2", "T20D2": "T20D2",
"T21": "T21", "T21": "T21",
"T21_BP": "Ranger",
"T21_BP_Custom": "Ranger All-Terrain",
"T21_BP_Custom_short": "Ranger All-Terrain",
"T21_BP_short": "Ranger",
"T23": "T23", "T23": "T23",
"T23D51080": "T23D51080", "T23D51080": "T23D51080",
"T23E3": "T23E3", "T23E3": "T23E3",
@@ -6455,6 +6775,7 @@
"V-2-54IS_GuP": "V-2-54 GuP", "V-2-54IS_GuP": "V-2-54 GuP",
"V-2IS": "V-2IS", "V-2IS": "V-2IS",
"V-2IS_B": "V-2IS", "V-2IS_B": "V-2IS",
"V-2IS_B_2": "V-2IS-2",
"V-2IS_R": "V-2-10", "V-2IS_R": "V-2-10",
"V-2IS_R1": "V-2-10", "V-2IS_R1": "V-2-10",
"V-2K": "V-2K", "V-2K": "V-2K",
@@ -6566,6 +6887,7 @@
"V_2_34": "V-2-34", "V_2_34": "V-2-34",
"V_2_34M": "V-2-34M", "V_2_34M": "V-2-34M",
"V_2_34_modificato": "V-2-34 modificato", "V_2_34_modificato": "V-2-34 modificato",
"V_2_34_modificato_BP": "V-2-34 modificato B",
"V_2_54SCG": "V-2-54SCG", "V_2_54SCG": "V-2-54SCG",
"V_46_5": "V-46-5", "V_46_5": "V-46-5",
"V_54": "V-54", "V_54": "V-54",
@@ -6577,7 +6899,9 @@
"Van_Helsing_Custom_short": "Helsing Raven", "Van_Helsing_Custom_short": "Helsing Raven",
"Van_Helsing_short": "Helsing", "Van_Helsing_short": "Helsing",
"Vauxhall_Bedford_Twin_Six": "Bedford Twin-Six", "Vauxhall_Bedford_Twin_Six": "Bedford Twin-Six",
"Vauxhall_Bedford_Twin_SixBP": "Bedford Twin-Six",
"Vauxhall_Bedford_Twin_Six_B": "Bedford Twin-Six B", "Vauxhall_Bedford_Twin_Six_B": "Bedford Twin-Six B",
"Vauxhall_Bedford_Twin_Six_BP": "Bedford Twin-Six II",
"Verbesserteturm": "L.K.A. 2", "Verbesserteturm": "L.K.A. 2",
"Vickers-Armstrong": "Armstrong Siddeley V8", "Vickers-Armstrong": "Armstrong Siddeley V8",
"Vickers-Armstrong_MkII": "Armstrong Siddeley V8 SC", "Vickers-Armstrong_MkII": "Armstrong Siddeley V8 SC",
@@ -6593,6 +6917,8 @@
"W_12_5_B": "W12-5B", "W_12_5_B": "W12-5B",
"W_12_5_P": "W12-5P", "W_12_5_P": "W12-5P",
"Waffentrager_IV": "Waffenträger auf Pz. IV", "Waffentrager_IV": "Waffenträger auf Pz. IV",
"Waffentrager_IV_CL": "Waffenträger Camelot",
"Waffentrager_IV_CL_short": "WT Camelot",
"Waffentrager_IV_short": "WT auf Pz. IV", "Waffentrager_IV_short": "WT auf Pz. IV",
"Waterspit_1": "Waterspit", "Waterspit_1": "Waterspit",
"Wespe": "Wespe", "Wespe": "Wespe",
@@ -6608,6 +6934,7 @@
"Wright_Continental_R-975C3": "Wright Continental R-975C3", "Wright_Continental_R-975C3": "Wright Continental R-975C3",
"Wright_Continental_R-975C4": "Wright Continental R-975C4", "Wright_Continental_R-975C4": "Wright Continental R-975C4",
"Wright_Continental_R-975C4B": "Wright Continental R-975C4B", "Wright_Continental_R-975C4B": "Wright Continental R-975C4B",
"Wright_Continental_R-975C4_BD": "Wright Continental R-975C4",
"Wright_Continental_R-975C4_short": "Wright C. R-975C4", "Wright_Continental_R-975C4_short": "Wright C. R-975C4",
"Wright_Continental_R-975EC2": "Wright Continental R-975EC2", "Wright_Continental_R-975EC2": "Wright Continental R-975EC2",
"Wright_Continental_R-975EC2_short": "Wright C. R-975EC2", "Wright_Continental_R-975EC2_short": "Wright C. R-975EC2",
+28 -28
View File
@@ -1,8 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
## PYTHON VERSION MUST BE 3.7 OR HIGHER ## PYTHON VERSION MUST BE 3.7 OR HIGHER
import json, argparse, inspect, sys, os, io, base64, aiohttp, urllib, asyncio, aiofiles, aioconsole import json, argparse, sys, os, io, asyncio, aiofiles, aioconsole
import logging, re, concurrent.futures, configparser, motor.motor_asyncio, ssl, zipfile import logging, re, configparser, zipfile
import blitzutils as bu import blitzutils as bu
from blitzutils import WG from blitzutils import WG
from blitzutils import WoTinspector from blitzutils import WoTinspector
@@ -21,17 +21,17 @@ SKIPPED_N = 0
ERROR_N = 0 ERROR_N = 0
WIurl='https://wotinspector.com/api/replay/upload?' WIurl='https://wotinspector.com/api/replay/upload?'
WG_appID = '81381d3f45fa4aa75b78a7198eb216ad' WG_appID = '81381d3f45fa4aa75b78a7198eb216ad'
wg = None wg : WG | None = None
wi = None wi : WoTinspector | None = None
async def main(argv): async def main(argv) -> int:
global wg, wi global wg, wi
current_dir = os.getcwd() current_dir = os.getcwd()
# set the directory for the script # set the directory for the script
os.chdir(os.path.dirname(sys.argv[0])) os.chdir(os.path.dirname(sys.argv[0]))
# options defaults # options defaults
OPT_WORKERS_N = 5 OPT_WORKERS_N = 2
WG_ID = None WG_ID = None
WG_ACCOUNT = None # format: nick@server, where server is either 'eu', 'ru', 'na', 'asia' or 'china'. WG_ACCOUNT = None # format: nick@server, where server is either 'eu', 'ru', 'na', 'asia' or 'china'.
# China is not supported since WG API stats are not available there # China is not supported since WG API stats are not available there
@@ -43,13 +43,7 @@ async def main(argv):
bu.debug('Reading config file: ' + FILE_CONFIG) bu.debug('Reading config file: ' + FILE_CONFIG)
config = configparser.ConfigParser() config = configparser.ConfigParser()
config.read(FILE_CONFIG) config.read(FILE_CONFIG)
try:
if 'UPLOADER' in config.sections():
configUploader = config['UPLOADER']
OPT_WORKERS_N = configUploader.getint('opt_workers', OPT_WORKERS_N)
except (KeyError, configparser.NoSectionError) as err:
bu.error(exception=err)
try: try:
if 'WG' in config.sections(): if 'WG' in config.sections():
configWG = config['WG'] configWG = config['WG']
@@ -82,18 +76,19 @@ async def main(argv):
bu.set_log_level(args.silent, args.verbose, args.debug) bu.set_log_level(args.silent, args.verbose, args.debug)
wg = WG(WG_appID, args.tankopedia, args.mapfile) wg = WG(WG_appID, args.tankopedia, args.mapfile)
wi = WoTinspector(rate_limit=WG_RATE_LIMIT) wi = WoTinspector()
if args.account != None: if args.account is not None:
args.accountID = await wg.get_account_id(args.account) args.accountID = await wg.get_account_id(args.account)
bu.debug('WG account_id: ' + str(args.accountID)) bu.debug('WG account_id: ' + str(args.accountID))
if args.accountID == None: if args.accountID is None:
args.accountID = 0 args.accountID = 0
try: try:
queue = asyncio.Queue() queue : asyncio.Queue = asyncio.Queue()
# rebase file arguments due to moving the working directory to the script location # rebase file arguments due to moving the working directory to the script location
bu.verbose_std("replays.wotinspector.com rate limits replays to 6 / minute")
args.files = bu.rebase_file_args(current_dir, args.files) args.files = bu.rebase_file_args(current_dir, args.files)
tasks = [] tasks = []
@@ -123,7 +118,7 @@ async def main(argv):
await wg.session.close() await wg.session.close()
await wi.close() await wi.close()
return None return 0
async def mkReplayQ(queue : asyncio.Queue, files : list, title : str): async def mkReplayQ(queue : asyncio.Queue, files : list, title : str):
@@ -137,13 +132,13 @@ async def mkReplayQ(queue : asyncio.Queue, files : list, title : str):
if not line: if not line:
break break
else: else:
if (p_replayfile.match(line) != None): if (p_replayfile.match(line) is not None):
await queue.put(await mkQueueItem(line, title)) await queue.put(await mkQueueItem(line, title))
else: else:
for fn in files: for fn in files:
if fn.endswith('"'): if fn.endswith('"'):
fn = fn[:-1] fn = fn[:-1]
if os.path.isfile(fn) and (p_replayfile.match(fn) != None): if os.path.isfile(fn) and (p_replayfile.match(fn) is not None):
await queue.put(await mkQueueItem(fn, title)) await queue.put(await mkQueueItem(fn, title))
bu.debug('File added to queue: ' + fn) bu.debug('File added to queue: ' + fn)
elif os.path.isdir(fn): elif os.path.isdir(fn):
@@ -151,7 +146,7 @@ async def mkReplayQ(queue : asyncio.Queue, files : list, title : str):
for entry in dirEntry: for entry in dirEntry:
try: try:
bu.debug('Found: ' + entry.name) bu.debug('Found: ' + entry.name)
if entry.is_file() and (p_replayfile.match(entry.name) != None): if entry.is_file() and (p_replayfile.match(entry.name) is not None):
bu.debug(entry.name) bu.debug(entry.name)
await queue.put(await mkQueueItem(entry.path, title)) await queue.put(await mkQueueItem(entry.path, title))
bu.debug('File added to queue: ' + entry.path) bu.debug('File added to queue: ' + entry.path)
@@ -175,6 +170,9 @@ async def replayWorker(queue: asyncio.Queue, workerID: int, account_id: int, pri
"""Async Worker to process the replay queue""" """Async Worker to process the replay queue"""
global SKIPPED_N global SKIPPED_N
global ERROR_N global ERROR_N
global wi
assert wi is not None, "wi cannot be None"
while True: while True:
item = await queue.get() item = await queue.get()
@@ -210,7 +208,7 @@ async def replayWorker(queue: asyncio.Queue, workerID: int, account_id: int, pri
filename = os.path.basename(filename) filename = os.path.basename(filename)
bu.debug(msg_str + 'File: ' + filename) bu.debug(msg_str + 'File: ' + filename)
json_resp = await wi.post_replay(await fp.read(), filename, account_id, title, priv, N) json_resp = await wi.post_replay(await fp.read(), filename, account_id, title, priv, N)
if json_resp != None: if json_resp is not None:
if (await bu.save_JSON(replay_json_fn,json_resp)): if (await bu.save_JSON(replay_json_fn,json_resp)):
if wi.chk_JSON_replay(json_resp): if wi.chk_JSON_replay(json_resp):
if not bu.debug(msg_str + 'Replay saved OK: ' + filename): if not bu.debug(msg_str + 'Replay saved OK: ' + filename):
@@ -235,7 +233,7 @@ async def replayWorker(queue: asyncio.Queue, workerID: int, account_id: int, pri
def getTitle_old(replayfile: str, title: str, i : int) -> str: def getTitle_old(replayfile: str, title: str, i : int) -> str:
global wg global wg
if title == None: if title is None:
try: try:
filename = os.path.basename(replayfile) filename = os.path.basename(replayfile)
bu.debug(filename) bu.debug(filename)
@@ -247,8 +245,8 @@ def getTitle_old(replayfile: str, title: str, i : int) -> str:
p = re.compile('\\d{8}_\\d{4}_.*?(' + '|'.join(tank_userStrs) + ')_(' + '|'.join(map_usrStrs) + ')(?:-\\d)?\\.wotbreplay$') p = re.compile('\\d{8}_\\d{4}_.*?(' + '|'.join(tank_userStrs) + ')_(' + '|'.join(map_usrStrs) + ')(?:-\\d)?\\.wotbreplay$')
m = p.match(filename) m = p.match(filename)
if (m != None): if (m is not None):
if wg.tanks != None: if wg.tanks is not None:
tank = m.group(1) tank = m.group(1)
if tank in wg.tanks['userStr']: if tank in wg.tanks['userStr']:
tank = wg.tanks['userStr'][tank] tank = wg.tanks['userStr'][tank]
@@ -273,7 +271,9 @@ def getTitle_old(replayfile: str, title: str, i : int) -> str:
def getTitle(replayfile: str, title: str = None, i : int = 0) -> str: def getTitle(replayfile: str, title: str = None, i : int = 0) -> str:
global wg global wg
if title == None: assert wg is not None, "wg must not be None"
if title is None:
try: try:
filename = os.path.basename(replayfile) filename = os.path.basename(replayfile)
bu.debug(filename) bu.debug(filename)
@@ -289,10 +289,10 @@ def getTitle(replayfile: str, title: str = None, i : int = 0) -> str:
#player = metadata_json['playerName'] #player = metadata_json['playerName']
tank_name = wg.get_tank_name(metadata_json['vehicleCompDescriptor']) tank_name = wg.get_tank_name(metadata_json['vehicleCompDescriptor'])
map_name = wg.get_map(metadata_json['mapName']) map_name = wg.get_map(metadata_json['mapName'])
bu.debug('Tank: ' + tank_name if tank_name != None else "NOT FOUND IN tanks.json" + ' Map: ' + map_name) bu.debug('Tank: ' + tank_name if tank_name is not None else "NOT FOUND IN tanks.json" + ' Map: ' + map_name)
except Exception as err: except Exception as err:
bu.error(exception = err) bu.error(exception = err)
if (tank_name != None) and (map_name != None): if (tank_name is not None) and (map_name is not None):
title = tank_name + ' @ ' + map_name title = tank_name + ' @ ' + map_name
else: else:
title = re.sub('\\.wotbreplay$', '', filename) title = re.sub('\\.wotbreplay$', '', filename)