poi-plugin-mcp 0.2.16 → 0.2.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +290 -201
- package/index.js +3 -0
- package/lib/bridge-controller.js +75 -1
- package/lib/fleet-metrics.js +249 -0
- package/lib/poi-action-events.js +405 -292
- package/lib/poi-api-responses.js +165 -0
- package/lib/poi-auto-interaction-recorder.js +399 -0
- package/lib/poi-data-query.js +254 -0
- package/lib/poi-http-bridge.js +1673 -1320
- package/lib/poi-input.js +366 -302
- package/lib/poi-interaction-recorder.js +1693 -0
- package/lib/poi-telemetry.js +515 -439
- package/lib/poi-webview-runtime.js +1181 -0
- package/lib/settings-view.js +112 -0
- package/lib/settings.js +8 -0
- package/mcp-server.js +542 -456
- package/package.json +3 -3
package/lib/poi-telemetry.js
CHANGED
|
@@ -1,439 +1,515 @@
|
|
|
1
|
-
const { createPoiActionEvents } = require('./poi-action-events')
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
'/kcsapi/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
'/kcsapi/api_req_kaisou/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
'/kcsapi/
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
'/kcsapi/
|
|
26
|
-
'/kcsapi/
|
|
27
|
-
'/kcsapi/api_req_sortie/
|
|
28
|
-
'/kcsapi/
|
|
29
|
-
'/kcsapi/
|
|
30
|
-
'/kcsapi/
|
|
31
|
-
'/kcsapi/
|
|
32
|
-
'/kcsapi/
|
|
33
|
-
'/kcsapi/api_req_combined_battle/
|
|
34
|
-
'/kcsapi/api_req_combined_battle/
|
|
35
|
-
'/kcsapi/api_req_combined_battle/
|
|
36
|
-
'/kcsapi/api_req_combined_battle/
|
|
37
|
-
'/kcsapi/api_req_combined_battle/
|
|
38
|
-
'/kcsapi/api_req_combined_battle/
|
|
39
|
-
'/kcsapi/api_req_combined_battle/
|
|
40
|
-
'/kcsapi/api_req_combined_battle/
|
|
41
|
-
'/kcsapi/api_req_combined_battle/
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
let
|
|
52
|
-
let
|
|
53
|
-
let
|
|
54
|
-
let
|
|
55
|
-
let
|
|
56
|
-
let
|
|
57
|
-
let
|
|
58
|
-
let
|
|
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
|
-
const
|
|
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
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
api_id
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
return null
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
1
|
+
const { createPoiActionEvents } = require('./poi-action-events')
|
|
2
|
+
const { createPoiApiResponses } = require('./poi-api-responses')
|
|
3
|
+
|
|
4
|
+
const QUEST_LIST_PATH = '/kcsapi/api_get_member/questlist'
|
|
5
|
+
const UNSET_SLOT_PATH = '/kcsapi/api_get_member/unsetslot'
|
|
6
|
+
const EQUIPMENT_SELECTION_PATH = '/kcsapi/api_req_kaisou/slot_select'
|
|
7
|
+
const QUEST_ACTION_PATHS = new Set([
|
|
8
|
+
'/kcsapi/api_req_quest/start',
|
|
9
|
+
'/kcsapi/api_req_quest/stop',
|
|
10
|
+
'/kcsapi/api_req_quest/clearitemget',
|
|
11
|
+
])
|
|
12
|
+
const EQUIPMENT_ACTION_PATHS = new Set([
|
|
13
|
+
'/kcsapi/api_req_kaisou/slotset',
|
|
14
|
+
'/kcsapi/api_req_kaisou/slotset_ex',
|
|
15
|
+
'/kcsapi/api_req_kaisou/slot_deprive',
|
|
16
|
+
'/kcsapi/api_req_kaisou/unsetslot_all',
|
|
17
|
+
])
|
|
18
|
+
const FLEET_ACTION_PATH = '/kcsapi/api_req_hensei/change'
|
|
19
|
+
const BATTLE_RESULT_PATHS = new Set([
|
|
20
|
+
'/kcsapi/api_req_practice/battle_result',
|
|
21
|
+
'/kcsapi/api_req_sortie/battleresult',
|
|
22
|
+
'/kcsapi/api_req_combined_battle/battleresult',
|
|
23
|
+
])
|
|
24
|
+
const BATTLE_PATHS = new Set([
|
|
25
|
+
'/kcsapi/api_req_practice/battle',
|
|
26
|
+
'/kcsapi/api_req_practice/midnight_battle',
|
|
27
|
+
'/kcsapi/api_req_sortie/battle',
|
|
28
|
+
'/kcsapi/api_req_sortie/airbattle',
|
|
29
|
+
'/kcsapi/api_req_sortie/ld_airbattle',
|
|
30
|
+
'/kcsapi/api_req_sortie/ld_shooting',
|
|
31
|
+
'/kcsapi/api_req_battle_midnight/battle',
|
|
32
|
+
'/kcsapi/api_req_battle_midnight/sp_midnight',
|
|
33
|
+
'/kcsapi/api_req_combined_battle/battle',
|
|
34
|
+
'/kcsapi/api_req_combined_battle/battle_water',
|
|
35
|
+
'/kcsapi/api_req_combined_battle/airbattle',
|
|
36
|
+
'/kcsapi/api_req_combined_battle/ld_airbattle',
|
|
37
|
+
'/kcsapi/api_req_combined_battle/ld_shooting',
|
|
38
|
+
'/kcsapi/api_req_combined_battle/ec_battle',
|
|
39
|
+
'/kcsapi/api_req_combined_battle/each_battle',
|
|
40
|
+
'/kcsapi/api_req_combined_battle/each_battle_water',
|
|
41
|
+
'/kcsapi/api_req_combined_battle/midnight_battle',
|
|
42
|
+
'/kcsapi/api_req_combined_battle/sp_midnight',
|
|
43
|
+
'/kcsapi/api_req_combined_battle/ec_midnight_battle',
|
|
44
|
+
'/kcsapi/api_req_combined_battle/ec_night_to_day',
|
|
45
|
+
])
|
|
46
|
+
|
|
47
|
+
function createPoiTelemetry(options = {}) {
|
|
48
|
+
const now = options.now || (() => new Date())
|
|
49
|
+
const actionEvents = createPoiActionEvents({ now })
|
|
50
|
+
const apiResponses = createPoiApiResponses({ now })
|
|
51
|
+
let questGeneration = 0
|
|
52
|
+
let questList = null
|
|
53
|
+
let questActionGeneration = 0
|
|
54
|
+
let questAction = null
|
|
55
|
+
let equipmentActionGeneration = 0
|
|
56
|
+
let equipmentAction = null
|
|
57
|
+
let equipmentSelectionGeneration = 0
|
|
58
|
+
let equipmentSelection = null
|
|
59
|
+
let unsetSlotGeneration = 0
|
|
60
|
+
let unsetSlot = null
|
|
61
|
+
let fleetActionGeneration = 0
|
|
62
|
+
let fleetAction = null
|
|
63
|
+
let battleGeneration = 0
|
|
64
|
+
let battleTelemetry = null
|
|
65
|
+
|
|
66
|
+
function handleGameResponse(event) {
|
|
67
|
+
const detail = event && event.detail
|
|
68
|
+
if (!detail || typeof detail.path !== 'string') return
|
|
69
|
+
const actionEvent = actionEvents.capture(detail)
|
|
70
|
+
apiResponses.capture(detail, actionEvent && actionEvent.capturedAt)
|
|
71
|
+
|
|
72
|
+
if (detail.path === QUEST_LIST_PATH) {
|
|
73
|
+
captureQuestList(detail, actionEvent)
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
if (detail.path === UNSET_SLOT_PATH) {
|
|
77
|
+
captureUnsetSlot(detail, actionEvent)
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
if (detail.path === EQUIPMENT_SELECTION_PATH) {
|
|
81
|
+
captureEquipmentSelection(detail, actionEvent)
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
if (QUEST_ACTION_PATHS.has(detail.path)) {
|
|
85
|
+
captureQuestAction(detail, actionEvent)
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
if (EQUIPMENT_ACTION_PATHS.has(detail.path)) {
|
|
89
|
+
captureEquipmentAction(detail, actionEvent)
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
if (detail.path === FLEET_ACTION_PATH) {
|
|
93
|
+
captureFleetAction(detail, actionEvent)
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
if (BATTLE_RESULT_PATHS.has(detail.path)) {
|
|
97
|
+
captureBattleResult(detail)
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
if (BATTLE_PATHS.has(detail.path)) {
|
|
101
|
+
captureBattlePacket(detail)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function captureQuestList(detail, actionEvent) {
|
|
106
|
+
const body = detail.body
|
|
107
|
+
const postBody = detail.postBody
|
|
108
|
+
const tabId = toInteger(postBody && postBody.api_tab_id)
|
|
109
|
+
if (
|
|
110
|
+
!body ||
|
|
111
|
+
!Array.isArray(body.api_list) ||
|
|
112
|
+
tabId == null ||
|
|
113
|
+
tabId < 0 ||
|
|
114
|
+
tabId > 9
|
|
115
|
+
) {
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const quests = body.api_list.flatMap((quest, index) => {
|
|
120
|
+
if (!quest || typeof quest !== 'object' || !Number.isInteger(quest.api_no)) {
|
|
121
|
+
return []
|
|
122
|
+
}
|
|
123
|
+
return [{ ...quest, listIndex: index + 1 }]
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
questGeneration += 1
|
|
127
|
+
questList = {
|
|
128
|
+
available: true,
|
|
129
|
+
generation: questGeneration,
|
|
130
|
+
capturedAt: actionEvent
|
|
131
|
+
? actionEvent.capturedAt
|
|
132
|
+
: now().toISOString(),
|
|
133
|
+
tabId,
|
|
134
|
+
pageNo: nonNegativeInteger(body.api_disp_page),
|
|
135
|
+
pageCount: nonNegativeInteger(body.api_page_count),
|
|
136
|
+
count: nonNegativeInteger(body.api_count),
|
|
137
|
+
execCount: nonNegativeInteger(body.api_exec_count),
|
|
138
|
+
execType: nonNegativeInteger(body.api_exec_type),
|
|
139
|
+
quests,
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function captureQuestAction(detail, actionEvent) {
|
|
144
|
+
const apiResult = actionEvent
|
|
145
|
+
? actionEvent.apiResult
|
|
146
|
+
: equipmentApiResult(detail)
|
|
147
|
+
if (apiResult !== 1) return
|
|
148
|
+
const postBody = detail.postBody
|
|
149
|
+
const questId = toInteger(postBody && postBody.api_quest_id)
|
|
150
|
+
if (questId == null || questId <= 0) return
|
|
151
|
+
|
|
152
|
+
questActionGeneration += 1
|
|
153
|
+
questAction = {
|
|
154
|
+
available: true,
|
|
155
|
+
generation: questActionGeneration,
|
|
156
|
+
capturedAt: actionEvent
|
|
157
|
+
? actionEvent.capturedAt
|
|
158
|
+
: now().toISOString(),
|
|
159
|
+
path: actionEvent ? actionEvent.path : detail.path,
|
|
160
|
+
questId,
|
|
161
|
+
flag: toInteger(postBody && postBody.api_quest_flag),
|
|
162
|
+
selectedKind: selectedKindOf(postBody),
|
|
163
|
+
apiResult,
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function captureEquipmentAction(detail, actionEvent) {
|
|
168
|
+
const apiResult = actionEvent
|
|
169
|
+
? actionEvent.apiResult
|
|
170
|
+
: equipmentApiResult(detail)
|
|
171
|
+
if (apiResult === 0) return
|
|
172
|
+
const postBody = normalizeEquipmentPostBody(detail.path, detail.postBody)
|
|
173
|
+
if (!postBody) return
|
|
174
|
+
|
|
175
|
+
equipmentActionGeneration += 1
|
|
176
|
+
equipmentAction = {
|
|
177
|
+
available: true,
|
|
178
|
+
generation: equipmentActionGeneration,
|
|
179
|
+
capturedAt: actionEvent
|
|
180
|
+
? actionEvent.capturedAt
|
|
181
|
+
: now().toISOString(),
|
|
182
|
+
path: actionEvent ? actionEvent.path : detail.path,
|
|
183
|
+
apiResult,
|
|
184
|
+
postBody,
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function captureUnsetSlot(detail, actionEvent) {
|
|
189
|
+
const bySlotType = normalizeUnsetSlotBody(detail.body)
|
|
190
|
+
if (bySlotType == null) return
|
|
191
|
+
|
|
192
|
+
unsetSlotGeneration += 1
|
|
193
|
+
unsetSlot = {
|
|
194
|
+
available: true,
|
|
195
|
+
generation: unsetSlotGeneration,
|
|
196
|
+
capturedAt: actionEvent
|
|
197
|
+
? actionEvent.capturedAt
|
|
198
|
+
: now().toISOString(),
|
|
199
|
+
bySlotType,
|
|
200
|
+
equipmentIds: Object.values(bySlotType)
|
|
201
|
+
.flatMap((groups) => Object.values(groups))
|
|
202
|
+
.flat(),
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function captureEquipmentSelection(detail, actionEvent) {
|
|
207
|
+
const equipmentIds = normalizeEquipmentSelectionBody(detail.body)
|
|
208
|
+
if (equipmentIds == null) return
|
|
209
|
+
const postBody = normalizeEquipmentSelectionPostBody(detail.postBody)
|
|
210
|
+
if (!postBody) return
|
|
211
|
+
|
|
212
|
+
equipmentSelectionGeneration += 1
|
|
213
|
+
equipmentSelection = {
|
|
214
|
+
available: true,
|
|
215
|
+
generation: equipmentSelectionGeneration,
|
|
216
|
+
capturedAt: actionEvent
|
|
217
|
+
? actionEvent.capturedAt
|
|
218
|
+
: now().toISOString(),
|
|
219
|
+
path: actionEvent ? actionEvent.path : detail.path,
|
|
220
|
+
postBody,
|
|
221
|
+
equipmentIds,
|
|
222
|
+
count: equipmentIds.length,
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function captureFleetAction(detail, actionEvent) {
|
|
227
|
+
const apiResult = actionEvent
|
|
228
|
+
? actionEvent.apiResult
|
|
229
|
+
: equipmentApiResult(detail)
|
|
230
|
+
if (apiResult === 0) return
|
|
231
|
+
const postBody = normalizeFleetPostBody(detail.postBody)
|
|
232
|
+
if (!postBody) return
|
|
233
|
+
|
|
234
|
+
fleetActionGeneration += 1
|
|
235
|
+
fleetAction = {
|
|
236
|
+
available: true,
|
|
237
|
+
generation: fleetActionGeneration,
|
|
238
|
+
capturedAt: actionEvent
|
|
239
|
+
? actionEvent.capturedAt
|
|
240
|
+
: now().toISOString(),
|
|
241
|
+
path: actionEvent ? actionEvent.path : detail.path,
|
|
242
|
+
apiResult,
|
|
243
|
+
postBody,
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function captureBattlePacket(detail) {
|
|
248
|
+
if (!battleTelemetry || battleTelemetry.status === 'settled') {
|
|
249
|
+
battleGeneration += 1
|
|
250
|
+
}
|
|
251
|
+
battleTelemetry = {
|
|
252
|
+
available: true,
|
|
253
|
+
generation: battleGeneration,
|
|
254
|
+
status: 'in_progress',
|
|
255
|
+
observed: {
|
|
256
|
+
capturedAt: now().toISOString(),
|
|
257
|
+
path: detail.path,
|
|
258
|
+
time: finiteOrNull(detail.time),
|
|
259
|
+
phaseStartHp: {
|
|
260
|
+
friendlyMain: numberArray(detail.body && detail.body.api_f_nowhps),
|
|
261
|
+
friendlyEscort: numberArray(
|
|
262
|
+
detail.body && detail.body.api_f_nowhps_combined,
|
|
263
|
+
),
|
|
264
|
+
enemyMain: numberArray(detail.body && detail.body.api_e_nowhps),
|
|
265
|
+
enemyEscort: numberArray(
|
|
266
|
+
detail.body && detail.body.api_e_nowhps_combined,
|
|
267
|
+
),
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
official: null,
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function captureBattleResult(detail) {
|
|
275
|
+
if (!battleTelemetry) battleGeneration += 1
|
|
276
|
+
const body = detail.body && typeof detail.body === 'object'
|
|
277
|
+
? detail.body
|
|
278
|
+
: {}
|
|
279
|
+
battleTelemetry = {
|
|
280
|
+
available: true,
|
|
281
|
+
generation: battleGeneration,
|
|
282
|
+
status: 'settled',
|
|
283
|
+
observed: battleTelemetry ? battleTelemetry.observed : null,
|
|
284
|
+
official: {
|
|
285
|
+
capturedAt: now().toISOString(),
|
|
286
|
+
path: detail.path,
|
|
287
|
+
time: finiteOrNull(detail.time),
|
|
288
|
+
rank: typeof body.api_win_rank === 'string' ? body.api_win_rank : null,
|
|
289
|
+
mvpPosition: {
|
|
290
|
+
main: positiveIntegerOrNull(body.api_mvp),
|
|
291
|
+
escort: positiveIntegerOrNull(body.api_mvp_combined),
|
|
292
|
+
},
|
|
293
|
+
drop: {
|
|
294
|
+
ship: objectOrNull(body.api_get_ship),
|
|
295
|
+
useItem: objectOrNull(body.api_get_useitem),
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return {
|
|
302
|
+
handleGameResponse,
|
|
303
|
+
getQuestList() {
|
|
304
|
+
return questList || { available: false, generation: 0 }
|
|
305
|
+
},
|
|
306
|
+
getQuestAction() {
|
|
307
|
+
return questAction || { available: false, generation: 0 }
|
|
308
|
+
},
|
|
309
|
+
getEquipmentAction() {
|
|
310
|
+
return equipmentAction || { available: false, generation: 0 }
|
|
311
|
+
},
|
|
312
|
+
getEquipmentSelection() {
|
|
313
|
+
return equipmentSelection || { available: false, generation: 0 }
|
|
314
|
+
},
|
|
315
|
+
getUnsetSlot() {
|
|
316
|
+
return unsetSlot || { available: false, generation: 0 }
|
|
317
|
+
},
|
|
318
|
+
getFleetAction() {
|
|
319
|
+
return fleetAction || { available: false, generation: 0 }
|
|
320
|
+
},
|
|
321
|
+
getActionEvents(options) {
|
|
322
|
+
return actionEvents.read(options)
|
|
323
|
+
},
|
|
324
|
+
getActionEventsWait(options) {
|
|
325
|
+
return actionEvents.wait(options)
|
|
326
|
+
},
|
|
327
|
+
getApiResponses(options) {
|
|
328
|
+
return apiResponses.read(options)
|
|
329
|
+
},
|
|
330
|
+
getBattleTelemetry() {
|
|
331
|
+
return battleTelemetry || { available: false, generation: 0 }
|
|
332
|
+
},
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function normalizeEquipmentSelectionBody(body) {
|
|
337
|
+
if (!body || typeof body !== 'object' || Array.isArray(body)) return null
|
|
338
|
+
const source = body.api_data &&
|
|
339
|
+
typeof body.api_data === 'object' &&
|
|
340
|
+
!Array.isArray(body.api_data)
|
|
341
|
+
? body.api_data
|
|
342
|
+
: body
|
|
343
|
+
if (!Array.isArray(source.api_slotitem)) return null
|
|
344
|
+
const ids = source.api_slotitem
|
|
345
|
+
.map((item) => item && typeof item === 'object' && !Array.isArray(item)
|
|
346
|
+
? positiveIntegerOrNull(item.api_id)
|
|
347
|
+
: null)
|
|
348
|
+
if (ids.some((id) => id == null) || ids.length > 10000) return null
|
|
349
|
+
return ids
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function normalizeEquipmentSelectionPostBody(postBody) {
|
|
353
|
+
if (!postBody || typeof postBody !== 'object') return null
|
|
354
|
+
const shipId = positiveIntegerOrNull(postBody.api_id)
|
|
355
|
+
const slotIndex = boundedInteger(postBody.api_slot_idx, 0, 5)
|
|
356
|
+
if (shipId == null || slotIndex == null) return null
|
|
357
|
+
return {
|
|
358
|
+
api_id: shipId,
|
|
359
|
+
api_slot_idx: slotIndex,
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function normalizeUnsetSlotBody(body) {
|
|
364
|
+
if (!body || typeof body !== 'object' || Array.isArray(body)) return null
|
|
365
|
+
const source = body.api_data &&
|
|
366
|
+
typeof body.api_data === 'object' &&
|
|
367
|
+
!Array.isArray(body.api_data)
|
|
368
|
+
? body.api_data
|
|
369
|
+
: body
|
|
370
|
+
const output = {}
|
|
371
|
+
let totalIds = 0
|
|
372
|
+
|
|
373
|
+
for (const [slotType, groups] of Object.entries(source)) {
|
|
374
|
+
if (!/^api_slottype\d+$/.test(slotType)) continue
|
|
375
|
+
if (!groups || typeof groups !== 'object' || Array.isArray(groups)) continue
|
|
376
|
+
const normalizedGroups = {}
|
|
377
|
+
for (const [groupId, ids] of Object.entries(groups)) {
|
|
378
|
+
if (!/^\d+$/.test(groupId) || !Array.isArray(ids)) continue
|
|
379
|
+
const normalizedIds = ids
|
|
380
|
+
.map(positiveIntegerOrNull)
|
|
381
|
+
.filter((id) => id != null)
|
|
382
|
+
totalIds += normalizedIds.length
|
|
383
|
+
if (totalIds > 10000) return null
|
|
384
|
+
normalizedGroups[groupId] = normalizedIds
|
|
385
|
+
}
|
|
386
|
+
if (Object.keys(normalizedGroups).length > 0) {
|
|
387
|
+
output[slotType] = normalizedGroups
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return Object.keys(output).length > 0 ? output : null
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function normalizeFleetPostBody(postBody) {
|
|
394
|
+
if (!postBody || typeof postBody !== 'object') return null
|
|
395
|
+
const fleetId = boundedInteger(postBody.api_id, 1, 4)
|
|
396
|
+
const shipIndex = boundedInteger(postBody.api_ship_idx, 0, 5)
|
|
397
|
+
const shipId = boundedInteger(postBody.api_ship_id, -1)
|
|
398
|
+
if (fleetId == null || shipIndex == null || shipId == null) return null
|
|
399
|
+
return {
|
|
400
|
+
api_id: fleetId,
|
|
401
|
+
api_ship_idx: shipIndex,
|
|
402
|
+
api_ship_id: shipId,
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function normalizeEquipmentPostBody(path, postBody) {
|
|
407
|
+
if (!postBody || typeof postBody !== 'object') return null
|
|
408
|
+
if (path === '/kcsapi/api_req_kaisou/slotset') {
|
|
409
|
+
const shipId = positiveIntegerOrNull(postBody.api_id)
|
|
410
|
+
const slotIndex = boundedInteger(postBody.api_slot_idx, 0, 4)
|
|
411
|
+
const itemId = boundedInteger(postBody.api_item_id, -1)
|
|
412
|
+
if (shipId == null || slotIndex == null || itemId == null) return null
|
|
413
|
+
return {
|
|
414
|
+
api_id: shipId,
|
|
415
|
+
api_slot_idx: slotIndex,
|
|
416
|
+
api_item_id: itemId,
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (path === '/kcsapi/api_req_kaisou/slotset_ex') {
|
|
420
|
+
const shipId = positiveIntegerOrNull(postBody.api_id)
|
|
421
|
+
const itemId = boundedInteger(postBody.api_item_id, -1)
|
|
422
|
+
if (shipId == null || itemId == null) return null
|
|
423
|
+
return {
|
|
424
|
+
api_id: shipId,
|
|
425
|
+
api_item_id: itemId,
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
if (path === '/kcsapi/api_req_kaisou/slot_deprive') {
|
|
429
|
+
const setShip = positiveIntegerOrNull(postBody.api_set_ship)
|
|
430
|
+
const unsetShip = positiveIntegerOrNull(postBody.api_unset_ship)
|
|
431
|
+
const setIndex = boundedInteger(postBody.api_set_idx, -1, 4)
|
|
432
|
+
const unsetIndex = boundedInteger(postBody.api_unset_idx, -1, 4)
|
|
433
|
+
if (
|
|
434
|
+
setShip == null ||
|
|
435
|
+
unsetShip == null ||
|
|
436
|
+
setIndex == null ||
|
|
437
|
+
unsetIndex == null
|
|
438
|
+
) {
|
|
439
|
+
return null
|
|
440
|
+
}
|
|
441
|
+
return {
|
|
442
|
+
api_set_ship: setShip,
|
|
443
|
+
api_unset_ship: unsetShip,
|
|
444
|
+
api_set_idx: setIndex,
|
|
445
|
+
api_unset_idx: unsetIndex,
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
if (path === '/kcsapi/api_req_kaisou/unsetslot_all') {
|
|
449
|
+
const shipId = positiveIntegerOrNull(postBody.api_id)
|
|
450
|
+
return shipId == null ? null : { api_id: shipId }
|
|
451
|
+
}
|
|
452
|
+
return null
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function equipmentApiResult(detail) {
|
|
456
|
+
const candidates = [
|
|
457
|
+
detail.apiResult,
|
|
458
|
+
detail.api_result,
|
|
459
|
+
detail.result,
|
|
460
|
+
detail.body && detail.body.api_result,
|
|
461
|
+
]
|
|
462
|
+
for (const candidate of candidates) {
|
|
463
|
+
const value = toInteger(candidate)
|
|
464
|
+
if (value != null) return value === 1 ? 1 : 0
|
|
465
|
+
}
|
|
466
|
+
return null
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function boundedInteger(value, minimum, maximum = Number.MAX_SAFE_INTEGER) {
|
|
470
|
+
const parsed = toInteger(value)
|
|
471
|
+
return parsed != null && parsed >= minimum && parsed <= maximum
|
|
472
|
+
? parsed
|
|
473
|
+
: null
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function selectedKindOf(postBody) {
|
|
477
|
+
if (!postBody || typeof postBody !== 'object') return null
|
|
478
|
+
const value = postBody.api_selected_kind
|
|
479
|
+
if (value == null || value === '') return null
|
|
480
|
+
if (typeof value === 'string' || typeof value === 'number') return String(value)
|
|
481
|
+
return null
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function toInteger(value, fallback = null) {
|
|
485
|
+
if (value == null || value === '') return fallback
|
|
486
|
+
const parsed = Number(value)
|
|
487
|
+
return Number.isInteger(parsed) ? parsed : null
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function nonNegativeInteger(value) {
|
|
491
|
+
const parsed = toInteger(value, 0)
|
|
492
|
+
return parsed != null && parsed >= 0 ? parsed : 0
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function positiveIntegerOrNull(value) {
|
|
496
|
+
const parsed = toInteger(value)
|
|
497
|
+
return parsed != null && parsed > 0 ? parsed : null
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function finiteOrNull(value) {
|
|
501
|
+
return Number.isFinite(value) ? value : null
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
function numberArray(value) {
|
|
505
|
+
if (!Array.isArray(value)) return []
|
|
506
|
+
return value.map((item) => finiteOrNull(item))
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
function objectOrNull(value) {
|
|
510
|
+
return value && typeof value === 'object' ? { ...value } : null
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
module.exports = {
|
|
514
|
+
createPoiTelemetry,
|
|
515
|
+
}
|