463 lines
16 KiB
Python
463 lines
16 KiB
Python
"""Prettifiers for genshinstats api returns.
|
||
|
||
Fixes the huge problem of outdated field names in the api,
|
||
that were leftover from during development
|
||
"""
|
||
import re
|
||
from datetime import datetime
|
||
|
||
character_icons = {
|
||
"PlayerGirl": "Traveler",
|
||
"PlayerBoy": "Traveler",
|
||
"Ambor": "Amber",
|
||
"Qin": "Jean",
|
||
"Hutao": "Hu Tao",
|
||
"Feiyan": "Yanfei",
|
||
"Kazuha": "Kadehara Kazuha",
|
||
"Sara": "Kujou Sara",
|
||
"Shougun": "Raiden Shogun",
|
||
"Tohma": "Thoma",
|
||
}
|
||
|
||
|
||
def _recognize_character_icon(url: str) -> str:
|
||
"""Recognizes a character's icon url and returns its name."""
|
||
exp = r"game_record/genshin/character_.*_(\w+)(?:@\dx)?.png"
|
||
match = re.search(exp, url)
|
||
if match is None:
|
||
raise ValueError(f"{url!r} is not a character icon or image url")
|
||
character = match.group(1)
|
||
return character_icons.get(character) or character
|
||
|
||
|
||
def prettify_stats(data):
|
||
s = data["stats"]
|
||
h = data["homes"][0] if data["homes"] else None
|
||
return {
|
||
"stats": {
|
||
"achievements": s["achievement_number"],
|
||
"active_days": s["active_day_number"],
|
||
"characters": s["avatar_number"],
|
||
"spiral_abyss": s["spiral_abyss"],
|
||
"anemoculi": s["anemoculus_number"],
|
||
"geoculi": s["geoculus_number"],
|
||
"electroculi": s["electroculus_number"],
|
||
"common_chests": s["common_chest_number"],
|
||
"exquisite_chests": s["exquisite_chest_number"],
|
||
"precious_chests": s["precious_chest_number"],
|
||
"luxurious_chests": s["luxurious_chest_number"],
|
||
"unlocked_waypoints": s["way_point_number"],
|
||
"unlocked_domains": s["domain_number"],
|
||
},
|
||
"characters": [
|
||
{
|
||
"name": i["name"],
|
||
"rarity": i["rarity"]
|
||
if i["rarity"] < 100
|
||
else i["rarity"] - 100, # aloy has 105 stars
|
||
"element": i["element"],
|
||
"level": i["level"],
|
||
"friendship": i["fetter"],
|
||
"icon": i["image"],
|
||
"id": i["id"],
|
||
}
|
||
for i in data["avatars"]
|
||
],
|
||
"teapot": {
|
||
# only unique data between realms are names and icons
|
||
"realms": [{"name": s["name"], "icon": s["icon"]} for s in data["homes"]],
|
||
"level": h["level"],
|
||
"comfort": h["comfort_num"],
|
||
"comfort_name": h["comfort_level_name"],
|
||
"comfort_icon": h["comfort_level_icon"],
|
||
"items": h["item_num"],
|
||
"visitors": h["visit_num"], # currently not in use
|
||
}
|
||
if h
|
||
else None,
|
||
"explorations": [
|
||
{
|
||
"name": i["name"],
|
||
"explored": round(i["exploration_percentage"] / 10, 1),
|
||
"type": i["type"],
|
||
"level": i["level"],
|
||
"icon": i["icon"],
|
||
"offerings": i["offerings"],
|
||
}
|
||
for i in data["world_explorations"]
|
||
],
|
||
}
|
||
|
||
|
||
def prettify_characters(data):
|
||
return [
|
||
{
|
||
"name": i["name"],
|
||
"rarity": i["rarity"] if i["rarity"] < 100 else i["rarity"] - 100, # aloy has 105 stars
|
||
"element": i["element"],
|
||
"level": i["level"],
|
||
"friendship": i["fetter"],
|
||
"constellation": sum(c["is_actived"] for c in i["constellations"]),
|
||
"icon": i["icon"],
|
||
"image": i["image"],
|
||
"id": i["id"],
|
||
"collab": i["rarity"] >= 100,
|
||
**(
|
||
{"traveler_name": "Aether" if "Boy" in i["icon"] else "Lumine"}
|
||
if "Player" in i["icon"]
|
||
else {}
|
||
),
|
||
"weapon": {
|
||
"name": i["weapon"]["name"],
|
||
"rarity": i["weapon"]["rarity"],
|
||
"type": i["weapon"]["type_name"],
|
||
"level": i["weapon"]["level"],
|
||
"ascension": i["weapon"]["promote_level"],
|
||
"refinement": i["weapon"]["affix_level"],
|
||
"description": i["weapon"]["desc"],
|
||
"icon": i["weapon"]["icon"],
|
||
"id": i["weapon"]["id"],
|
||
},
|
||
"artifacts": [
|
||
{
|
||
"name": a["name"],
|
||
"pos_name": {
|
||
1: "flower",
|
||
2: "feather",
|
||
3: "hourglass",
|
||
4: "goblet",
|
||
5: "crown",
|
||
}[a["pos"]],
|
||
"full_pos_name": a["pos_name"],
|
||
"pos": a["pos"],
|
||
"rarity": a["rarity"],
|
||
"level": a["level"],
|
||
"set": {
|
||
"name": a["set"]["name"],
|
||
"effect_type": ["none", "single", "classic"][len(a["set"]["affixes"])],
|
||
"effects": [
|
||
{
|
||
"pieces": e["activation_number"],
|
||
"effect": e["effect"],
|
||
}
|
||
for e in a["set"]["affixes"]
|
||
],
|
||
"set_id": int(re.search(r"UI_RelicIcon_(\d+)_\d+", a["icon"]).group(1)), # type: ignore
|
||
"id": a["set"]["id"],
|
||
},
|
||
"icon": a["icon"],
|
||
"id": a["id"],
|
||
}
|
||
for a in i["reliquaries"]
|
||
],
|
||
"constellations": [
|
||
{
|
||
"name": c["name"],
|
||
"effect": c["effect"],
|
||
"is_activated": c["is_actived"],
|
||
"index": c["pos"],
|
||
"icon": c["icon"],
|
||
"id": c["id"],
|
||
}
|
||
for c in i["constellations"]
|
||
],
|
||
"outfits": [
|
||
{"name": c["name"], "icon": c["icon"], "id": c["id"]} for c in i["costumes"]
|
||
],
|
||
}
|
||
for i in data
|
||
]
|
||
|
||
|
||
def prettify_abyss(data):
|
||
fchars = lambda d: [
|
||
{
|
||
"value": a["value"],
|
||
"name": _recognize_character_icon(a["avatar_icon"]),
|
||
"rarity": a["rarity"] if a["rarity"] < 100 else a["rarity"] - 100, # aloy has 105 stars
|
||
"icon": a["avatar_icon"],
|
||
"id": a["avatar_id"],
|
||
}
|
||
for a in d
|
||
]
|
||
todate = lambda x: datetime.fromtimestamp(int(x)).strftime("%Y-%m-%d")
|
||
totime = lambda x: datetime.fromtimestamp(int(x)).isoformat(" ")
|
||
return {
|
||
"season": data["schedule_id"],
|
||
"season_start_time": todate(data["start_time"]),
|
||
"season_end_time": todate(data["end_time"]),
|
||
"stats": {
|
||
"total_battles": data["total_battle_times"],
|
||
"total_wins": data["total_win_times"],
|
||
"max_floor": data["max_floor"],
|
||
"total_stars": data["total_star"],
|
||
},
|
||
"character_ranks": {
|
||
"most_played": fchars(data["reveal_rank"]),
|
||
"most_kills": fchars(data["defeat_rank"]),
|
||
"strongest_strike": fchars(data["damage_rank"]),
|
||
"most_damage_taken": fchars(data["take_damage_rank"]),
|
||
"most_bursts_used": fchars(data["normal_skill_rank"]),
|
||
"most_skills_used": fchars(data["energy_skill_rank"]),
|
||
},
|
||
"floors": [
|
||
{
|
||
"floor": f["index"],
|
||
"stars": f["star"],
|
||
"max_stars": f["max_star"],
|
||
"icon": f["icon"],
|
||
"chambers": [
|
||
{
|
||
"chamber": l["index"],
|
||
"stars": l["star"],
|
||
"max_stars": l["max_star"],
|
||
"has_halves": len(l["battles"]) == 2,
|
||
"battles": [
|
||
{
|
||
"half": b["index"],
|
||
"timestamp": totime(b["timestamp"]),
|
||
"characters": [
|
||
{
|
||
"name": _recognize_character_icon(c["icon"]),
|
||
"rarity": c["rarity"]
|
||
if c["rarity"] < 100
|
||
else c["rarity"] - 100, # aloy has 105 stars
|
||
"level": c["level"],
|
||
"icon": c["icon"],
|
||
"id": c["id"],
|
||
}
|
||
for c in b["avatars"]
|
||
],
|
||
}
|
||
for b in l["battles"]
|
||
],
|
||
}
|
||
for l in f["levels"]
|
||
],
|
||
}
|
||
for f in data["floors"]
|
||
],
|
||
}
|
||
|
||
|
||
def prettify_activities(data):
|
||
activities = {
|
||
k: v if v.get("exists_data") else {"records": []}
|
||
for activity in data["activities"]
|
||
for k, v in activity.items()
|
||
}
|
||
return {
|
||
"hyakunin": [
|
||
{
|
||
"id": r["challenge_id"],
|
||
"name": r["challenge_name"],
|
||
"difficulty": r["difficulty"],
|
||
"medal_icon": r["heraldry_icon"],
|
||
"score": r["max_score"],
|
||
"multiplier": r["score_multiple"],
|
||
"lineups": [
|
||
{
|
||
"characters": [
|
||
{
|
||
"name": _recognize_character_icon(c["icon"]),
|
||
"rarity": c["rarity"]
|
||
if c["rarity"] < 100
|
||
else c["rarity"] - 100, # aloy has 105 stars
|
||
"level": c["level"],
|
||
"icon": c["icon"],
|
||
"id": c["id"],
|
||
"trial": c["is_trail_avatar"],
|
||
}
|
||
for c in l["avatars"]
|
||
],
|
||
"skills": [
|
||
{"name": s["name"], "desc": s["desc"], "icon": s["icon"], "id": s["id"]}
|
||
for s in l["skills"]
|
||
],
|
||
}
|
||
for l in r["lineups"]
|
||
],
|
||
}
|
||
for r in activities["sumo"]["records"]
|
||
],
|
||
"labyrinth": None,
|
||
}
|
||
|
||
|
||
def prettify_notes(data):
|
||
return {
|
||
"resin": data["current_resin"],
|
||
"until_resin_limit": data["resin_recovery_time"],
|
||
"max_resin": data["max_resin"],
|
||
"total_commissions": data["total_task_num"],
|
||
"completed_commissions": data["finished_task_num"],
|
||
"claimed_commission_reward": data["is_extra_task_reward_received"],
|
||
"max_boss_discounts": data["resin_discount_num_limit"],
|
||
"remaining_boss_discounts": data["remain_resin_discount_num"],
|
||
"expeditions": [
|
||
{
|
||
"icon": exp["avatar_side_icon"],
|
||
"remaining_time": exp["remained_time"],
|
||
"status": exp["status"],
|
||
}
|
||
for exp in data["expeditions"]
|
||
],
|
||
"max_expeditions": data["max_expedition_num"],
|
||
"realm_currency": data["current_home_coin"],
|
||
"max_realm_currency": data["max_home_coin"],
|
||
"until_realm_currency_limit": data["home_coin_recovery_time"],
|
||
}
|
||
|
||
|
||
def prettify_game_accounts(data):
|
||
return [
|
||
{
|
||
"uid": int(a["game_uid"]),
|
||
"server": a["region_name"],
|
||
"level": a["level"],
|
||
"nickname": a["nickname"],
|
||
# idk what these are for:
|
||
"biz": a["game_biz"],
|
||
"is_chosen": a["is_chosen"],
|
||
"is_official": a["is_official"],
|
||
}
|
||
for a in data
|
||
]
|
||
|
||
|
||
def prettify_wish_history(data, banner_name=None):
|
||
return [
|
||
{
|
||
"type": i["item_type"],
|
||
"name": i["name"],
|
||
"rarity": int(i["rank_type"]),
|
||
"time": i["time"],
|
||
"id": int(i["id"]),
|
||
"banner": banner_name,
|
||
"banner_type": int(i["gacha_type"]),
|
||
"uid": int(i["uid"]),
|
||
}
|
||
for i in data
|
||
]
|
||
|
||
|
||
def prettify_gacha_items(data):
|
||
return [
|
||
{
|
||
"name": i["name"],
|
||
"type": i["item_type"],
|
||
"rarity": int(i["rank_type"]),
|
||
"id": 10000000 + int(i["item_id"]) - 1000
|
||
if len(i["item_id"]) == 4
|
||
else int(i["item_id"]),
|
||
}
|
||
for i in data
|
||
]
|
||
|
||
|
||
def prettify_banner_details(data):
|
||
per = lambda p: None if p == "0%" else float(p[:-1].replace(",", "."))
|
||
fprobs = (
|
||
lambda l: [
|
||
{
|
||
"type": i["item_type"],
|
||
"name": i["item_name"],
|
||
"rarity": int(i["rank"]),
|
||
"is_up": bool(i["is_up"]),
|
||
"order_value": i["order_value"],
|
||
}
|
||
for i in l
|
||
]
|
||
if l
|
||
else []
|
||
)
|
||
fitems = (
|
||
lambda l: [
|
||
{
|
||
"type": i["item_type"],
|
||
"name": i["item_name"],
|
||
"element": {
|
||
"风": "Anemo",
|
||
"火": "Pyro",
|
||
"水": "Hydro",
|
||
"雷": "Electro",
|
||
"冰": "Cryo",
|
||
"岩": "Geo",
|
||
"?": "Dendro",
|
||
"": None,
|
||
}[i["item_attr"]],
|
||
"icon": i["item_img"],
|
||
}
|
||
for i in l
|
||
]
|
||
if l
|
||
else []
|
||
)
|
||
return {
|
||
"banner_type_name": {
|
||
100: "Novice Wishes",
|
||
200: "Permanent Wish",
|
||
301: "Character Event Wish",
|
||
302: "Weapon Event Wish",
|
||
}[int(data["gacha_type"])],
|
||
"banner_type": int(data["gacha_type"]),
|
||
"banner": re.sub(r"<.*?>", "", data["title"]).strip(),
|
||
"title": data["title"],
|
||
"content": data["content"],
|
||
"date_range": data["date_range"],
|
||
"r5_up_prob": per(data["r5_up_prob"]), # probability for rate-up 5*
|
||
"r4_up_prob": per(data["r4_up_prob"]), # probability for rate-up 4*
|
||
"r5_prob": per(data["r5_prob"]), # probability for 5*
|
||
"r4_prob": per(data["r4_prob"]), # probability for 4*
|
||
"r3_prob": per(data["r3_prob"]), # probability for 3*
|
||
"r5_guarantee_prob": per(data["r5_baodi_prob"]), # probability for 5* incl. guarantee
|
||
"r4_guarantee_prob": per(data["r4_baodi_prob"]), # probability for 4* incl. guarantee
|
||
"r3_guarantee_prob": per(data["r3_baodi_prob"]), # probability for 3* incl. guarantee
|
||
"r5_up_items": fitems(
|
||
data["r5_up_items"]
|
||
), # list of 5* rate-up items that you can get from banner
|
||
"r4_up_items": fitems(
|
||
data["r4_up_items"]
|
||
), # list of 4* rate-up items that you can get from banner
|
||
"r5_items": fprobs(data["r5_prob_list"]), # list 5* of items that you can get from banner
|
||
"r4_items": fprobs(data["r4_prob_list"]), # list 4* of items that you can get from banner
|
||
"r3_items": fprobs(data["r3_prob_list"]), # list 3* of items that you can get from banner
|
||
"items": fprobs(
|
||
sorted(
|
||
data["r5_prob_list"] + data["r4_prob_list"] + data["r3_prob_list"],
|
||
key=lambda x: x["order_value"],
|
||
)
|
||
),
|
||
}
|
||
|
||
|
||
def prettify_trans(data, reasons={}):
|
||
if data and "name" in data[0]:
|
||
# transaction item
|
||
return [
|
||
{
|
||
"time": i["time"],
|
||
"name": i["name"],
|
||
"rarity": int(i["rank"]),
|
||
"amount": int(i["add_num"]),
|
||
"reason": reasons.get(int(i["reason"]), ""),
|
||
"reason_id": int(i["reason"]),
|
||
"uid": int(i["uid"]),
|
||
"id": int(i["id"]),
|
||
}
|
||
for i in data
|
||
]
|
||
else:
|
||
# transaction
|
||
return [
|
||
{
|
||
"time": i["time"],
|
||
"amount": int(i["add_num"]),
|
||
"reason": reasons.get(int(i["reason"]), ""),
|
||
"reason_id": int(i["reason"]),
|
||
"uid": int(i["uid"]),
|
||
"id": int(i["id"]),
|
||
}
|
||
for i in data
|
||
]
|