def parse_report(args): """Reads an fflogs report and return a list of entries""" # Default values report_start_time = 0 start_time = 0 end_time = 0 enemies = {} last_ability = start_time # Get report information report_data = fflogs.api('fights', args.report, 'www', {'api_key': args.key}) report_start_time = report_data['start'] # Get the start and end timestamps for the specific fight fight_id_found = False for fight in report_data['fights']: if args.fight and fight['id'] == args.fight: start_time = fight['start_time'] end_time = fight['end_time'] fight_id_found = True break elif fight['end_time'] - fight['start_time'] > end_time - start_time: start_time = fight['start_time'] end_time = fight['end_time'] if args.fight and not fight_id_found: raise Exception('Fight ID not found in report') # Build an enemy name list, since these aren't in the events for enemy in report_data['enemies']: enemies[enemy['id']] = enemy['name'] # Get the actual event list for the single fight options = { 'api_key': args.key, 'start': start_time, 'end': end_time, 'filter': 'source.disposition="enemy" and type="cast"', 'translate': 'true', } event_data = fflogs.api('events', args.report, 'www', options) entries = [] # Actually make the entry dicts for event in event_data['events']: entry = { 'time': datetime.fromtimestamp((report_start_time + event['timestamp']) / 1000), 'combatant': enemies[event['sourceID']], 'ability_id': hex(event['ability']['guid'])[2:].upper(), 'ability_name': event['ability']['name'], } entries.append(entry) return entries, datetime.fromtimestamp((report_start_time + start_time) / 1000)
def parse_report(args): """Reads an fflogs report and return a list of entries""" # Default values report_start_time = 0 start_time = 0 end_time = 0 enemies = {} # Get report information report_data = fflogs.api("fights", args.report, "www", {"api_key": args.key}) report_start_time = report_data["start"] # Get the start and end timestamps for the specific fight fight_id_found = False for fight in report_data["fights"]: if args.fight and fight["id"] == args.fight: start_time = fight["start_time"] end_time = fight["end_time"] fight_id_found = True break elif fight["end_time"] - fight["start_time"] > end_time - start_time: start_time = fight["start_time"] end_time = fight["end_time"] if args.fight and not fight_id_found: raise Exception("Fight ID not found in report") # Build an enemy name list, since these aren't in the events for enemy in report_data["enemies"]: enemies[enemy["id"]] = enemy["name"] # Get the actual event list for the single fight options = { "api_key": args.key, "start": start_time, "end": end_time, "filter": '(source.disposition="enemy" and (type="cast" or type="begincast")) or (target.disposition="enemy" and source.disposition!="friendly" and type="applydebuff")', "translate": "true", } event_data = fflogs.api("events", args.report, "www", options) entries = [] # Actually make the entry dicts for event in event_data["events"]: entry = { "time": datetime.fromtimestamp((report_start_time + event["timestamp"]) / 1000), "ability_id": hex(event["ability"]["guid"])[2:].upper(), "ability_name": event["ability"]["name"], "type": event["type"], } # In the applydebuff case, the source is -1 (environment) and we want the target instead if event["type"] == "applydebuff": entry["combatant"] = enemies[event["targetID"]] elif "sourceID" in event: entry["combatant"] = enemies[event["sourceID"]] else: entry["combatant"] = "" entries.append(entry) return entries, datetime.fromtimestamp((report_start_time + start_time) / 1000)
def main(args): # Get overall reports and enemies. enemies = defaultdict(dict) options = { 'api_key': args.key, 'translate': 'true', } fight_id = 0 for lang in languages: report = fflogs.api('fights', args.report, prefixes[lang], options) if fight_id == 0: start_time, end_time, fight_id = find_start_end_time(report, args) for enemy in report['enemies']: # Because the overall report contains all enemies from any report, # filter them by the fight_id that we care about. for fight in enemy['fights']: if fight['id'] == fight_id: enemies[enemy['id']][lang] = enemy['name'] break # Get abilities for the chosen fight. options = { 'api_key': args.key, 'start': start_time, 'end': end_time, # filtering for disposition='enemy' drops neutral abilities like Encumber. # including death also gets you Wroth Ghosts that don't show up otherwise. 'filter': 'source.type="NPC" and (type="cast" or type="death" or type="begincast")', 'translate': 'true', } abilities = defaultdict(dict) for lang in languages: events = fflogs.api('events', args.report, prefixes[lang], options)['events'] for event in events: actor = 0 if 'targetID' in event: actor = event['targetID'] if 'ability' not in event: # Some mobs don't show up in the fight enemy list, but are things that get death events. # e.g. {'sourceIsFriendly': False, 'type': 'death', 'target': {'guid': 58, 'name': 'Wroth Ghost', 'type': 'NPC' } } if event['type'] == 'death' and not actor and 'target' in event: target = event['target'] enemies[target['guid']][lang] = target['name'] continue ability = event['ability'] abilities[(actor, ability['guid'])][lang] = ability['name'] # Finally, get boss debuff names. These aren't used directly in timeline replacement # but are helpful to have listed when writing other triggers. options = { 'api_key': args.key, 'start': start_time, 'end': end_time, 'filter': 'source.type="NPC" and (type="applydebuffstack" or type="applydebuff" or type="refreshdebuff")', 'translate': 'true', } effects = defaultdict(dict) for lang in languages: events = fflogs.api('events', args.report, prefixes[lang], options)['events'] for event in events: actor = 0 if 'targetID' in event: actor = event['targetID'] ability = event['ability'] effects[(actor, ability['guid'])][lang] = ability['name'] # Generate mappings of english => locale names. mob_replace = build_mapping(enemies) ability_replace = build_mapping(abilities, ignore_abilities) effect_replace = build_mapping(effects, ignore_effects) add_default_sync_mappings(mob_replace) add_default_ability_mappings(ability_replace) # Build some JSON similar to what cactbot expects in trigger files. timeline_replace = [] for lang in languages: if lang == default_language: continue timeline_replace.append({ 'locale': lang, 'replaceSync': dict(sorted(mob_replace[lang].items(), reverse=True)), 'replaceText': dict(sorted(ability_replace[lang].items(), reverse=True)), # sort this last <_< '~effectNames': dict(sorted(effect_replace[lang].items(), reverse=True)), }) output = {'timelineReplace': timeline_replace} output_str = json.dumps(output, ensure_ascii=False, indent=2, sort_keys=False) output_str = format_output_str(output_str) # Write that out to the user. if args.output_file: with open(args.output_file, 'w', encoding='utf-8') as fp: fp.write(output_str) else: try: print(output_str) except UnicodeEncodeError: print("Warning: unable to print to console, use --output-file", file=sys.stderr) raise
def main(args): # Get overall reports and enemies. enemies = defaultdict(dict) options = { 'api_key': args.key, 'translate': 'true', } fight_id = 0 for lang in languages: report = fflogs.api('fights', args.report, prefixes[lang], options) if fight_id == 0: start_time, end_time, fight_id = find_start_end_time(report, args) for enemy in report['enemies']: # Because the overall report contains all enemies from any report, # filter them by the fight_id that we care about. for fight in enemy['fights']: if fight['id'] == fight_id: enemies[enemy['id']][lang] = enemy['name'] break # Get abilities for the chosen fight. options = { 'api_key': args.key, 'start': start_time, 'end': end_time, # filtering for disposition='enemy' drops neutral abilities like Encumber. # including death also gets you Wroth Ghosts that don't show up otherwise. 'filter': 'source.type="NPC" and (type="cast" or type="death" or type="begincast")', 'translate': 'true', } abilities = defaultdict(dict) for lang in languages: events = fflogs.api('events', args.report, prefixes[lang], options)['events'] for event in events: actor = 0 if 'targetID' in event: actor = event['targetID'] if 'ability' not in event: # Some mobs don't show up in the fight enemy list, but are things that get death events. # e.g. {'sourceIsFriendly': False, 'type': 'death', 'target': {'guid': 58, 'name': 'Wroth Ghost', 'type': 'NPC' } } if event['type'] == 'death' and not actor and 'target' in event: target = event['target'] enemies[target['guid']][lang] = target['name'] continue ability = event['ability'] abilities[(actor, ability['guid'])][lang] = ability['name'] # Finally, get boss debuff names. These aren't used directly in timeline replacement # but are helpful to have listed when writing other triggers. options = { 'api_key': args.key, 'start': start_time, 'end': end_time, 'filter': 'source.type="NPC" and (type="applydebuffstack" or type="applydebuff" or type="refreshdebuff")', 'translate': 'true', } effects = defaultdict(dict) for lang in languages: events = fflogs.api('events', args.report, prefixes[lang], options)['events'] for event in events: actor = 0 if 'targetID' in event: actor = event['targetID'] ability = event['ability'] effects[(actor, ability['guid'])][lang] = ability['name'] # Generate mappings of english => locale names. mob_replace = build_mapping(enemies) ability_replace = build_mapping(abilities, ignore_abilities) effect_replace = build_mapping(effects, ignore_effects) add_default_sync_mappings(mob_replace) add_default_ability_mappings(ability_replace) # Build some JSON similar to what cactbot expects in trigger files. timeline_replace = [] for lang in languages: if lang == default_language: continue timeline_replace.append({ 'locale': lang, 'replaceText': ability_replace[lang], 'replaceSync': mob_replace[lang], # sort this last <_< '~effectNames': effect_replace[lang], }) output = {'timelineReplace': timeline_replace} output_str = json.dumps(output, ensure_ascii=False, indent=2, sort_keys=True) # hacky reformatting: single quotes, and remove quotes lines = [] headers = ['timelineReplace', 'locale', 'replaceSync', 'replaceText', 'locale'] for line in output_str.splitlines(): # add trailing commas line = re.sub(r"\"\s*$", "\",", line) line = re.sub(r"]\s*$", "],", line) line = re.sub(r"}\s*$", "},", line) # replace all quotes on headers for header in headers: if line.find('"' + header + '":') != -1: line = line.replace('"', '', 2) # replace double with single quotes on any line without apostrophes. if line.find("'") == -1: line = line.replace('"', "'") lines.append(line) # Write that out to the user. if args.output_file: with open(args.output_file, 'w', encoding='utf-8') as fp: fp.write('\n'.join(lines)) else: try: print(output_str) except UnicodeEncodeError: print("Warning: unable to print to console, use --output-file", file=sys.stderr) raise
def parse_report(args): """Reads an fflogs report and return a list of entries""" # Default values report_start_time = 0 start_time = 0 end_time = 0 enemies = {} # Get report information report_data = fflogs.api("fights", args.report, "www", {"api_key": args.key}) report_start_time = report_data["start"] # Get the start and end timestamps for the specific fight fight_id_found = False for fight in report_data["fights"]: if args.fight and fight["id"] == args.fight: start_time = fight["start_time"] end_time = fight["end_time"] fight_id_found = True break elif fight["end_time"] - fight["start_time"] > end_time - start_time: start_time = fight["start_time"] end_time = fight["end_time"] if args.fight and not fight_id_found: raise Exception("Fight ID not found in report") # Build an enemy name list, since these aren't in the events # Environment special case enemies[-1] = "" # Real enemies for enemy in report_data["enemies"]: enemies[enemy["id"]] = enemy["name"] # Get the actual event list for the single fight options = { "api_key": args.key, "start": start_time, "end": end_time, "filter": 'source.disposition="enemy" and type="cast"', "translate": "true", } event_data = fflogs.api("events", args.report, "www", options) entries = [] # Actually make the entry dicts for event in event_data["events"]: if "sourceID" not in event: event["sourceID"] = event["source"]["id"] enemies[event["sourceID"]] = event["source"]["name"] if event["ability"]["name"].startswith(("Unknown_", "unknown_")): event["ability"]["name"] = "--sync--" entry = make_entry({ "time": datetime.fromtimestamp( (report_start_time + event["timestamp"]) / 1000), "combatant": enemies[event["sourceID"]], "ability_id": hex(event["ability"]["guid"])[2:].upper(), "ability_name": event["ability"]["name"], "line_type": log_event_types[event["type"]], }) entries.append(entry) return entries, datetime.fromtimestamp( (report_start_time + start_time) / 1000)
def main(args): # Get overall reports and enemies. enemies = defaultdict(dict) options = { "api_key": args.key, "translate": "true", } fight_id = 0 for lang in languages: report = fflogs.api("fights", args.report, prefixes[lang], options) if fight_id == 0: start_time, end_time, fight_id = find_start_end_time(report, args) for enemy in report["enemies"]: # Because the overall report contains all enemies from any report, # filter them by the fight_id that we care about. for fight in enemy["fights"]: if fight["id"] == fight_id: enemies[enemy["id"]][lang] = enemy["name"] break # Get abilities for the chosen fight. options = { "api_key": args.key, "start": start_time, "end": end_time, # filtering for disposition='enemy' drops neutral abilities like Encumber. # including death also gets you Wroth Ghosts that don't show up otherwise. "filter": 'source.type="NPC" and (type="cast" or type="death" or type="begincast")', "translate": "true", } abilities = defaultdict(dict) for lang in languages: events = fflogs.api("events", args.report, prefixes[lang], options)["events"] for event in events: actor = 0 if "targetID" in event: actor = event["targetID"] if "ability" not in event: # Some mobs don't show up in the fight enemy list, but are things that get death events. # e.g. {'sourceIsFriendly': False, 'type': 'death', 'target': {'guid': 58, 'name': 'Wroth Ghost', 'type': 'NPC' } } if event["type"] == "death" and not actor and "target" in event: target = event["target"] enemies[target["guid"]][lang] = target["name"] continue ability = event["ability"] abilities[(actor, ability["guid"])][lang] = ability["name"] # Generate mappings of english => locale names. mob_replace = build_mapping(enemies) ability_replace = build_mapping(abilities, ignore_abilities) add_default_sync_mappings(mob_replace) add_default_ability_mappings(ability_replace) # Build some JSON similar to what cactbot expects in trigger files. timeline_replace = [] for lang in languages: if lang == default_language: continue timeline_replace.append( { "locale": lang, "replaceSync": dict(sorted(mob_replace[lang].items(), reverse=True)), "replaceText": dict(sorted(ability_replace[lang].items(), reverse=True)), } ) output = {"timelineReplace": timeline_replace} output_str = json.dumps(output, ensure_ascii=False, indent=2, sort_keys=False) output_str = format_output_str(output_str) # Write that out to the user. if args.output_file: with open(args.output_file, "w", encoding="utf-8") as fp: fp.write(output_str) else: try: print(output_str) except UnicodeEncodeError: print("Warning: unable to print to console, use --output-file", file=sys.stderr) raise
def parse_report(args): """Reads an fflogs report and return a list of entries""" # Default values report_start_time = 0 start_time = 0 end_time = 0 enemies = {} # Get report information report_data = fflogs.api('fights', args.report, 'www', {'api_key': args.key}) report_start_time = report_data['start'] # Get the start and end timestamps for the specific fight fight_id_found = False for fight in report_data['fights']: if args.fight and fight['id'] == args.fight: start_time = fight['start_time'] end_time = fight['end_time'] fight_id_found = True break elif fight['end_time'] - fight['start_time'] > end_time - start_time: start_time = fight['start_time'] end_time = fight['end_time'] if args.fight and not fight_id_found: raise Exception('Fight ID not found in report') # Build an enemy name list, since these aren't in the events # Environment special case enemies[-1] = '' # Real enemies for enemy in report_data['enemies']: enemies[enemy['id']] = enemy['name'] # Get the actual event list for the single fight options = { 'api_key': args.key, 'start': start_time, 'end': end_time, 'filter': 'source.disposition="enemy" and type="cast"', 'translate': 'true', } event_data = fflogs.api('events', args.report, 'www', options) entries = [] # Actually make the entry dicts for event in event_data['events']: if 'sourceID' not in event: event['sourceID'] = event['source']['id'] entry = { 'time': datetime.fromtimestamp((report_start_time + event['timestamp']) / 1000), 'combatant': enemies[event['sourceID']], 'ability_id': hex(event['ability']['guid'])[2:].upper(), 'ability_name': event['ability']['name'], } entries.append(entry) return entries, datetime.fromtimestamp((report_start_time + start_time) / 1000)