예제 #1
0
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)
예제 #2
0
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)
예제 #3
0
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
예제 #4
0
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
예제 #5
0
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)
예제 #6
0
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
예제 #7
0
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)