async def _initialize(self) -> None: """Configures initial universe, loads mappers and runs one iteration, does not start any periodic callbacks or loops. Prereq of daemon() and killmail_oneshot().""" self.universes = {} self.mappers = {} # Path is a custom universe where users can add jumpbridges or other # custom connections. If it is in use we create a universe for it and # add all custom connections. if "path" in self.config and len(self.config["path"]): self.universes["_path"] = await Universe.from_empty() for path in self.config["path"]: state = State() setattr(state, path["type"], True) left = System(path["from"]) right = System(path["to"]) connection = Connection(left, right, state) await self.universes["_path"].connect(connection) if "mapper" in self.config and len(self.config["mapper"]): log.info("initialize: `unchaind` with {} mappers.".format( len(self.config["mapper"]))) for index, mapper in enumerate(self.config["mapper"]): mapper.update({"home_system": self.config.get("home_system")}) transport = await get_transport(mapper["type"] ).from_config(mapper) if transport is None: log.warn("initialize: failed to create %s mapper", mapper["type"]) else: # The transport has been created, we can now create a name # and universe for this mapper. self.universes[ f"_{mapper['type']}_{index}"] = await Universe.from_empty( ) self.mappers[f"_{mapper['type']}_{index}"] = get_mapper( mapper["type"])(transport) log.info( "initialize: %d mappers initialized, starting initial pass.", len(self.mappers), ) # Run our initial pass to get all the results without firing their # callbacks since we're booting await self.periodic_mappers()
async def update(self) -> Universe: """Update our internal Universe with a new Universe.""" data = await self.transport.update() if self.universe is None: self.universe: Universe = await Universe.from_empty() universe: Universe = await Universe.from_empty() chain = data["chainMap"] connections = chain["wormholes"] if isinstance(connections, list): # For some weird reason when there are no connections siggy changes # the type of this to a list instead of a dict log.debug("update: connections was a list") self.universe = universe return universe for connection in connections.values(): state = State() state.end_of_life = bool(connection.get("eol", 0)) await universe.connect( Connection( System(connection["from_system_id"]), System(connection["to_system_id"]), state, )) aliases: Dict[System, str] = {} systems = chain["systems"] for system in systems.values(): if (len(system.get("displayName", "")) and system["displayName"] != system["name"]): aliases[System(system["systemID"])] = system["displayName"] universe.aliases = aliases self.universe = universe return self.universe
async def _match_location(value: str, package: Dict[str, Any], universe: Universe) -> bool: killmail = package["killmail"] solar_system = System(killmail.get("solar_system_id", None)) if value == "chain": return solar_system in universe.systems if value == "wspace": return bool(re.match(r"J\d{6}", solar_system.name)) return value.lower() == solar_system.name.lower()
async def update(self) -> Universe: """Update our internal Universe with a new Universe.""" data = await self.transport.update() if self.universe is None: self.universe: Universe = await Universe.from_empty() universe: Universe = await Universe.from_empty() for connection in data: state = State() await universe.connect( Connection( System(connection["source_solar_system"]["id"]), System(connection["destination_solar_system"]["id"]), state, ) ) self.universe = universe return self.universe
async def _match_security_status(value: str, package: Dict[str, Any], universe: Universe) -> bool: killmail = package["killmail"] solar_system = System(killmail.get("solar_system_id", None)) value = value.lower() if value in ("high", "highsec"): return solar_system.truesec >= 0.45 if value in ("low", "lowsec"): return 0.0 < solar_system.truesec < 0.45 if value in ("null", "nullsec"): return solar_system.truesec < 0.0 log.warning("unknown security status '%s'", value) return False
async def stats_for_killmail(package: Dict[str, Any], universe: Universe) -> Optional[KillmailStats]: try: victim_ship_typeid: int = int( package["killmail"]["victim"]["ship_type_id"]) solar_system_id: int = int(package["killmail"]["solar_system_id"]) d = { "victim_moniker": char_name_with_ticker(package["killmail"]["victim"]), "victim_ship": esi_util.type_details(victim_ship_typeid), "final_blow_moniker": char_name_with_ticker( next( filter( lambda x: x["final_blow"], package["killmail"]["attackers"], ))), "top_damage_moniker": char_name_with_ticker( max( package["killmail"]["attackers"], key=lambda x: x["damage_done"], )), "attacker_entities": gather(*[ entity_ticker_for_char(x) for x in package["killmail"]["attackers"] ]), "attacker_ships": gather(*[(esi_util.type_details(x["ship_type_id"])) for x in package["killmail"]["attackers"]]), "solar_system_name": universe.system_name(System(solar_system_id)), } d = await multi(d) d["victim_ship"] = d["victim_ship"]["name"] d["attacker_entities_summary"] = _stringify_counter_by_popularity( collections.Counter(d["attacker_entities"])) d["attacker_ships_summary"] = _stringify_counter_by_popularity( collections.Counter( map(operator.itemgetter("name"), d["attacker_ships"]))) d.pop("attacker_entities", None) d.pop("attacker_ships", None) d["timestamp"] = int( dateutil.parser.parse( package["killmail"]["killmail_time"]).timestamp()) d["victim_ship_typeid"] = victim_ship_typeid d["kill_id"] = package["killID"] d["isk_value"] = package["zkb"]["totalValue"] d["solar_system_id"] = solar_system_id return KillmailStats(**d) except Exception as e: log.exception(e) return None