From 5a9f7587b9d19ff1b9a94d51b0f234b18787f1e0 Mon Sep 17 00:00:00 2001 From: Jylpah Date: Thu, 4 Oct 2018 11:24:24 +0800 Subject: [PATCH] Fix reading filenames from STDIN --- blitzutils.py | 24 +- extractTankopedia.py | 86 ++---- tanks.json | 620 ++++++++++++++++++++--------------------- wotbreplay_uploader.py | 110 +++----- 4 files changed, 395 insertions(+), 445 deletions(-) diff --git a/blitzutils.py b/blitzutils.py index 4484a45..d238819 100644 --- a/blitzutils.py +++ b/blitzutils.py @@ -133,8 +133,24 @@ class WG: } nations = [ 'ussr', 'germany', 'usa', 'china', 'france', 'uk', 'japan', 'other'] + nation_id = { + 'ussr' : 0, + 'germany' : 1, + 'usa' : 2, + 'china' : 3, + 'france' : 4, + 'uk' : 5, + 'japan' : 6, + 'other' : 7 + } tank_type = [ 'lightTank', 'mediumTank', 'heavyTank', 'AT-SPG' ] + tank_type_id = { + 'lightTank' : 0, + 'mediumTank' : 1, + 'heavyTank' : 2, + 'AT-SPG' : 3 + } URL_WG_server = { 'eu' : 'https://api.wotblitz.eu/wotb/', @@ -167,9 +183,13 @@ class WG: self.maps = json.loads(f.read()) except Exception as err: error('Could not read maps file: ' + maps_fn + '\n' + str(err)) + + if self.WG_appID != None: + self.session = aiohttp.ClientSession() + else: + self.session = None - self.session = aiohttp.ClientSession() - # self.session = None + ## Class methods ------------------------------ diff --git a/extractTankopedia.py b/extractTankopedia.py index 8507ee4..ca47685 100644 --- a/extractTankopedia.py +++ b/extractTankopedia.py @@ -3,6 +3,7 @@ # Extract tankopedia data in WG API JSON format from Blitz app files (Android APK unzipped) import sys, argparse, json, os, inspect, aiohttp, asyncio, aiofiles, re, logging, time, xmltodict, collections +import blitzutils as bu from blitzutils import WG logging.getLogger("asyncio").setLevel(logging.DEBUG) @@ -11,31 +12,25 @@ BLITZAPP_STRINGS='/assets/Data/Strings/en.yaml' BLITZAPP_VEHICLES_DIR='/assets/Data/XML/item_defs/vehicles/' BLITZAPP_VEHICLE_FILE='/list.xml' -VERBOSE = False -DEBUG = False - -wg = WG() -NATION_ID = {} -for i in range(len(wg.nations)): - NATION_ID[wg.nations[i]] = i +wg = None ## main() ------------------------------------------------------------- async def main(argv): - global VERBOSE, DEBUG + global wg parser = argparse.ArgumentParser(description='Extract Tankopedia data from Blitz game files') parser.add_argument('blitzAppBase', type=str, metavar="BLITZAPP_FOLDER", default=".", help='Base dir of the Blitz App files') - parser.add_argument('file', type=str, metavar="FILE", help='Write Tankopedia to file') - parser.add_argument('-fm', '--file-maps', dest='file_maps', type=str, default=None, help='File to write map names') + parser.add_argument('tanks', type=str, default='tanks.json',metavar="TANKS_FILE", help='File to write Tankopedia') + parser.add_argument('maps', type=str, default='maps.json', metavar='MAPS_FILE', help='File to write map names') parser.add_argument('-d', '--debug', action='store_true', default=False, help='Debug mode') parser.add_argument('-v', '--verbose', action='store_true', default=False, help='Verbose mode') args = parser.parse_args() - VERBOSE = args.verbose - DEBUG = args.debug - if DEBUG: VERBOSE = True - + bu.setVerbose(args.verbose) + bu.setDebug(args.debug) + wg = WG() + tasks = [] for nation in wg.nations: tasks.append(asyncio.create_task(extractTanks(args.blitzAppBase, nation))) @@ -46,16 +41,15 @@ async def main(argv): tank_strs, map_strs = await readUserStrs(args.blitzAppBase) - async with aiofiles.open(args.file, 'w', encoding="utf8") as outfile: - tankopedia = {} + async with aiofiles.open(args.tanks, 'w', encoding="utf8") as outfile: + tankopedia = collections.OrderedDict() tankopedia['status'] = 'ok' tankopedia['meta'] = { "count": len(tanklist) } tankopedia['data'], tankopedia['userStr'] = await convertTankNames(tanklist, tank_strs) - #debug(tankopedia) await outfile.write(json.dumps(tankopedia, ensure_ascii=False, indent=4, sort_keys=False)) - if args.file_maps != None: - async with aiofiles.open(args.file_maps, 'w', encoding="utf8") as outfile: + if args.maps != None: + async with aiofiles.open(args.maps, 'w', encoding="utf8") as outfile: await outfile.write(json.dumps(map_strs, ensure_ascii=False, indent=4, sort_keys=True)) return None @@ -67,7 +61,7 @@ async def extractTanks(blitzAppBase : str, nation: str): if not os.path.isfile(list_xml_file): print('ERROR: cannot open ' + list_xml_file) return None - debug('Opening file: ' + list_xml_file + ' (Nation: ' + nation + ')') + bu.debug('Opening file: ' + list_xml_file + ' (Nation: ' + nation + ')') async with aiofiles.open(list_xml_file, 'r') as f: try: tankList = xmltodict.parse(await f.read()) @@ -83,7 +77,7 @@ async def extractTanks(blitzAppBase : str, nation: str): tank['type'] = await getTankType(tank_xml['tags']) tanks.append(tank) except Exception as err: - error(err) + bu.error(err) sys.exit(2) return tanks @@ -92,7 +86,7 @@ async def readUserStrs(blitzAppBase : str) -> dict: tank_strs = {} map_strs = {} filename = blitzAppBase + BLITZAPP_STRINGS - debug('Opening file: ' + filename + ' for reading UserStrings') + bu.debug('Opening file: ' + filename + ' for reading UserStrings') try: async with aiofiles.open(filename, 'r', encoding="utf8") as f: p_tank = re.compile('^"(#\\w+?_vehicles:.+?)": "(.+)"$') @@ -108,7 +102,7 @@ async def readUserStrs(blitzAppBase : str) -> dict: map_strs[m.group(1)] = m.group(2) except Exception as err: - error(err) + bu.error(err) sys.exit(1) return tank_strs, map_strs @@ -117,27 +111,31 @@ async def convertTankNames(tanklist : list, tank_strs: dict) -> dict: """Convert tank names for Tankopedia""" tankopedia = {} userStrs = {} - tank_ids = [] + try: for tank in tanklist: tank['name'] = tank_strs[tank['userStr']] userStrs[tank['userStr'].split(':')[1]] = tank['name'] tank.pop('userStr', None) tankopedia[str(tank['tank_id'])] = tank - tank_ids.append(tank['tank_id']) # for sorting... - tank_ids.sort() + # sorting tankopedia_sorted = collections.OrderedDict() - for tank_id in tank_ids: + for tank_id in sorted(tankopedia.keys(), key=int): tankopedia_sorted[str(tank_id)] = tankopedia[str(tank_id)] + userStrs_sorted = collections.OrderedDict() + for userStr in sorted(userStrs.keys()): + userStrs_sorted[userStr] = userStrs[userStr] + except Exception as err: - error(err) + bu.error(err) sys.exit(1) - return tankopedia_sorted, userStrs + + return tankopedia_sorted, userStrs_sorted async def getTankID(nation: str, tankID : int) -> int: - return (tankID << 8) + (NATION_ID[nation] << 4) + 1 + return (tankID << 8) + (wg.nation_id[nation] << 4) + 1 async def getTankType(tagstr : str): tags = tagstr.split(' ') @@ -146,34 +144,6 @@ async def getTankType(tagstr : str): return t_type return None -def verbose(msg = ""): - """Print a message""" - if VERBOSE: - curframe = inspect.currentframe() - calframe = inspect.getouterframes(curframe, 2) - caller = calframe[1][3] - print(caller + '(): ' + msg) - return None - -def debug(msg = ""): - """print a conditional debug message""" - if DEBUG: - curframe = inspect.currentframe() - calframe = inspect.getouterframes(curframe, 2) - caller = calframe[1][3] - print('DEBUG: ' + caller + '(): ' + msg) - return None - - -def error(msg = ""): - """Print an error message""" - curframe = inspect.currentframe() - calframe = inspect.getouterframes(curframe, 2) - caller = calframe[1][3] - print('ERROR: ' + caller + '(): ' + msg) - - return None - ### main() if __name__ == "__main__": diff --git a/tanks.json b/tanks.json index bd9e7d2..815564a 100644 --- a/tanks.json +++ b/tanks.json @@ -2942,363 +2942,363 @@ } }, "userStr": { - "T-34": "T-34", - "SU-85": "SU-85", - "IS": "IS", - "BT-7": "BT-7", - "BT-2": "BT-2", - "T-28": "T-28", "A-20": "A-20", - "SU-152": "SU-152", - "T-34-85": "T-34-85", - "KV-1s": "KV-1S", - "T-46": "T-46", - "MS-1": "MS-1", - "SU-100": "SU-100", - "T-44": "T-44", - "T-26": "T-26", - "AT-1": "AT-1", - "IS-3": "IS-3", - "KV-3": "KV-3", - "IS-4": "IS-4", - "SU-76": "SU-76", - "T-43": "T-43", - "GAZ-74b": "SU-85B", - "IS-7": "IS-7", - "ISU-152": "ISU-152", - "T-54": "T-54", - "Object_704": "Object 704", - "KV-13": "KV-13", - "IS-6": "IS-6", - "SU-101": "SU-101", - "SU100M1": "SU-100M1", - "KV2": "KV-2", - "ST_I": "ST-I", - "KV4": "KV-4", - "T150": "T-150", - "IS8": "IS-8", - "KV1": "KV-1", - "SU122_54": "SU-122-54", - "R127_T44_100_P": "T-44-100", - "Object268": "Object 268", - "T62A": "T-62A", - "Object263": "Object 263", - "MT25": "MT-25", - "Object_140": "Object 140", - "R109_T54S": "T-54 ltwt.", - "R107_LTB": "LTTB", - "KV-220_test": "KV-220 Beta-Test", - "Matilda_II_LL": "Matilda IV", - "Churchill_LL": "Churchill III", - "BT-SV": "BT-SV", - "Valentine_LL": "Valentine II", - "M3_Stuart_LL": "M3 Light", "A-32": "A-32", - "KV-5": "KV-5", - "T-127": "T-127", - "SU_85I": "SU-85I", - "SU76I": "SU-76I", - "Tetrarch_LL": "Tetrarch", - "SU100Y": "SU-100Y", - "SU122_44": "SU-122-44", - "LTP": "LTP", - "R113_Object_730": "IS-5 (Object 730)", - "R71_IS_2B": "IS-2 (1945)", - "R116_ISU122C_Berlin": "ISU-122S", - "R112_T54_45": "T-54 first prototype", - "IS-2Sh": "IS-2Sh", - "R115_IS-3_auto": "IS-3 Defender", - "R105_BT_7A": "BT-7 artillery", - "T-34-85_Rudy": "T-34-85 Rudy", - "T-34-85_Victory": "T-34-85 Victory", - "IS-6_Fearless": "IS-6 Fearless", - "K-91": "K-91", - "R43_T-70_57": "T-70/57", - "R111_ISU130": "ISU-130", - "PzIV": "Pz.Kpfw. IV", - "PzVI": "Tiger I", - "Pz35t": "Pz.Kpfw. 35 (t)", - "StuGIII": "StuG III Ausf. G", - "PzV": "Panther I", - "JagdPzIV": "Jagdpanzer IV", - "Hetzer": "Hetzer", - "PzII": "Pz.Kpfw. II", - "VK3601H": "VK 36.01 (H)", - "VK3001H": "VK 30.01 (H)", - "Ltraktor": "Leichttraktor", - "Pz38t": "Pz.Kpfw. 38 (t)", - "PanzerJager_I": "Panzerjäger I", - "JagdPanther": "Jagdpanther", - "VK3002DB": "VK 30.02 (D)", - "PzIII": "Pz.Kpfw. III", - "PzIII_A": "Pz.Kpfw. III Ausf. A", - "PzVIB_Tiger_II": "Tiger II", - "VK1602": "VK 16.02 Leopard", - "PzII_Luchs": "Pz.Kpfw. II Luchs", - "PzIII_IV": "Pz.Kpfw. III/IV", - "G20_Marder_II": "Marder II", - "Maus": "Maus", - "VK3001P": "VK 30.01 (P)", - "VK4502P": "VK 45.02 (P) Ausf. B", - "Ferdinand": "Ferdinand", - "JagdTiger": "Jagdtiger", - "Pz38_NA": "Pz.Kpfw. 38 (t) n.A.", - "Panther_II": "Panther II", - "E-100": "E 100", - "E-75": "E 75", - "VK2801": "VK 28.01", - "E-50": "E 50", - "VK4502A": "VK 45.02 (P) Ausf. A", - "PzVI_Tiger_P": "Tiger (P)", - "Sturer_Emil": "Sturer Emil", - "JagdPantherII": "Jagdpanther II", - "Nashorn": "Nashorn", - "JagdPz_E100": "Jagdpanzer E 100", - "E50_Ausf_M": "E 50 Ausf. M", - "Pz_II_AusfG": "Pz.Kpfw. II Ausf. G", - "DW_II": "Durchbruchswagen 2", - "Indien_Panzer": "Indien-Panzer", - "VK3002DB_V1": "VK 30.01 (D)", - "Leopard1": "Leopard 1", - "Pro_Ag_A": "Leopard Prototyp A", - "VK3002M": "VK 30.02 (M)", - "Pz_Sfl_IVc": "Pz.Sfl. IVc", - "Waffentrager_IV": "Waffenträger auf Pz. IV", - "RhB_Waffentrager": "Rhm.-Borsig Waffenträger", - "Pz_IV_AusfA": "Pz.Kpfw. IV Ausf. A", - "Pz_IV_AusfD": "Pz.Kpfw. IV Ausf. D", - "G103_RU_251": "Spähpanzer Ru 251", - "G113_SP_I_C": "Spähpanzer SP I C", - "G121_Grille_15_L63": "Grille 15", - "PzV_PzIV": "Pz.Kpfw. V/IV", - "PzII_J": "Pz.Kpfw. II Ausf. J", - "S35_captured": "Pz.Kpfw. S35 739 (f)", - "B-1bis_captured": "Pz.Kpfw. B2 740 (f)", - "H39_captured": "Pz.Kpfw. 38H 735 (f)", - "Lowe": "Löwe", - "T-25": "T-25", - "T-15": "T-15", - "PzIV_Hydro": "Pz.Kpfw. IV hydrostat.", - "JagdTiger_SdKfz_185": "8,8 cm Pak 43 Jagdtiger", - "DickerMax": "Dicker Max", - "PzIV_schmalturm": "Pz.Kpfw. IV Schmalturm", - "Panther_M10": "Panther/M10", - "VK7201": "VK 72.01 (K)", - "G100_Gtraktor_Krupp": "Großtraktor - Krupp", - "G106_PzKpfwPanther_AusfF": "Panther mit 8,8 cm L/71", - "G120_M41_90": "leKpz M 41 90 mm", - "G118_VK4503": "VK 45.03", - "S01_Frankentank": "Tankenstein", - "JagdTiger_SdKfz_185_Snowstorm": "Snowstorm Jagdtiger 8.8", - "E-25": "E 25", - "G_KpfPz_70": "KpfPz 70", - "Krupp-38D": "Krupp-38(D)", - "G114_Rheinmetall_Scorpion": "Rheinmetall Skorpion G", - "G115_VK10001P": "VK 100.01 (P)", - "T14": "T14", - "M3_Stuart": "M3 Stuart", - "T1_Cunningham": "T1 Cunningham", - "M6": "M6", - "M4_Sherman": "M4 Sherman", - "M4A3E8_Sherman": "M4A3E8 Sherman", - "T20": "T20", - "M2_lt": "M2 Light Tank", - "T30": "T30", - "T34_hvy": "T34", - "M3_Grant": "M3 Lee", - "T1_hvy": "T1 Heavy Tank", - "T29": "T29", - "T32": "T32", - "M2_med": "M2 Medium Tank", - "M5_Stuart": "M5 Stuart", - "M7_med": "M7", - "T2_med": "T2 Medium Tank", - "Pershing": "M26 Pershing", - "T18": "T18", - "T82": "T82", - "M10_Wolverine": "M10 Wolverine", - "M36_Slagger": "M36 Jackson", - "T40": "T40", - "T28": "T28", - "T95": "T95", - "M46_Patton": "M46 Patton", - "T25_AT": "T25 AT", - "M103": "M103", - "M24_Chaffee": "M24 Chaffee", - "Sherman_Jumbo": "M4A3E2 Sherman Jumbo", - "M8A1": "M8A1", - "T49": "T67", - "T110": "T110E5", - "T25_2": "T25/2", - "T28_Prototype": "T28 Prototype", - "M18_Hellcat": "M18 Hellcat", - "T23E3": "T23E3", - "T110E4": "T110E4", - "T26_E4_SuperPershing": "T26E4 SuperPershing", - "T110E3": "T110E3", - "M48A1": "M48A1 Patton", - "T69": "T69", - "T57_58": "T57 Heavy Tank", - "T21": "T21", - "T54E1": "T54E1", - "T71": "T71", - "M60": "M60", - "T37": "T37", - "M41_Bulldog": "M41 Walker Bulldog", "A100_T49": "T49", - "T2_lt": "T2 Light Tank", - "Ram-II": "Ram II", - "M4A2E4": "M4A2E4 Sherman", - "M6A2E1": "M6A2E1", - "M22_Locust": "M22 Locust", - "M6A2E1_exp": "M6A2E1 EXP", - "T1_E6": "T1E6", - "T7_Combat_Car": "T7 Combat Car", - "A104_M4A3E8A": "M4A3E8 Fury", "A102_T28_concept": "T28 Concept", - "T34_hvy_Freedom": "T34 Independence", - "T49_Rocket": "T49 ATM", - "T95E2": "T95E2", + "A104_M4A3E8A": "M4A3E8 Fury", "A77_T6M": "T6E1", + "AMX38": "AMX 38", + "AMX40": "AMX 40", + "AMX50_Foch": "AMX 50 Foch", + "AMX_12t": "AMX 12 t", + "AMX_13_75": "AMX 13 75", + "AMX_13_90": "AMX 13 90", + "AMX_50Fosh_155": "AMX 50 Foch (155)", + "AMX_50_100": "AMX 50 100", + "AMX_50_120": "AMX 50 120", + "AMX_50_68t": "AMX 50 B", + "AMX_AC_Mle1946": "AMX AC mle. 46", + "AMX_AC_Mle1948": "AMX AC mle. 48", + "AMX_M4_1945": "AMX M4 mle. 45", + "AMX_dracula": "T6 Dracula", + "ARL_44": "ARL 44", + "ARL_V39": "ARL V39", + "AT-1": "AT-1", + "Akawara": "O-47", + "B-1bis_captured": "Pz.Kpfw. B2 740 (f)", + "B1": "B1", + "BDR_G1B": "BDR G1 B", + "BT-2": "BT-2", + "BT-7": "BT-7", + "BT-SV": "BT-SV", + "Bat_Chatillon25t": "Bat.-Châtillon 25 t", + "CH38_WZ113G-FT": "WZ-113G FT", "Ch01_Type59": "Type 59", + "Ch02_Type62": "Type 62", "Ch03_WZ-111": "WZ-111", "Ch04_T34_1": "T-34-1", - "Ch06_Renault_NC31": "Renault NC-31", "Ch05_T34_2": "T-34-2", - "Ch18_WZ-120": "WZ-120", - "Ch12_111_1_2_3": "WZ-111 model 1-4", + "Ch06_Renault_NC31": "Renault NC-31", "Ch07_Vickers_MkE_Type_BT26": "Vickers Mk. E Type B", - "Ch11_110": "WZ-110", + "Ch08_Type97_Chi_Ha": "Type 2597 Chi-Ha", "Ch09_M5": "M5A1 Stuart", "Ch10_IS2": "IS-2", + "Ch11_110": "WZ-110", + "Ch12_111_1_2_3": "WZ-111 model 1-4", + "Ch14_T34_3": "T-34-3", + "Ch18_WZ-120": "WZ-120", "Ch19_121": "WZ-121", - "Ch08_Type97_Chi_Ha": "Type 2597 Chi-Ha", - "Ch21_T34": "Type T-34", "Ch20_Type58": "Type 58", + "Ch21_T34": "Type T-34", "Ch22_113": "WZ-113", "Ch23_112": "112 Glacial", - "Ch02_Type62": "Type 62", "Ch24_Type64": "Type 64", "Ch25_121_mod_1971B": "121B", "Ch26_59_Patton": "59-Patton", - "Ch14_T34_3": "T-34-3", - "CH38_WZ113G-FT": "WZ-113G FT", "Ch39_WZ120_1G_FT": "WZ-120-1G FT", - "D2": "D2", - "RenaultFT": "Renault FT", - "B1": "B1", + "Chi_Ha": "Type 97 Chi-Ha", + "Chi_He": "Type 1 Chi-He", + "Chi_Ni": "Chi-Ni", + "Chi_Nu": "Type 3 Chi-Nu", + "Chi_Nu_Kai": "Type 3 Chi-Nu Kai", + "Chi_Ri": "Type 5 Chi-Ri", + "Chi_To": "Type 4 Chi-To", + "Churchill_LL": "Churchill III", "D1": "D1", - "FCM_36Pak40": "FCM 36 Pak 40", - "ARL_44": "ARL 44", - "AMX40": "AMX 40", - "AMX_50_100": "AMX 50 100", - "Bat_Chatillon25t": "Bat.-Châtillon 25 t", - "AMX_50_120": "AMX 50 120", - "AMX_13_90": "AMX 13 90", - "AMX_13_75": "AMX 13 75", - "AMX38": "AMX 38", - "AMX_50_68t": "AMX 50 B", - "AMX_12t": "AMX 12 t", - "BDR_G1B": "BDR G1 B", - "AMX_M4_1945": "AMX M4 mle. 45", - "RenaultFT_AC": "Renault FT AC", - "RenaultUE57": "Renault UE 57", - "Somua_Sau_40": "Somua SAu 40", - "S_35CA": "S35 CA", - "AMX_AC_Mle1946": "AMX AC mle. 46", - "AMX50_Foch": "AMX 50 Foch", - "ARL_V39": "ARL V39", - "AMX_AC_Mle1948": "AMX AC mle. 48", - "AMX_50Fosh_155": "AMX 50 Foch (155)", + "D2": "D2", + "DW_II": "Durchbruchswagen 2", + "DickerMax": "Dicker Max", + "E-100": "E 100", + "E-25": "E 25", + "E-50": "E 50", + "E-75": "E 75", + "E50_Ausf_M": "E 50 Ausf. M", "ELC_AMX": "AMX ELC bis", - "F68_AMX_Chasseur_de_char_46": "AMX Chasseur de chars", - "FCM_50t": "FCM 50 t", - "F75_Char_de_25t": "Bat.-Châtillon 25 t AP", - "F74_AMX_M4_1949": "AMX M4 mle. 49", - "RenaultR35": "Renault R35", "F19_Lorraine40t": "Lorraine 40 t", + "F68_AMX_Chasseur_de_char_46": "AMX Chasseur de chars", + "F69_AMX_13_57GF": "AMX 13 57 GF", "F71_AMX_30_prototype": "AMX 30 1er prototype", "F72_AMX_30": "AMX 30 B", "F73_M4A1_Revalorise": "M4A1 Revalorisé", - "F69_AMX_13_57GF": "AMX 13 57 GF", + "F74_AMX_M4_1949": "AMX M4 mle. 49", + "F75_Char_de_25t": "Bat.-Châtillon 25 t AP", + "FCM_36Pak40": "FCM 36 Pak 40", + "FCM_50t": "FCM 50 t", + "Ferdinand": "Ferdinand", + "G100_Gtraktor_Krupp": "Großtraktor - Krupp", + "G103_RU_251": "Spähpanzer Ru 251", + "G106_PzKpfwPanther_AusfF": "Panther mit 8,8 cm L/71", + "G113_SP_I_C": "Spähpanzer SP I C", + "G114_Rheinmetall_Scorpion": "Rheinmetall Skorpion G", + "G115_VK10001P": "VK 100.01 (P)", + "G118_VK4503": "VK 45.03", + "G120_M41_90": "leKpz M 41 90 mm", + "G121_Grille_15_L63": "Grille 15", + "G20_Marder_II": "Marder II", + "GAZ-74b": "SU-85B", "GB01_Medium_Mark_I": "Vickers Medium Mk. I", + "GB03_Cruiser_Mk_I": "Cruiser Mk. I", + "GB04_Valentine": "Valentine", "GB05_Vickers_Medium_Mk_II": "Vickers Medium Mk. II", - "GB07_Matilda": "Matilda", - "GB21_Cromwell": "Cromwell", - "GB20_Crusader": "Crusader", "GB06_Vickers_Medium_Mk_III": "Vickers Medium Mk. III", + "GB07_Matilda": "Matilda", "GB08_Churchill_I": "Churchill I", + "GB09_Churchill_VII": "Churchill VII", "GB10_Black_Prince": "Black Prince", "GB11_Caernarvon": "Caernarvon", "GB12_Conqueror": "Conqueror", - "GB09_Churchill_VII": "Churchill VII", - "GB04_Valentine": "Valentine", - "GB03_Cruiser_Mk_I": "Cruiser Mk. I", - "GB22_Comet": "Comet", - "GB24_Centurion_Mk3": "Centurion Mk. 7/1", - "GB23_Centurion": "Centurion Mk. I", "GB13_FV215b": "FV215b", + "GB19_Sherman_Firefly": "Sherman Firefly", + "GB20_Crusader": "Crusader", + "GB21_Cromwell": "Cromwell", + "GB22_Comet": "Comet", + "GB23_Centurion": "Centurion Mk. I", + "GB24_Centurion_Mk3": "Centurion Mk. 7/1", + "GB32_Tortoise": "Tortoise", + "GB35_Sentinel_AC_IV": "AC IV Sentinel", + "GB39_Universal_CarrierQF2": "Universal Carrier 2-pdr", + "GB40_Gun_Carrier_Churchill": "Churchill Gun Carrier", + "GB42_Valentine_AT": "Valentine AT", + "GB44_Archer_Custom": "Angry Connor", + "GB48_FV215b_183": "FV215b (183)", + "GB51_Excelsior": "Excelsior", + "GB52_FV201_A45": "FV201 (A45)", + "GB57_Alecto": "Alecto", + "GB58_Cruiser_Mk_III": "Cruiser Mk. III", + "GB59_Cruiser_Mk_IV": "Cruiser Mk. IV", "GB60_Covenanter": "Covenanter", + "GB63_TOG_II": "TOG II*", + "GB68_Matilda_Black_Prince": "Matilda Black Prince", "GB69_Cruiser_Mk_II": "Cruiser Mk. II", "GB70_FV4202_105": "FV4202", - "GB59_Cruiser_Mk_IV": "Cruiser Mk. IV", - "GB58_Cruiser_Mk_III": "Cruiser Mk. III", - "GB42_Valentine_AT": "Valentine AT", - "GB39_Universal_CarrierQF2": "Universal Carrier 2-pdr", + "GB71_AT_15A": "AT 15A", "GB72_AT15": "AT 15", "GB73_AT2": "AT 2", - "GB57_Alecto": "Alecto", - "GB48_FV215b_183": "FV215b (183)", "GB74_AT8": "AT 8", - "GB40_Gun_Carrier_Churchill": "Churchill Gun Carrier", "GB75_AT7": "AT 7", - "GB87_Chieftain_T95_turret": "Chieftain/T95", - "GB32_Tortoise": "Tortoise", - "GB68_Matilda_Black_Prince": "Matilda Black Prince", - "GB63_TOG_II": "TOG II*", - "GB71_AT_15A": "AT 15A", - "GB51_Excelsior": "Excelsior", "GB76_Mk_VIC": "Light Mk. VIC", - "GB85_Cromwell_Berlin": "Cromwell B", - "GB_Mark_I": "Mk I* Heavy Tank", - "GB35_Sentinel_AC_IV": "AC IV Sentinel", - "GB44_Archer_Custom": "Angry Connor", - "Sentinel_AC1": "AC 1 Sentinel", - "GB19_Sherman_Firefly": "Sherman Firefly", - "GB_Churchill_MkVI": "Churchill Mk. VI", - "GB_Sherman_V": "Sherman V", "GB84_Chieftain_Mk6": "Chieftain Mk.6", - "GB52_FV201_A45": "FV201 (A45)", - "Chi_Ni": "Chi-Ni", - "NC27": "Renault Otsu", + "GB85_Cromwell_Berlin": "Cromwell B", + "GB87_Chieftain_T95_turret": "Chieftain/T95", + "GB_Churchill_MkVI": "Churchill Mk. VI", + "GB_Mark_I": "Mk I* Heavy Tank", + "GB_Sherman_V": "Sherman V", + "G_KpfPz_70": "KpfPz 70", + "H39_captured": "Pz.Kpfw. 38H 735 (f)", "Ha_Go": "Type 95 Ha-Go", - "Chi_Ri": "Type 5 Chi-Ri", - "Chi_Nu": "Type 3 Chi-Nu", - "Chi_He": "Type 1 Chi-He", - "Chi_To": "Type 4 Chi-To", - "Chi_Ha": "Type 97 Chi-Ha", - "Ke_Ni": "Type 98 Ke-Ni", - "STA_1": "STA-1", - "Ke_Ho": "Type 5 Ke-Ho", - "Type_61": "Type 61", - "ST_B1": "STB-1", - "Ke_Ni_B": "Type 98 Ke-Ni Otsu", - "PzVI_GuP": "Kuro Mori Mine", - "Pz_IV_AusfH": "Panzer IV Anko Special", - "Chi_Nu_Kai": "Type 3 Chi-Nu Kai", + "Hetzer": "Hetzer", "Hetzer_GuP": "Hetzer Kame SP", - "Strv_74A2": "Stridsvagn 74A2", - "Sega_Edelweiss": "Edelweiss", - "Sega_Nameless": "Nameless", - "Van_Helsing": "Helsing H0", - "Akawara": "O-47", - "WZ-135G_FT": "WZ 135G FT Blaze", - "AMX_dracula": "T6 Dracula", - "Sega_Lupus": "Lupus", + "IS": "IS", + "IS-2Sh": "IS-2Sh", + "IS-3": "IS-3", + "IS-4": "IS-4", + "IS-6": "IS-6", + "IS-6_Fearless": "IS-6 Fearless", + "IS-7": "IS-7", + "IS8": "IS-8", + "ISU-152": "ISU-152", + "Indien_Panzer": "Indien-Panzer", + "JagdPanther": "Jagdpanther", + "JagdPantherII": "Jagdpanther II", + "JagdPzIV": "Jagdpanzer IV", + "JagdPz_E100": "Jagdpanzer E 100", + "JagdTiger": "Jagdtiger", + "JagdTiger_SdKfz_185": "8,8 cm Pak 43 Jagdtiger", + "JagdTiger_SdKfz_185_Snowstorm": "Snowstorm Jagdtiger 8.8", + "K-91": "K-91", + "KV-13": "KV-13", + "KV-1s": "KV-1S", + "KV-220_test": "KV-220 Beta-Test", + "KV-3": "KV-3", + "KV-5": "KV-5", + "KV1": "KV-1", + "KV2": "KV-2", + "KV4": "KV-4", + "Ke_Ho": "Type 5 Ke-Ho", + "Ke_Ni": "Type 98 Ke-Ni", + "Ke_Ni_B": "Type 98 Ke-Ni Otsu", + "Krupp-38D": "Krupp-38(D)", + "LTP": "LTP", + "Leopard1": "Leopard 1", + "Lowe": "Löwe", + "Ltraktor": "Leichttraktor", + "M103": "M103", + "M10_Wolverine": "M10 Wolverine", + "M18_Hellcat": "M18 Hellcat", + "M22_Locust": "M22 Locust", + "M24_Chaffee": "M24 Chaffee", + "M2_lt": "M2 Light Tank", + "M2_med": "M2 Medium Tank", + "M36_Slagger": "M36 Jackson", + "M3_Grant": "M3 Lee", + "M3_Stuart": "M3 Stuart", + "M3_Stuart_LL": "M3 Light", + "M41_Bulldog": "M41 Walker Bulldog", + "M46_Patton": "M46 Patton", + "M48A1": "M48A1 Patton", + "M4A2E4": "M4A2E4 Sherman", + "M4A3E8_Sherman": "M4A3E8 Sherman", + "M4_Sherman": "M4 Sherman", + "M5_Stuart": "M5 Stuart", + "M6": "M6", + "M60": "M60", + "M6A2E1": "M6A2E1", + "M6A2E1_exp": "M6A2E1 EXP", + "M7_med": "M7", + "M8A1": "M8A1", + "MS-1": "MS-1", + "MT25": "MT-25", + "Matilda_II_LL": "Matilda IV", + "Maus": "Maus", + "NC27": "Renault Otsu", + "Nashorn": "Nashorn", + "Object263": "Object 263", + "Object268": "Object 268", + "Object_140": "Object 140", + "Object_704": "Object 704", "Oth08_WH_Vindicator": "Vindicator Ultramarines", "Oth09_WH_Predator": "Predator Ultramarines", - "Oth11_T-55A": "T 55A", "Oth10_WarDuck": "WarDuck", - "Oth12_Sega_Hafen": "Hafen" + "Oth11_T-55A": "T 55A", + "Oth12_Sega_Hafen": "Hafen", + "Panther_II": "Panther II", + "Panther_M10": "Panther/M10", + "PanzerJager_I": "Panzerjäger I", + "Pershing": "M26 Pershing", + "Pro_Ag_A": "Leopard Prototyp A", + "Pz35t": "Pz.Kpfw. 35 (t)", + "Pz38_NA": "Pz.Kpfw. 38 (t) n.A.", + "Pz38t": "Pz.Kpfw. 38 (t)", + "PzII": "Pz.Kpfw. II", + "PzIII": "Pz.Kpfw. III", + "PzIII_A": "Pz.Kpfw. III Ausf. A", + "PzIII_IV": "Pz.Kpfw. III/IV", + "PzII_J": "Pz.Kpfw. II Ausf. J", + "PzII_Luchs": "Pz.Kpfw. II Luchs", + "PzIV": "Pz.Kpfw. IV", + "PzIV_Hydro": "Pz.Kpfw. IV hydrostat.", + "PzIV_schmalturm": "Pz.Kpfw. IV Schmalturm", + "PzV": "Panther I", + "PzVI": "Tiger I", + "PzVIB_Tiger_II": "Tiger II", + "PzVI_GuP": "Kuro Mori Mine", + "PzVI_Tiger_P": "Tiger (P)", + "PzV_PzIV": "Pz.Kpfw. V/IV", + "Pz_II_AusfG": "Pz.Kpfw. II Ausf. G", + "Pz_IV_AusfA": "Pz.Kpfw. IV Ausf. A", + "Pz_IV_AusfD": "Pz.Kpfw. IV Ausf. D", + "Pz_IV_AusfH": "Panzer IV Anko Special", + "Pz_Sfl_IVc": "Pz.Sfl. IVc", + "R105_BT_7A": "BT-7 artillery", + "R107_LTB": "LTTB", + "R109_T54S": "T-54 ltwt.", + "R111_ISU130": "ISU-130", + "R112_T54_45": "T-54 first prototype", + "R113_Object_730": "IS-5 (Object 730)", + "R115_IS-3_auto": "IS-3 Defender", + "R116_ISU122C_Berlin": "ISU-122S", + "R127_T44_100_P": "T-44-100", + "R43_T-70_57": "T-70/57", + "R71_IS_2B": "IS-2 (1945)", + "Ram-II": "Ram II", + "RenaultFT": "Renault FT", + "RenaultFT_AC": "Renault FT AC", + "RenaultR35": "Renault R35", + "RenaultUE57": "Renault UE 57", + "RhB_Waffentrager": "Rhm.-Borsig Waffenträger", + "S01_Frankentank": "Tankenstein", + "S35_captured": "Pz.Kpfw. S35 739 (f)", + "STA_1": "STA-1", + "ST_B1": "STB-1", + "ST_I": "ST-I", + "SU-100": "SU-100", + "SU-101": "SU-101", + "SU-152": "SU-152", + "SU-76": "SU-76", + "SU-85": "SU-85", + "SU100M1": "SU-100M1", + "SU100Y": "SU-100Y", + "SU122_44": "SU-122-44", + "SU122_54": "SU-122-54", + "SU76I": "SU-76I", + "SU_85I": "SU-85I", + "S_35CA": "S35 CA", + "Sega_Edelweiss": "Edelweiss", + "Sega_Lupus": "Lupus", + "Sega_Nameless": "Nameless", + "Sentinel_AC1": "AC 1 Sentinel", + "Sherman_Jumbo": "M4A3E2 Sherman Jumbo", + "Somua_Sau_40": "Somua SAu 40", + "Strv_74A2": "Stridsvagn 74A2", + "StuGIII": "StuG III Ausf. G", + "Sturer_Emil": "Sturer Emil", + "T-127": "T-127", + "T-15": "T-15", + "T-25": "T-25", + "T-26": "T-26", + "T-28": "T-28", + "T-34": "T-34", + "T-34-85": "T-34-85", + "T-34-85_Rudy": "T-34-85 Rudy", + "T-34-85_Victory": "T-34-85 Victory", + "T-43": "T-43", + "T-44": "T-44", + "T-46": "T-46", + "T-54": "T-54", + "T110": "T110E5", + "T110E3": "T110E3", + "T110E4": "T110E4", + "T14": "T14", + "T150": "T-150", + "T18": "T18", + "T1_Cunningham": "T1 Cunningham", + "T1_E6": "T1E6", + "T1_hvy": "T1 Heavy Tank", + "T20": "T20", + "T21": "T21", + "T23E3": "T23E3", + "T25_2": "T25/2", + "T25_AT": "T25 AT", + "T26_E4_SuperPershing": "T26E4 SuperPershing", + "T28": "T28", + "T28_Prototype": "T28 Prototype", + "T29": "T29", + "T2_lt": "T2 Light Tank", + "T2_med": "T2 Medium Tank", + "T30": "T30", + "T32": "T32", + "T34_hvy": "T34", + "T34_hvy_Freedom": "T34 Independence", + "T37": "T37", + "T40": "T40", + "T49": "T67", + "T49_Rocket": "T49 ATM", + "T54E1": "T54E1", + "T57_58": "T57 Heavy Tank", + "T62A": "T-62A", + "T69": "T69", + "T71": "T71", + "T7_Combat_Car": "T7 Combat Car", + "T82": "T82", + "T95": "T95", + "T95E2": "T95E2", + "Tetrarch_LL": "Tetrarch", + "Type_61": "Type 61", + "VK1602": "VK 16.02 Leopard", + "VK2801": "VK 28.01", + "VK3001H": "VK 30.01 (H)", + "VK3001P": "VK 30.01 (P)", + "VK3002DB": "VK 30.02 (D)", + "VK3002DB_V1": "VK 30.01 (D)", + "VK3002M": "VK 30.02 (M)", + "VK3601H": "VK 36.01 (H)", + "VK4502A": "VK 45.02 (P) Ausf. A", + "VK4502P": "VK 45.02 (P) Ausf. B", + "VK7201": "VK 72.01 (K)", + "Valentine_LL": "Valentine II", + "Van_Helsing": "Helsing H0", + "WZ-135G_FT": "WZ 135G FT Blaze", + "Waffentrager_IV": "Waffenträger auf Pz. IV" } } \ No newline at end of file diff --git a/wotbreplay_uploader.py b/wotbreplay_uploader.py index 0879444..b57f28e 100644 --- a/wotbreplay_uploader.py +++ b/wotbreplay_uploader.py @@ -1,6 +1,7 @@ #!/usr/bin/python3.7 import json, argparse, inspect, sys, os, base64, aiohttp, urllib, asyncio, aiofiles, aioconsole, logging, re +import blitzutils as bu from blitzutils import WG logging.getLogger("asyncio").setLevel(logging.DEBUG) @@ -21,7 +22,7 @@ wg = None async def main(argv): - global VERBOSE, DEBUG, wg + global wg parser = argparse.ArgumentParser(description='Post replays(s) to WoTinspector.com and retrieve battle data') parser.add_argument('--output', default='single', choices=['file', 'files', 'db'] , help='Select output mode: single/multiple files or database') @@ -30,21 +31,22 @@ async def main(argv): parser.add_argument('-t','--title', type=str, default=None, help='Title for replays. Use NN for continous numbering. Default is filename-based numbering') parser.add_argument('-p', '--private', dest="private", action='store_true', default=False, help='Set replays private on WoTinspector.com') parser.add_argument('--tasks', dest='N_tasks', type=int, default=10, help='Number of worker threads') - parser.add_argument('--tankopedia', type=str, default=None, help='Open Tankopedia') + parser.add_argument('--tankopedia', type=str, default=None, help='JSON file to read Tankopedia from') + parser.add_argument('--mapfile', type=str, default=None, help='JSON file to read Blitz map names from') parser.add_argument('-d', '--debug', action='store_true', default=False, help='Debug mode') parser.add_argument('-v', '--verbose', action='store_true', default=False, help='Verbose mode') parser.add_argument('files', metavar='FILE1 [FILE2 ...]', type=str, nargs='+', help='Files to read. Use \'-\' for STDIN"') args = parser.parse_args() - VERBOSE = args.verbose - DEBUG = args.debug - if DEBUG: VERBOSE = True - - wg = WG(WG_appID, args.tankopedia) + print(args.files) + bu.setVerbose(args.verbose) + bu.setDebug(args.debug) + + wg = WG(WG_appID, args.tankopedia, args.mapfile) if args.account != None: args.accountID = await wg.getAccountID(args.account) - debug('WG account_id: ' + str(args.accountID)) + bu.debug('WG account_id: ' + str(args.accountID)) if args.accountID == None: args.accountID = 0 @@ -58,18 +60,18 @@ async def main(argv): # Start tasks to process the Queue for i in range(args.N_tasks): tasks.append(asyncio.create_task(replayWorker(queue, i, args.accountID, args.private))) - debug('Task ' + str(i) + ' started') + bu.debug('Task ' + str(i) + ' started') - debug('Waiting for the replay scanner to finish') + bu.debug('Waiting for the replay scanner to finish') await asyncio.wait([tasks[0]]) - debug('Scanner finished. Waiting for workers to finish queue') + bu.debug('Scanner finished. Waiting for workers to finish queue') await queue.join() - debug('Cancelling workers') + bu.debug('Cancelling workers') for task in tasks: task.cancel() - debug('Waiting for workers to cancel') + bu.debug('Waiting for workers to cancel') await asyncio.gather(*tasks, return_exceptions=True) - verbose(str(REPLAY_N) + ' replays: ' + str(REPLAY_N - SKIPPED_N) + ' uploaded, ' + str(SKIPPED_N) + ' skipped') + bu.verbose(str(REPLAY_N) + ' replays: ' + str(REPLAY_N - SKIPPED_N) + ' uploaded, ' + str(SKIPPED_N) + ' skipped') except KeyboardInterrupt: print('Ctrl-c pressed ...') @@ -85,31 +87,27 @@ async def mkReplayQ(queue : asyncio.Queue, files : list, title : str): """Create queue of replays to post""" p_replayfile = re.compile('.*\\.wotbreplay$') if files[0] == '-': - debug('reading replay file list from STDIN') + bu.debug('reading replay file list from STDIN') stdin, _ = await aioconsole.get_standard_streams() - #stdin = sys.stdin while True: - line = await stdin.readline() + line = (await stdin.readline()).decode('utf-8').rstrip() if not line: break else: if (p_replayfile.match(line) != None): await queue.put(await mkQueueItem(line, title)) else: - # debug('Reading files from the command line: ') - # debug(', '.join(files)) for fn in files: - # debug(fn) if os.path.isfile(fn) and (p_replayfile.match(fn) != None): await queue.put(await mkQueueItem(fn, title)) elif os.path.isdir(fn): with os.scandir(fn) as dirEntry: for entry in dirEntry: if entry.is_file() and (p_replayfile.match(entry.name) != None): - debug(entry.name) + bu.debug(entry.name) await queue.put(await mkQueueItem(entry.path, title)) - debug('File added to queue: ' + fn) - debug('Finished') + bu.debug('File added to queue: ' + fn) + bu.debug('Finished') return None @@ -119,7 +117,6 @@ async def mkQueueItem(filename : str, title : str) -> list: REPLAY_N +=1 return [filename, REPLAY_N, getTitle(filename, title, REPLAY_N)] - async def replayWorker(queue: asyncio.Queue, workerID: int, account_id: int, priv = False): """Async Worker to process the replay queue""" global SKIPPED_N @@ -138,12 +135,12 @@ async def replayWorker(queue: asyncio.Queue, workerID: int, account_id: int, pri async with aiofiles.open(replay_json_fn) as fp: replay_json = json.loads(await fp.read()) if replay_json['status'] == 'ok': - verbose('Replay[' + str(N) + ']: ' + title + ' has already been posted. Skipping.' ) + bu.verbose('Replay[' + str(N) + ']: ' + title + ' has already been posted. Skipping.' ) queue.task_done() SKIPPED_N += 1 continue except Exception as err: - error(err) + bu.error(err) sys.exit(1) # debug('Opening file [' + str(N) +']: ' + filename) @@ -158,49 +155,43 @@ async def replayWorker(queue: asyncio.Queue, workerID: int, account_id: int, pri } url = WIurl + urllib.parse.urlencode(params, quote_via=urllib.parse.quote) headers ={'Content-type': 'application/x-www-form-urlencoded'} - # debug('Reading the payload') payload = { 'file' : (filename, base64.b64encode(await fp.read())) } - # debug('Payload read') - # debug('URL: ' + url) for retry in range(MAX_RETRIES): - # verbose('Worker ' + str(workerID) + ': Posting replay[' + str(N) +']: ' + title + ' Try #: ' + str(retry+ 1) + '/' + str(MAX_RETRIES) ) - verbose('Posting replay[' + str(N) +']: ' + title + ' Try #: ' + str(retry+ 1) + '/' + str(MAX_RETRIES) ) + bu.verbose('Posting replay[' + str(N) +']: ' + title + ' Try #: ' + str(retry+ 1) + '/' + str(MAX_RETRIES) ) try: async with session.post(url, headers=headers, data=payload) as resp: - debug('Replay #' + str(N) + ' HTTP response: '+ str(resp.status)) + bu.debug('Replay #' + str(N) + ' HTTP response: '+ str(resp.status)) if resp.status == 200: - debug('HTTP POST 200 = Success. Reading response data') + bu.debug('HTTP POST 200 = Success. Reading response data') json_resp = await resp.json() if json_resp['status'] == 'error': - error(json_resp['error']['message'] + ' : Replay[' + str(N) + ']' + title) + bu.error(json_resp['error']['message'] + ' : Replay[' + str(N) + ']' + title) else: - debug('Response data read') - verbose('Replay[' + str(N) + ']: ' + title + ' posted') + bu.debug('Response data read') + bu.verbose('Replay[' + str(N) + ']: ' + title + ' posted') await storeReplayJSON(replay_json_fn,json_resp) break else: - debug('Replay[' + str(N)+ ']: Got HTTP/' + str(resp.status) + ' Retrying... ' + str(retry)) + bu.debug('Replay[' + str(N)+ ']: Got HTTP/' + str(resp.status) + ' Retrying... ' + str(retry)) await asyncio.sleep(SLEEP) except Exception as err: - error(str(err)) + bu.error(str(err)) await asyncio.sleep(SLEEP) - debug('Marking task ' + str(N) + ' done') + bu.debug('Marking task ' + str(N) + ' done') queue.task_done() return None - async def storeReplayJSON(filename: str, json_data : dict): try: async with aiofiles.open(filename, 'w', encoding='utf8') as outfile: await outfile.write(json.dumps(json_data, ensure_ascii=False, indent=4)) return True except Exception as err: - error(str(err)) + bu.error(str(err)) return False return False - def getTitle(replayfile: str, title: str, i : int) -> str: global wg @@ -212,52 +203,21 @@ def getTitle(replayfile: str, title: str, i : int) -> str: p = re.compile('\\d{8}_\\d{4}_(.+)_(' + '|'.join(map_usrStrs) + ')(?:-\\d)?\\.wotbreplay$') m = p.match(filename) if (m != None): - # debug('Match') if wg.tanks != None: tank = wg.tanks['userStr'][m.group(1)] - # debug('Tank from Tankopedia:' + tank) else: tank = m.group(1) - # debug(tank) blitz_map = wg.maps[m.group(2)] title = tank + ' @ ' + blitz_map else: - # debug('No match') title = re.sub('\\.wotbreplay$', '', filename) except Exception as err: - error(err) + bu.error(err) else: title.replace('NN', str(i)) - # debug(title) return title -def verbose(msg = ""): - """Print a message""" - if VERBOSE: - print(msg) - return None - - -def debug(msg = ""): - """print a conditional debug message""" - if DEBUG: - curframe = inspect.currentframe() - calframe = inspect.getouterframes(curframe, 2) - caller = calframe[1][3] - print('DEBUG: ' + caller + '(): ' + msg) - return None - - -def error(msg = ""): - """Print an error message""" - curframe = inspect.currentframe() - calframe = inspect.getouterframes(curframe, 2) - caller = calframe[1][3] - print('ERROR: ' + caller + '(): ' + msg) - - return None - -### main() +### main() ------------------------------------------- if __name__ == "__main__": asyncio.run(main(sys.argv[1:]), debug=False) \ No newline at end of file