fix merge conflicts

This commit is contained in:
eli fessler
2022-11-25 10:41:18 -06:00
parent 8d60a6a8ac
commit 4fc712ede4

463
s3s.py
View File

@@ -6,11 +6,12 @@
import argparse, base64, datetime, json, os, shutil, re, requests, sys, time, uuid
from concurrent.futures import ThreadPoolExecutor
import mmh3, msgpack
from subprocess import call
import msgpack
from packaging import version
import iksm, utils
A_VERSION = "0.1.17"
A_VERSION = "0.2.0"
DEBUG = False
@@ -85,19 +86,26 @@ def write_config(tokens):
config_file.close()
def headbutt():
def headbutt(forcelang=None):
'''Returns a (dynamic!) header used for GraphQL requests.'''
if forcelang:
lang = forcelang
country = forcelang[-2:]
else:
lang = USER_LANG
country = USER_COUNTRY
graphql_head = {
'Authorization': f'Bearer {BULLETTOKEN}', # update every time it's called with current global var
'Accept-Language': USER_LANG,
'Accept-Language': lang,
'User-Agent': APP_USER_AGENT,
'X-Web-View-Ver': iksm.get_web_view_ver(),
'Content-Type': 'application/json',
'Accept': '*/*',
'Origin': iksm.SPLATNET3_URL,
'X-Requested-With': 'com.nintendo.znca',
'Referer': f'{iksm.SPLATNET3_URL}?lang={USER_LANG}&na_country={USER_COUNTRY}&na_lang={USER_LANG}',
'Referer': f'{iksm.SPLATNET3_URL}?lang={lang}&na_country={country}&na_lang={lang}',
'Accept-Encoding': 'gzip, deflate'
}
return graphql_head
@@ -228,10 +236,14 @@ def fetch_json(which, separate=False, exportall=False, specific=False, numbers_o
if sha is not None:
if DEBUG:
print(f"* making query1 to {sha}")
lang = 'en-US' if sha == "CoopHistoryQuery" else None
sha = utils.translate_rid[sha]
battle_ids, job_ids = [], []
query1 = requests.post(utils.GRAPHQL_URL, data=utils.gen_graphql_body(sha), headers=headbutt(), cookies=dict(_gtoken=GTOKEN))
query1 = requests.post(utils.GRAPHQL_URL,
data=utils.gen_graphql_body(sha),
headers=headbutt(forcelang=lang),
cookies=dict(_gtoken=GTOKEN))
query1_resp = json.loads(query1.text)
swim()
@@ -270,7 +282,6 @@ def fetch_json(which, separate=False, exportall=False, specific=False, numbers_o
ink_list.extend(battle_ids)
salmon_list.extend(job_ids)
else: # ALL DATA - TAKES A LONG TIME
ink_list.extend(thread_pool.map(fetch_detailed_result, [True]*len(battle_ids), battle_ids, [swim]*len(battle_ids)))
salmon_list.extend(thread_pool.map(fetch_detailed_result, [False]*len(job_ids), job_ids, [swim]*len(job_ids)))
@@ -300,26 +311,33 @@ def fetch_json(which, separate=False, exportall=False, specific=False, numbers_o
return combined
def fetch_detailed_result(is_vs_history, history_id, swim):
'''Helper function for fetch_json().'''
sha = "VsHistoryDetailQuery" if is_vs_history else "CoopHistoryDetailQuery"
varname = "vsResultId" if is_vs_history else "coopHistoryDetailId"
lang = None if is_vs_history else 'en-US'
query2 = requests.post(utils.GRAPHQL_URL,
data=utils.gen_graphql_body(utils.translate_rid[sha], varname, history_id),
headers=headbutt(),
headers=headbutt(forcelang=lang),
cookies=dict(_gtoken=GTOKEN))
query2_resp = json.loads(query2.text)
swim()
return query2_resp
def update_salmon_profile():
''' Updates stat.ink Salmon Run stats/profile.'''
pass
pass # SR TODO
# prefetch_checks()
# old code - need stat.ink s3 support - TODO
# url = nintendo's api/coop_results...
# results_list = requests.get(url, headers=headbutt(), cookies=dict(_gtoken=GTOKEN))
# results_list = requests.post(utils.GRAPHQL_URL,
# data=utils.gen_graphql_body(utils.translate_rid["CoopHistoryQuery"]),
# headers=headbutt(forcelang='en-US'),
# cookies=dict(_gtoken=GTOKEN))
# data = json.loads(results_list.text)
# profile = data["summary"]
@@ -329,10 +347,10 @@ def update_salmon_profile():
# "total_eggs": profile["card"]["ikura_total"],
# "total_rescued": profile["card"]["help_total"],
# "total_point": profile["card"]["kuma_point_total"]
# # TODO - other new things - fish scales, xtrawave stuff?, etc.
# # other new things - total fish scales, kings defeated, current & total points?
# }
# url = "https://stat.ink/api/v2/salmon-stats" # TODO - need stat.ink s3 support
# url = "https://stat.ink/api/v2/salmon-stats"
# auth = {'Authorization': f'Bearer {API_KEY}'}
# updateprofile = requests.post(url, headers=auth, data=payload)
@@ -598,7 +616,9 @@ def prepare_battle_result(battle, ismonitoring, isblackout, overview_data=None):
payload["knockout"] = "no" if battle["knockout"] is None or battle["knockout"] == "NEITHER" else "yes"
payload["rank_exp_change"] = battle["bankaraMatch"]["earnedUdemaePoint"]
battle_id = base64.b64decode(battle["id"]).decode('utf-8')
battle_id = base64.b64decode(battle["id"]).decode('utf-8')
battle_id_mutated = battle_id.replace("BANKARA", "RECENT") # normalize the ID, make work with -M and -r
if overview_data is None: # no passed in file with -i
overview_post = requests.post(utils.GRAPHQL_URL,
data=utils.gen_graphql_body(utils.translate_rid["BankaraBattleHistoriesQuery"]),
@@ -620,13 +640,14 @@ def prepare_battle_result(battle, ismonitoring, isblackout, overview_data=None):
for parent in ranked_list: # groups in overview (ranked) JSON/screen
for idx, child in enumerate(parent["historyDetails"]["nodes"]):
overview_battle_id = base64.b64decode(child["id"]).decode('utf-8')
overview_battle_id = base64.b64decode(child["id"]).decode('utf-8')
overview_battle_id_mutated = overview_battle_id.replace("BANKARA", "RECENT") # same battle, different screens
if overview_battle_id_mutated == battle_id: # found the battle ID in the other file
if overview_battle_id_mutated == battle_id_mutated: # found the battle ID in the other file
full_rank = re.split('([0-9]+)', child["udemae"].lower())
was_s_plus_before = len(full_rank) > 1 # true if "before" rank is s+
payload["rank_before"] = full_rank[0]
if was_s_plus_before:
payload["rank_before_s_plus"] = int(full_rank[1])
@@ -643,13 +664,11 @@ def prepare_battle_result(battle, ismonitoring, isblackout, overview_data=None):
else:
payload["rank_up_battle"] = "no"
if parent["bankaraMatchChallenge"]["udemaeAfter"] is None:
payload["rank_after"] = payload["rank_before"]
if was_s_plus_before:
payload["rank_after_s_plus"] = payload["rank_before_s_plus"] # also s+ after
else:
if parent["bankaraMatchChallenge"]["udemaeAfter"] is not None:
if idx != 0:
payload["rank_after"] = payload["rank_before"]
if was_s_plus_before: # not a rank-up battle, so must be the same
payload["rank_after_s_plus"] = payload["rank_before_s_plus"]
else: # the battle where we actually ranked up
full_rank_after = re.split('([0-9]+)', parent["bankaraMatchChallenge"]["udemaeAfter"].lower())
payload["rank_after"] = full_rank_after[0]
@@ -729,13 +748,233 @@ def prepare_battle_result(battle, ismonitoring, isblackout, overview_data=None):
return payload
def prepare_job_result(battle, ismonitoring, isblackout, overview_data=None):
def prepare_job_result(job, ismonitoring, isblackout, overview_data=None):
'''Converts the Nintendo JSON format for a Salmon Run job to the stat.ink one.'''
pass # stat.ink doesn't support SR yet
# combo of set_teammates() + salmon_post_shift() - mirrors prepare_battle_result()
# specify if private or not; also ensure SR results are formatted the same in post_result()!
# set payload["splatnet_json"]
# https://github.com/fetus-hina/stat.ink/wiki/Spl3-API:-Salmon-%EF%BC%8D-Post
payload = {}
job = job["coopHistoryDetail"]
full_id = utils.b64d(job["id"])
payload["uuid"] = str(uuid.uuid5(utils.SALMON_NAMESPACE, full_id[-52:]))
payload["stage"] = utils.b64d(job["coopStage"]["id"])
payload["danger_rate"] = job["dangerRate"] * 100
payload["king_smell"] = job["smellMeter"]
waves_cleared = job["resultWave"] - 1 # resultWave = 0 if all normal waves cleared
payload["clear_waves"] = 3 if waves_cleared == -1 else waves_cleared
if payload["clear_waves"] < 0: # player dc'd
payload["clear_waves"] = None
elif payload["clear_waves"] != 3: # job defeat only
underperformed = False
for wave in job["waveResults"]:
if wave["teamDeliverCount"] < wave["deliverNorm"]:
underperformed = True
payload["fail_reason"] = "time_limit" if underperformed else "wipe_out"
# xtrawave only
# https://stat.ink/api-info/boss-salmonid3
if job["bossResult"]:
king_id = utils.b64d(job["bossResult"]["boss"]["id"])
if king_id == 23: # cohozuna
payload["king_salmonid"] = "yokozuna"
payload["clear_extra"] = "yes" if job["bossResult"]["hasDefeatBoss"] else "no"
# https://stat.ink/api-info/salmon-title3
payload["title_after"] = utils.b64d(job["afterGrade"]["id"])
payload["title_exp_after"] = job["afterGradePoint"]
point_diff = 20 if payload["clear_waves"] == 3 else -30 + (10 * job["resultWave"]) # +20 for win or -(30-10w) for loss
if payload["title_exp_after"] - point_diff >= 0: # before exp isn't negative, i.e. no title change
payload["title_before"] = payload["title_after"]
payload["title_exp_before"] = payload["title_exp_after"] - point_diff
else: # ranked up
payload["title_before"] = payload["title_after"] - 1
# exp...
geggs = job["myResult"]["goldenDeliverCount"]
peggs = job["myResult"]["deliverCount"]
for player in job["memberResults"]:
geggs += player["goldenDeliverCount"]
peggs += player["deliverCount"]
payload["golden_eggs"] = geggs
payload["power_eggs"] = peggs
if job["scale"]:
payload["gold_scale"] = job["scale"]["gold"]
payload["silver_scale"] = job["scale"]["silver"]
payload["bronze_scale"] = job["scale"]["bronze"]
payload["job_score"] = job["jobScore"] # job score
payload["job_rate"] = job["jobRate"] # pay grade
payload["job_bonus"] = job["jobBonus"] # clear bonus
payload["job_point"] = job["jobPoint"] # your points = floor((score x rate) + bonus)
# note the current bug with "bonus" lol... https://github.com/frozenpandaman/s3s/wiki/%7C-splatnet-bugs
waves = []
for wave in job["waveResults"]:
wave_info = {}
wave_info["tide"] = "low" if wave["waterLevel"] == 0 else "high" if wave["waterLevel"] == 2 else "normal"
wave_info["golden_quota"] = wave["deliverNorm"]
wave_info["golden_delivered"] = wave["teamDeliverCount"]
wave_info["golden_appearances"] = wave["goldenPopCount"]
if wave["eventWave"]:
event_id = utils.b64d(wave["eventWave"]["id"])
translate_occurrence = {
1: "rush",
2: "goldie_seeking",
3: "the_griller",
4: "the_mothership",
5: "fog",
6: "cohock_charge",
7: "giant_tornado",
8: "mudmouth_eruption"
}
wave_info["event"] = translate_occurrence[event_id]
translate_special = {
20006: "nicedama",
20007: "hopsonar",
20009: "megaphone51",
20010: "jetpack",
20012: "kanitank",
20013: "sameride",
20014: "tripletornado"
}
special_uses = {
"nicedama": 0,
"hopsonar": 0,
"megaphone51": 0,
"jetpack": 0,
"kanitank": 0,
"sameride": 0,
"tripletornado": 0,
"unknown": 0
}
for wep_use in wave["specialWeapons"]:
special_id = utils.b64d(wep_use["id"])
special_key = translate_special.get(special_id, "unknown")
special_uses[special_key] += 1 # increment value of the key each time it's found
wave_info["special_uses"] = special_uses
waves.append(wave_info)
payload["waves"] = waves
players = []
players_json = [job["myResult"]]
for teammate in job["memberResults"]:
players_json.append(teammate)
for player in players_json:
player_info = {}
player_info["me"] = "yes" if player["player"]["isMyself"] else "no"
player_info["name"] = player["player"]["name"]
player_info["number"] = player["player"]["nameId"]
player_info["splashtag_title"] = player["player"]["byname"]
player_info["golden_eggs"] = player["goldenDeliverCount"]
player_info["golden_assist"] = player["goldenAssistCount"]
player_info["power_eggs"] = player["deliverCount"]
player_info["rescue"] = player["rescueCount"]
player_info["rescued"] = player["rescuedCount"]
player_info["defeat_boss"] = player["defeatEnemyCount"]
dc_indicators = [
player_info["golden_eggs"],
player_info["power_eggs"],
player_info["rescue"],
player_info["rescued"],
player_info["defeat_boss"]
]
player_info["disconnected"] = "yes" if all(value == 0 for value in dc_indicators) else "no"
slop_num = utils.b64d(player["player"]["uniform"]["id"])
translate_slop = {
1: "orange",
2: "green",
3: "yellow",
4: "pink",
5: "blue",
6: "black",
7: "white"
}
player_info["uniform"] = translate_slop[slop_num]
try:
special_id = utils.b64d(player["specialWeapon"]["id"])
player_info["special"] = translate_special[special_id]
except TypeError: # player.specialWeapon is null - player dc'd
pass
except KeyError: # invalid special weapon - likely defaulted to '1' before it could be assigned
pass
weapons = []
for weapon in player["weapons"]: # should always be returned in in english due to headbutt() using forcelang
wep_string = weapon["name"].lower().replace(" ", "_").replace("-", "_").replace(".", "").replace("'", "")
if wep_string == "random": # NINTENDOOOOOOO
wep_string = None
weapons.append(wep_string)
player_info["weapons"] = weapons
players.append(player_info)
payload["players"] = players
# https://stat.ink/api-info/boss-salmonid3
bosses = {}
translate_boss = {
4: "bakudan",
5: "katapad",
6: "teppan",
7: "hebi",
8: "tower",
9: "mogura",
10: "koumori",
11: "hashira",
12: "diver",
13: "tekkyu",
14: "nabebuta",
15: "kin_shake",
17: "grill",
20: "doro_shake"
}
for boss in job["enemyResults"]:
boss_id = utils.b64d(boss["enemy"]["id"])
boss_key = translate_boss[boss_id]
bosses[boss_key] = {
"appearances": boss["popCount"],
"defeated": boss["teamDefeatCount"],
"defeated_by_me": boss["defeatCount"]
}
payload["bosses"] = bosses
payload["start_at"] = utils.epoch_time(job["playedTime"])
payload["private"] = "yes" if not job["jobPoint"] else "no"
payload["big_run"] = "no" # SR TODO
if isblackout:
# fix payload
for player in payload["players"]:
if player["me"] == "no":
player["name"] = None
player["number"] = None
player["splashtag_title"] = None
# fix job json
for player in job["memberResults"]:
player["player"]["name"] = None
player["player"]["nameId"] = None
player["player"]["byname"] = None
payload["splatnet_json"] = json.dumps(job)
payload["automated"] = "yes"
return payload
def post_result(data, ismonitoring, isblackout, istestrun, overview_data=None):
@@ -747,12 +986,12 @@ def post_result(data, ismonitoring, isblackout, istestrun, overview_data=None):
if isinstance(data, list): # -o export format
try:
data = [x for x in data if x['data']['vsHistoryDetail'] is not None] # avoid {'data': {'vsHistoryDetail': None}} error
results = sorted(data, key=lambda d: d['data']['vsHistoryDetail']['playedTime'])
data = [x for x in data if x["data"]["vsHistoryDetail"] is not None] # avoid {"data": {"vsHistoryDetail": None}} error
results = sorted(data, key=lambda d: d["data"]["vsHistoryDetail"]["playedTime"])
except KeyError:
try:
data = [x for x in data if x['coopHistoryDetail'] is not None]
results = sorted(data, key=lambda d: d['data']['coopHistoryDetail']['playedTime'])
data = [x for x in data if x["data"]["coopHistoryDetail"] is not None]
results = sorted(data, key=lambda d: d["data"]["coopHistoryDetail"]["playedTime"])
except KeyError: # unsorted - shouldn't happen
print("(!) Uploading without chronologically sorting results")
results = data
@@ -766,8 +1005,10 @@ def post_result(data, ismonitoring, isblackout, istestrun, overview_data=None):
for i in range(len(results)):
if "vsHistoryDetail" in results[i]["data"]: # ink battle
payload = prepare_battle_result(results[i]["data"], ismonitoring, isblackout, overview_data)
which = "ink"
elif "coopHistoryDetail" in results[i]["data"]: # salmon run job
payload = prepare_job_result(results[i]["data"], ismonitoring, isblackout, overview_data)
which = "salmon"
else: # shouldn't happen
print("Ill-formatted JSON while uploading. Exiting.")
print('\nDebug info:')
@@ -778,7 +1019,8 @@ def post_result(data, ismonitoring, isblackout, istestrun, overview_data=None):
continue
# should have been taken care of in fetch_json() but just in case...
if payload["lobby"] == "private" and utils.custom_key_exists("ignore_private", CONFIG_DATA):
if payload.get("lobby") == "private" and utils.custom_key_exists("ignore_private", CONFIG_DATA) or \
payload.get("private") == "yes" and utils.custom_key_exists("ignore_private", CONFIG_DATA): # SR version
continue
s3s_values = {'agent': '\u0073\u0033\u0073', 'agent_version': f'v{A_VERSION}'} # lol
@@ -792,8 +1034,12 @@ def post_result(data, ismonitoring, isblackout, istestrun, overview_data=None):
if istestrun:
payload["test"] = "yes"
# post
url = "https://stat.ink/api/v3/battle"
# POST
url = "https://stat.ink/api/v3"
if which == "ink":
url += "/battle"
elif which == "salmon":
url += "/salmon"
auth = {'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/x-msgpack'}
postbattle = requests.post(url, headers=auth, data=msgpack.packb(payload), allow_redirects=False)
@@ -817,12 +1063,12 @@ def post_result(data, ismonitoring, isblackout, istestrun, overview_data=None):
print(f"* time uploaded: {time_uploaded}; time now: {time_now}")
if postbattle.status_code != 201: # Created (or already exists)
print("Error uploading battle. Message from server:")
print(f"Error uploading {utils.set_noun(which)[:-1]}. Message from server:")
print(postbattle.content.decode('utf-8'))
elif time_uploaded <= time_now - 5: # give some leeway
print(f"Battle already uploaded - {headerloc}")
print(f"{utils.set_noun(which)[:-1].capitalize()} already uploaded - {headerloc}")
else: # 200 OK
print(f"Battle uploaded to {headerloc}")
print(f"{utils.set_noun(which)[:-1].capitalize()} uploaded to {headerloc}")
def check_for_updates():
@@ -849,7 +1095,7 @@ def check_for_updates():
"` as soon as possible.\n")
else: # no git directory
print(" Visit the site below to update:\nhttps://github.com/frozenpandaman/s3s\n")
except: # if there's a problem connecting to github
except Exception as e: # if there's a problem connecting to github
print('\033[3m' + "» Couldn't connect to GitHub. Please update the script manually via " \
'`\033[91m' + "git pull" + '\033[0m' + "`." + '\033[0m' + "\n")
# print('\033[3m' + "» While s3s is in beta, please update the script regularly via " \
@@ -928,24 +1174,26 @@ def get_num_results(which):
def fetch_and_upload_single_result(hash, noun, isblackout, istestrun):
'''Performs a GraphQL request for a single vsResultId/coopResultId and call post_result().'''
'''Performs a GraphQL request for a single vsResultId/coopHistoryDetailId and call post_result().'''
if noun in ("battles", "battle"):
dict_key = "VsHistoryDetailQuery"
dict_key2 = "vsResultId"
lang = None
else: # noun == "jobs" or "job"
dict_key = "CoopHistoryQuery"
dict_key2 = "coopResultId"
dict_key = "CoopHistoryDetailQuery"
dict_key2 = "coopHistoryDetailId"
lang = 'en-US'
result_post = requests.post(utils.GRAPHQL_URL,
data=utils.gen_graphql_body(utils.translate_rid[dict_key], dict_key2, hash),
headers=headbutt(),
headers=headbutt(forcelang=lang),
cookies=dict(_gtoken=GTOKEN))
result = json.loads(result_post.text)
post_result(result, False, isblackout, istestrun) # not monitoring mode
def check_if_missing(which, isblackout, istestrun):
def check_if_missing(which, isblackout, istestrun, skipprefetch):
'''Checks for unuploaded battles and uploads any that are found (-r flag).'''
noun = utils.set_noun(which)
@@ -958,10 +1206,10 @@ def check_if_missing(which, isblackout, istestrun):
urls.append("https://stat.ink/api/v3/s3s/uuid-list") # max 200 entries
else:
urls.append(None)
# if which == "both" or which == "salmon":
# urls.append("...")
# else:
# urls.append(None)
if which == "both" or which == "salmon":
urls.append("https://stat.ink/api/v3/salmon/uuid-list")
else:
urls.append(None)
noun = "battles" # first (and maybe only)
which = "ink"
@@ -978,19 +1226,27 @@ def check_if_missing(which, isblackout, istestrun):
# ! fetch from online
# specific - check ALL possible battles; printout - to show tokens are being checked at program start
splatnet_ids = fetch_json(which, specific=True, numbers_only=True, printout=True)
splatnet_ids = fetch_json(which, specific=True, numbers_only=True, printout=True, skipprefetch=skipprefetch)
# same as code in -i section below...
for id in reversed(splatnet_ids):
full_id = utils.b64d(id)
old_uuid = full_id[-36:]
new_uuid = str(uuid.uuid5(utils.S3S_NAMESPACE, full_id[-52:]))
if new_uuid in statink_uploads:
continue
if old_uuid in statink_uploads:
if not utils.custom_key_exists("force_uploads", CONFIG_DATA):
if which == "ink":
old_battle_uuid = full_id[-36:]
new_battle_uuid = str(uuid.uuid5(utils.S3S_NAMESPACE, full_id[-52:]))
if new_battle_uuid in statink_uploads:
continue
if old_battle_uuid in statink_uploads:
if not utils.custom_key_exists("force_uploads", CONFIG_DATA):
continue
elif which == "salmon":
job_uuid = str(uuid.uuid5(utils.SALMON_NAMESPACE, full_id[-52:]))
if job_uuid in statink_uploads:
continue
if not printed:
printed = True
print(f"Previously-unuploaded {noun} detected. Uploading now...")
@@ -1003,25 +1259,9 @@ def check_if_missing(which, isblackout, istestrun):
noun = "jobs" # for second run through the loop
which = "salmon"
def check_for_new_results(input_params):
def check_for_new_results(which, cached_battles, cached_jobs, battle_wins, battle_losses, battle_draws, splatfest_wins, splatfest_losses, splatfest_draws, mirror_matches, job_successes, job_failures, isblackout, istestrun):
'''Helper function for monitor_battles(), called every N seconds or when exiting.'''
# INPUT PARAMETERS, IN ORDER:
which = input_params[0] # "ink", "salmon", or "both"
cached_battles = input_params[1] # lists of battles/jobs monitoring mode already knows about
cached_jobs = input_params[2]
battle_wins = input_params[3] # ink battle counters
battle_losses = input_params[4]
battle_draws = input_params[5]
splatfest_wins = input_params[6] # splatfest counters
splatfest_losses = input_params[7]
splatfest_draws = input_params[8]
mirror_matches = input_params[9]
job_successes = input_params[10] # salmon run counters
job_failures = input_params[11]
isblackout = input_params[12] # type of run
istestrun = input_params[13]
# ! fetch from online
# check only numbers (quicker); specific=False since checks recent (latest) only
ink_results, salmon_results = fetch_json(which, separate=True, numbers_only=True)
@@ -1085,8 +1325,8 @@ def check_for_new_results(input_params):
if num not in cached_jobs:
# get the full job data
result_post = requests.post(utils.GRAPHQL_URL,
data=utils.gen_graphql_body(utils.translate_rid["CoopHistoryDetailQuery"], "coopResultId", num),
headers=headbutt(),
data=utils.gen_graphql_body(utils.translate_rid["CoopHistoryDetailQuery"], "coopHistoryDetailId", num),
headers=headbutt(forcelang='en-US'),
cookies=dict(_gtoken=GTOKEN))
result = json.loads(result_post.text)
@@ -1103,8 +1343,7 @@ def check_for_new_results(input_params):
stagename = result["data"]["coopHistoryDetail"]["coopStage"]["name"]
shortname = stagename.split(" ")[-1] # fine for salmon run stage names too
endtime = utils.epoch_time(result["data"]["coopHistoryDetail"]["playedTime"]) + \
result["data"]["coopHistoryDetail"]["duration"]
endtime = utils.epoch_time(result["data"]["coopHistoryDetail"]["playedTime"])
dt = datetime.datetime.fromtimestamp(endtime).strftime('%I:%M:%S %p').lstrip("0")
print(f"New job result detected at {dt}! ({shortname}, {outcome})")
@@ -1153,7 +1392,7 @@ def monitor_battles(which, secs, isblackout, istestrun, skipprefetch):
job_successes, job_failures,
isblackout, istestrun
]
which, cached_battles, cached_jobs, battle_wins, battle_losses, battle_draws, splatfest_wins, splatfest_losses, splatfest_draws, mirror_matches, job_successes, job_failures, foundany = check_for_new_results(input_params)
which, cached_battles, cached_jobs, battle_wins, battle_losses, battle_draws, splatfest_wins, splatfest_losses, splatfest_draws, mirror_matches, job_successes, job_failures, foundany = check_for_new_results(*input_params)
except KeyboardInterrupt:
print(f"\n\nChecking to see if there are unuploaded {utils.set_noun(which)} before exiting...")
@@ -1166,7 +1405,7 @@ def monitor_battles(which, secs, isblackout, istestrun, skipprefetch):
job_successes, job_failures,
isblackout, istestrun
]
which, cached_battles, cached_jobs, battle_wins, battle_losses, battle_draws, splatfest_wins, splatfest_losses, splatfest_draws, mirror_matches, job_successes, job_failures, foundany = check_for_new_results(input_params)
which, cached_battles, cached_jobs, battle_wins, battle_losses, battle_draws, splatfest_wins, splatfest_losses, splatfest_draws, mirror_matches, job_successes, job_failures, foundany = check_for_new_results(*input_params)
noun = utils.set_noun(which)
if foundany:
@@ -1197,10 +1436,9 @@ def monitor_battles(which, secs, isblackout, istestrun, skipprefetch):
print(f"{mirror_matches} mirror match{'' if mirror_matches == 1 else 'es'} against your Splatfest team.")
# SR TODO
# if which in ("salmon", "both"):
# print(f"Salmon Run: {job_successes} success{'' if job_successes == 1 else 'es'} and " \
# f"{job_failures} failure{'' if job_failures == 1 else 's'}.")
if which in ("salmon", "both"):
print(f"Salmon Run: {job_successes} success{'' if job_successes == 1 else 'es'} and " \
f"{job_failures} failure{'' if job_failures == 1 else 's'}.")
print("Bye!")
@@ -1228,6 +1466,13 @@ class SquidProgress:
def export_seed_json(skipprefetch=False):
'''Export a JSON file for use with Lean's seed checker at https://leanny.github.io/splat3seedchecker/.'''
try:
import pymmh3 as mmh3
except ModuleNotFoundError:
print("This function requires a Python module you don't have installed. " \
"Please run " + '`\033[91m' + "pip install -r requirements.txt" + '\033[0m`' + " and try again.")
sys.exit(1)
if not skipprefetch:
prefetch_checks(printout=True)
@@ -1304,7 +1549,7 @@ def parse_arguments():
parser.add_argument("-o", required=False, action="store_true",
help="export all possible results to local files")
parser.add_argument("-i", dest="file", nargs=2, required=False,
help="upload local results; use `-i results.json overview.json`")
help="upload local results: `-i (coop_)results.json overview.json`")
parser.add_argument("-t", required=False, action="store_true",
help="dry run for testing (won't post to stat.ink)")
parser.add_argument("--getseed", required=False, action="store_true",
@@ -1410,9 +1655,11 @@ def main():
# manual json upload: -i flag
#############################
if filenames: # 2 files in list
if os.path.basename(filenames[0]) != "results.json" or os.path.basename(filenames[1]) != "overview.json":
if (os.path.basename(filenames[0]) != "results.json" \
and os.path.basename(filenames[0]) != "coop_results.json") \
or os.path.basename(filenames[1]) != "overview.json":
print("Must use the format " \
'\033[91m' + "-i path/to/results.json path/to/overview.json" + '\033[0m' + ". Exiting.")
'\033[91m' + "-i path/to/(coop_)results.json path/to/overview.json" + '\033[0m' + ". Exiting.")
sys.exit(1)
for filename in filenames:
if not os.path.exists(filename):
@@ -1422,7 +1669,7 @@ def main():
try:
data = json.load(data_file)
except ValueError:
print("Could not decode JSON object in results.json.")
print(f"Could not decode JSON object in {os.path.basename(filenames[0])}.")
sys.exit(1)
with open(filenames[1]) as data_file:
try:
@@ -1434,28 +1681,41 @@ def main():
# only upload unuploaded results
auth = {'Authorization': f'Bearer {API_KEY}'}
resp = requests.get("https://stat.ink/api/v3/s3s/uuid-list", headers=auth)
resp_b = requests.get("https://stat.ink/api/v3/s3s/uuid-list", headers=auth)
resp_j = requests.get("https://stat.ink/api/v3/salmon/uuid-list", headers=auth)
try:
statink_uploads = json.loads(resp.text)
statink_uploads = json.loads(resp_b.text)
statink_uploads.extend(json.loads(resp_j.text))
except:
print(f"Encountered an error while checking recently-uploaded data. Is stat.ink down?")
sys.exit(1)
to_upload = []
for battle in data:
if battle["data"]["vsHistoryDetail"] is not None:
full_id = utils.b64d(battle["data"]["vsHistoryDetail"]["id"])
old_uuid = full_id[-36:] # not unique because nintendo hates us
new_uuid = str(uuid.uuid5(utils.S3S_NAMESPACE, full_id[-52:]))
for result in data:
try: # ink battle
if result["data"]["vsHistoryDetail"] is not None:
full_id = utils.b64d(result["data"]["vsHistoryDetail"]["id"])
old_uuid = full_id[-36:] # not unique because nintendo hates us
new_uuid = str(uuid.uuid5(utils.S3S_NAMESPACE, full_id[-52:]))
if new_uuid in statink_uploads:
print("Skipping already-uploaded battle.")
continue
if old_uuid in statink_uploads:
if not utils.custom_key_exists("force_uploads", CONFIG_DATA):
if new_uuid in statink_uploads:
print("Skipping already-uploaded battle.")
continue
to_upload.append(battle)
if old_uuid in statink_uploads:
if not utils.custom_key_exists("force_uploads", CONFIG_DATA):
print("Skipping already-uploaded battle.")
continue
to_upload.append(result)
except KeyError: # salmon run job
if result["data"]["coopHistoryDetail"] is not None:
full_id = utils.b64d(result["data"]["coopHistoryDetail"]["id"])
the_uuid = str(uuid.uuid5(utils.SALMON_NAMESPACE, full_id[-52:]))
if the_uuid in statink_uploads:
print("Skipping already-uploaded job.")
continue
to_upload.append(result)
post_result(to_upload, False, blackout, test_run, overview_data=overview_file) # one or multiple; monitoring mode = False
sys.exit(0)
@@ -1468,7 +1728,10 @@ def main():
update_salmon_profile()
if check_old:
check_if_missing(which, blackout, test_run) # monitoring mode hasn't begun yet
if which == "both":
prefetch_checks(printout=True)
skipprefetch = True
check_if_missing(which, blackout, test_run, skipprefetch) # monitoring mode hasn't begun yet
print()
if secs != -1: # monitoring mode