forked from frozenpandaman/splatnet2statink
-
Notifications
You must be signed in to change notification settings - Fork 0
/
salmonrun.py
340 lines (283 loc) · 13.5 KB
/
salmonrun.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
# eli fessler
from __future__ import print_function
from builtins import input
import requests, json, uuid
import dbs
version = "unknown"
cookie = ""
api_key = ""
app_head = {}
def salmon_load_json():
'''Returns Salmon Run summary JSON from online.'''
print("Pulling Salmon Run data from online...")
url = "https://app.splatoon2.nintendo.net/api/coop_results"
results_list = requests.get(url, headers=app_head, cookies=dict(iksm_session=cookie))
return json.loads(results_list.text)
def salmon_post_profile(profile):
''' Updates stat.ink Salmon Run stats/profile.'''
payload = {
"work_count": profile["card"]["job_num"],
"total_golden_eggs": profile["card"]["golden_ikura_total"],
"total_eggs": profile["card"]["ikura_total"],
"total_rescued": profile["card"]["help_total"],
"total_point": profile["card"]["kuma_point_total"]
}
url = 'https://stat.ink/api/v2/salmon-stats'
auth = {'Authorization': 'Bearer {}'.format(api_key)}
updateprofile = requests.post(url, headers=auth, data=payload)
if updateprofile.ok:
print("Successfully updated your Salmon Run profile.")
else:
print("Could not update your Salmon Run profile. Error from stat.ink:")
print(updateprofile.text)
def set_teammates(payload, job_id):
'''Returns a new payload with the teammates key present.'''
url = "https://app.splatoon2.nintendo.net/api/coop_results/{}".format(job_id)
job = requests.get(url, headers=app_head, cookies=dict(iksm_session=cookie))
results = json.loads(job.text)
try:
results["other_results"] # only present in shift jsons
except KeyError:
print("Problem retrieving shift details. Continuing without teammates' scoreboard statistics.")
return payload # same payload as passed in, no modifications
payload["teammates"] = []
num_teammates = len(results["other_results"])
translate_specials = {2: 'pitcher', 7: 'presser', 8: 'jetpack', 9: 'chakuchi'}
for num in range(num_teammates):
payload["teammates"].append({})
# Principal ID & nickname
payload["teammates"][num]["splatnet_id"] = results["other_results"][num]["pid"]
payload["teammates"][num]["name"] = results["other_results"][num]["name"]
# Special weapon
payload["teammates"][num]["special"] = translate_specials[int(results["other_results"][num]["special"]["id"])]
# Rescues, deaths, egg stats
payload["teammates"][num]["rescue"] = results["other_results"][num]["help_count"]
payload["teammates"][num]["death"] = results["other_results"][num]["dead_count"]
payload["teammates"][num]["golden_egg_delivered"] = results["other_results"][num]["golden_ikura_num"]
payload["teammates"][num]["power_egg_collected"] = results["other_results"][num]["ikura_num"]
# Special uses, main weapon
weapon_list = results["other_results"][num]["weapon_list"]
payload["teammates"][num]["special_uses"] = results["other_results"][num]["special_counts"] # list
payload["teammates"][num]["weapons"] = [dbs.weapons.get(int(d["id"]), None) for d in weapon_list]
# Boss kills
boss_kills = {}
boss_kills["goldie"] = results["other_results"][num]["boss_kill_counts"]["3"]["count"]
boss_kills["steelhead"] = results["other_results"][num]["boss_kill_counts"]["6"]["count"]
boss_kills["flyfish"] = results["other_results"][num]["boss_kill_counts"]["9"]["count"]
boss_kills["scrapper"] = results["other_results"][num]["boss_kill_counts"]["12"]["count"]
boss_kills["steel_eel"] = results["other_results"][num]["boss_kill_counts"]["13"]["count"]
boss_kills["stinger"] = results["other_results"][num]["boss_kill_counts"]["14"]["count"]
boss_kills["maws"] = results["other_results"][num]["boss_kill_counts"]["15"]["count"]
boss_kills["griller"] = results["other_results"][num]["boss_kill_counts"]["16"]["count"]
boss_kills["drizzler"] = results["other_results"][num]["boss_kill_counts"]["21"]["count"]
payload["teammates"][num]["boss_kills"] = boss_kills
return payload # return modified payload w/ teammates key
def salmon_post_shift(i, results):
'''Uploads shift #i from the provided results dictionary.'''
payload = {'agent': 'splatnet2statink', 'agent_version': version, 'automated': 'yes'}
################
# PAYLOAD ROOT #
################
job_id = results[i]["job_id"]
payload["splatnet_number"] = job_id
# stat.ink UUID
principal_id = results[i]["my_result"]["pid"]
namespace = uuid.UUID(u'{418fe150-cb33-11e8-8816-d050998473ba}')
name = "{}@{}".format(job_id, principal_id)
payload["uuid"] = str(uuid.uuid5(namespace, name))
# Title
title_num = int(results[i]["grade"]["id"])
translate_titles = {5: 'profreshional', 4: 'overachiever', 3: 'go_getter', 2: 'part_timer', 1: 'apprentice'}
payload["title_after"] = translate_titles[title_num]
title_exp_after = results[i]["grade_point"]
title_exp_delta = results[i]["grade_point_delta"] # positive for win, negative for loss
title_exp = title_exp_after - title_exp_delta
payload["title_exp_after"] = title_exp_after
payload["title_exp"] = title_exp
if title_exp_after == 40 and title_exp_delta == 20:
pass # could be legit clear 20->40, or could be rank up ?->40
elif title_exp_after == 40 and title_exp_delta < 0 and title_num != 5:
pass # could be legit wave 1 fail 60->40, or could be rank down ?->40; not always -20 for loss
elif title_exp_after == 999:
pass # ? -> 999; not always 20 for gain
else: # rank/title did not change
payload["title"] = translate_titles[title_num]
# Stage
stage_img_url = results[i]["schedule"]["stage"]["image"]
if "6d68f5baa75f3a94e5e9bfb89b82e7377e3ecd2c" in stage_img_url:
payload["stage"] = "shaketoba"
elif "e07d73b7d9f0c64e552b34a2e6c29b8564c63388" in stage_img_url:
payload["stage"] = "donburako"
elif "e9f7c7b35e6d46778cd3cbc0d89bd7e1bc3be493" in stage_img_url:
payload["stage"] = "tokishirazu"
elif "65c68c6f0641cc5654434b78a6f10b0ad32ccdee" in stage_img_url:
payload["stage"] = "dam"
elif "50064ec6e97aac91e70df5fc2cfecf61ad8615fd" in stage_img_url:
payload["stage"] = "polaris"
# Hazard level
payload["danger_rate"] = results[i]["danger_rate"]
# Boss appearances/count
num_of_bosses = {}
num_of_bosses["goldie"] = results[i]["boss_counts"]["3"]["count"]
num_of_bosses["steelhead"] = results[i]["boss_counts"]["6"]["count"]
num_of_bosses["flyfish"] = results[i]["boss_counts"]["9"]["count"]
num_of_bosses["scrapper"] = results[i]["boss_counts"]["12"]["count"]
num_of_bosses["steel_eel"] = results[i]["boss_counts"]["13"]["count"]
num_of_bosses["stinger"] = results[i]["boss_counts"]["14"]["count"]
num_of_bosses["maws"] = results[i]["boss_counts"]["15"]["count"]
num_of_bosses["griller"] = results[i]["boss_counts"]["16"]["count"]
num_of_bosses["drizzler"] = results[i]["boss_counts"]["21"]["count"]
payload["boss_appearances"] = num_of_bosses
# Number of waves played
num_waves = len(results[i]["wave_details"])
cleared = results[i]["job_result"]["is_clear"]
payload["clear_waves"] = 3 if cleared else num_waves - 1
payload["fail_reason"] = results[i]["job_result"]["failure_reason"]
# Time
payload["shift_start_at"] = results[i]["start_time"] # rotation start time
payload["start_at"] = results[i]["play_time"] # job/shift start time
##############
# WAVES LIST #
##############
payload["waves"] = []
for wave in range(num_waves):
payload["waves"].append({})
# Known Occurrence
# cohock_charge, fog, goldie_seeking, griller, mothership, rush
event = results[i]["wave_details"][wave]["event_type"]["key"].replace("the-", "", 1).replace("-", "_")
if event != "water_levels":
payload["waves"][wave]["known_occurrence"] = event
# Water level
payload["waves"][wave]["water_level"] = results[i]["wave_details"][wave]["water_level"]["key"] # low, normal, high
# Eggs
payload["waves"][wave]["golden_egg_quota"] = results[i]["wave_details"][wave]["quota_num"]
payload["waves"][wave]["golden_egg_appearances"] = results[i]["wave_details"][wave]["golden_ikura_pop_num"]
payload["waves"][wave]["golden_egg_delivered"] = results[i]["wave_details"][wave]["golden_ikura_num"]
payload["waves"][wave]["power_egg_collected"] = results[i]["wave_details"][wave]["ikura_num"]
#################
# PLAYER'S DATA #
#################
payload["my_data"] = {}
# Principal ID & nickname
payload["my_data"]["splatnet_id"] = principal_id
payload["my_data"]["name"] = results[i]["my_result"]["name"]
# Special weapon
translate_specials = {2: 'pitcher', 7: 'presser', 8: 'jetpack', 9: 'chakuchi'}
payload["my_data"]["special"] = translate_specials[int(results[i]["my_result"]["special"]["id"])]
# Rescues, deaths, egg stats
payload["my_data"]["rescue"] = results[i]["my_result"]["help_count"]
payload["my_data"]["death"] = results[i]["my_result"]["dead_count"]
payload["my_data"]["golden_egg_delivered"] = results[i]["my_result"]["golden_ikura_num"]
payload["my_data"]["power_egg_collected"] = results[i]["my_result"]["ikura_num"]
# Species, gender
payload["my_data"]["species"] = results[i]["player_type"]["species"][:-1] # inklings -> inkling
payload["my_data"]["gender"] = results[i]["player_type"]["style"]
# Special uses, main weapon
weapon_list = results[i]["my_result"]["weapon_list"]
payload["my_data"]["special_uses"] = results[i]["my_result"]["special_counts"] # list
payload["my_data"]["weapons"] = [dbs.weapons.get(int(d["id"]), None) for d in weapon_list]
# Boss kills
boss_kills = {}
boss_kills["goldie"] = results[i]["my_result"]["boss_kill_counts"]["3"]["count"]
boss_kills["steelhead"] = results[i]["my_result"]["boss_kill_counts"]["6"]["count"]
boss_kills["flyfish"] = results[i]["my_result"]["boss_kill_counts"]["9"]["count"]
boss_kills["scrapper"] = results[i]["my_result"]["boss_kill_counts"]["12"]["count"]
boss_kills["steel_eel"] = results[i]["my_result"]["boss_kill_counts"]["13"]["count"]
boss_kills["stinger"] = results[i]["my_result"]["boss_kill_counts"]["14"]["count"]
boss_kills["maws"] = results[i]["my_result"]["boss_kill_counts"]["15"]["count"]
boss_kills["griller"] = results[i]["my_result"]["boss_kill_counts"]["16"]["count"]
boss_kills["drizzler"] = results[i]["my_result"]["boss_kill_counts"]["21"]["count"]
payload["my_data"]["boss_kills"] = boss_kills
#########################
# TEAMMATES LIST & DATA #
#########################
payload = set_teammates(payload, job_id)
#************
#*** POST ***
#************
url = 'https://stat.ink/api/v2/salmon'
auth = {'Authorization': 'Bearer {}'.format(api_key), 'Content-Type': 'application/json'}
postshift = requests.post(url, headers=auth, data=json.dumps(payload), allow_redirects=False)
# Response
headerloc = postshift.headers.get('location')
if headerloc != None:
if postshift.status_code == 302: # receive redirect
print("Shift #{} already uploaded to {}".format(i+1, headerloc))
# continue trying to upload remaining
else: # http status code should be OK (200)
print("Shift #{} uploaded to {}".format(i+1, headerloc))
else: # error of some sort
print("Error uploading shift #{}. Message from server:".format(i+1))
print(postshift.content.decode("utf-8"))
if i != 0: # don't prompt for final shift
cont = input('Continue? [Y/n] ')
if cont[0].lower() == "n":
print("Exiting.")
exit(1)
def salmon_get_data():
'''Retrieves JSON data from SplatNet.'''
data = salmon_load_json()
if cookie == "" or "code" in data:
print("Blank or invalid cookie. Please run splatnet2statink in non-Salmon Run mode to obtain a cookie.")
exit(1)
try:
profile = data["summary"]
results = data["results"]
except KeyError:
print("Error reading JSON from online.")
exit(1)
return profile, results
def salmon_get_num_shifts(results):
'''Prompts user to upload a certain number of recent shifts.'''
try:
n = int(input("Number of recent Salmon Run shifts to upload (0-50)? "))
except ValueError:
print("Please enter an integer between 0 and 50. Exiting.")
exit(0)
if n < 1:
print("Exiting without uploading any shifts.")
exit(0)
elif n > 50:
print("SplatNet 2 only stores the 50 most recent shifts. Exiting.")
exit(1)
if len(results) == 0:
print("You do not have any Salmon Run shifts recorded on SplatNet 2. Exiting.")
exit(1)
elif n > len(results):
print("You do not have {} Salmon Run shifts recorded on SplatNet 2. Uploading all {}.".format(n, len(results)))
n = len(results)
return n
def get_statink_shifts(api_key):
'''Returns the 100 most recently-uploaded Salmon Run shifts from stat.ink.'''
print("Checking if there are previously-unuploaded shifts...")
url = 'https://stat.ink/api/v2/user-salmon?only=splatnet_number&count=100'
auth = {'Authorization': 'Bearer {}'.format(api_key)}
resp = requests.get(url, headers=auth)
statink_shifts = json.loads(resp.text)
return statink_shifts
def upload_salmon_run(s2s_version, s2s_cookie, s2s_api_key, s2s_app_head, r_flag):
'''Main process for uploading Salmon Run shifts.'''
global version
version = s2s_version
global cookie
cookie = s2s_cookie
global api_key
api_key = s2s_api_key
global app_head
app_head = s2s_app_head
profile, results = salmon_get_data()
salmon_post_profile(profile)
if r_flag: # upload all unuploaded shifts
statink_shifts = get_statink_shifts(s2s_api_key)
new_results = list(filter(lambda r: not(r["job_id"] in statink_shifts), results))
unup_shifts = len(new_results)
if unup_shifts > 0:
print("Previously-unuploaded shifts detected. Uploading now...")
for i in reversed(range(unup_shifts)):
salmon_post_shift(i, new_results)
else:
print("No previously-unuploaded shifts found.")
else: # manually upload a number of shifts
n = salmon_get_num_shifts(results)
for i in reversed(range(n)):
salmon_post_shift(i, results)