funcraft-api-v3 0.0.1-security → 3.0.2
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.
Potentially problematic release.
This version of funcraft-api-v3 might be problematic. Click here for more details.
- package/LICENSE +21 -0
- package/README.md +322 -5
- package/errors.js +20 -0
- package/index.js +419 -0
- package/package.json +29 -6
- package/parsers.js +629 -0
- package/types/errors.d.ts +60 -0
- package/types/index.d.ts +249 -0
- package/types/parsers.d.ts +97 -0
- package/types/utils.d.ts +86 -0
- package/utils.js +178 -0
package/parsers.js
ADDED
@@ -0,0 +1,629 @@
|
|
1
|
+
const HTMLParser = require("node-html-parser");
|
2
|
+
const errors = require('./errors');
|
3
|
+
const {
|
4
|
+
data: { games, months },
|
5
|
+
Round,
|
6
|
+
parseFCInt,
|
7
|
+
parseFCDate,
|
8
|
+
getGame,
|
9
|
+
getMonth
|
10
|
+
} = require('./utils');
|
11
|
+
|
12
|
+
/**
|
13
|
+
* Get stats from html body
|
14
|
+
* @param {string} body
|
15
|
+
* @param {string} href
|
16
|
+
* @param {{ username: string, monthDiff: number, numGame: number, month: number }} data
|
17
|
+
* @returns {import('./').StatsResponse}
|
18
|
+
*/
|
19
|
+
function stats(body, href, { username, monthDiff, numGame, month }) {
|
20
|
+
const dom = HTMLParser.parse(body);
|
21
|
+
|
22
|
+
const usernameChildren = dom.querySelector('#main-layout').childNodes[5].childNodes[1].childNodes[1].childNodes[3];
|
23
|
+
if (!usernameChildren)
|
24
|
+
return errors.stats.unknownPlayer(username);
|
25
|
+
|
26
|
+
const rows = dom.querySelector('#player-stats').childNodes[5].childNodes[numGame * 2 + 1].childNodes[1].childNodes[3].childNodes;
|
27
|
+
const datas = [];
|
28
|
+
for (let i = 3; i < rows.length; i++) {
|
29
|
+
const row = rows[i];
|
30
|
+
if (row.childNodes.length > 0) {
|
31
|
+
let contentRow;
|
32
|
+
if (monthDiff !== 0)
|
33
|
+
contentRow = row.childNodes[5].childNodes[monthDiff * 2 - 1].text;
|
34
|
+
else
|
35
|
+
contentRow = row.childNodes[3].text
|
36
|
+
if (contentRow.trim().replace("-", "") == '')
|
37
|
+
datas.push(0);
|
38
|
+
else if (contentRow.trim().match(/\d+[a-z]\s+\d+[a-z]/mi)) {
|
39
|
+
const elems = contentRow.trim().match(/(\d+)[a-z]\s+(\d+)[a-z]/mi);
|
40
|
+
datas.push(parseInt(elems[1], 10) * 60 + parseInt(elems[2], 10));
|
41
|
+
}
|
42
|
+
else
|
43
|
+
datas.push(parseInt(contentRow.trim().replace(/\s+/gi, ""), 10));
|
44
|
+
}
|
45
|
+
}
|
46
|
+
if (datas[2] === 0)
|
47
|
+
return errors.stats.noStatistics();
|
48
|
+
|
49
|
+
const stats = {};
|
50
|
+
stats.code = 0;
|
51
|
+
stats.error = null;
|
52
|
+
|
53
|
+
stats.userId = href.match(/^https?:\/\/(www\.)?funcraft\.\w{1,3}(\/\w+){2}\/(\d+)\/\w+$/i)[3];
|
54
|
+
|
55
|
+
let playerUsername = usernameChildren.childNodes[1].childNodes[usernameChildren.childNodes[1].childNodes.length - 2].text.trim();
|
56
|
+
while (playerUsername.includes(' ')) {
|
57
|
+
playerUsername = playerUsername.split(' ')[1];
|
58
|
+
}
|
59
|
+
stats.username = playerUsername;
|
60
|
+
|
61
|
+
statsFromData(stats, datas, month, numGame);
|
62
|
+
|
63
|
+
stats.skin = dom.querySelector('#main-layout').childNodes[5].childNodes[1].childNodes[1].childNodes[1].childNodes[1].rawAttrs.match(/^src="(.*)"$/)[1];
|
64
|
+
|
65
|
+
return stats;
|
66
|
+
}
|
67
|
+
|
68
|
+
|
69
|
+
/**
|
70
|
+
* Get all stats from html body
|
71
|
+
* @param {string} body
|
72
|
+
* @param {string} href
|
73
|
+
* @param {{ username: string }} data
|
74
|
+
* @returns {import('./').AllStatsResponse}
|
75
|
+
*/
|
76
|
+
function allStats(body, href, { username }) {
|
77
|
+
const dom = HTMLParser.parse(body);
|
78
|
+
|
79
|
+
const usernameChildren = dom.querySelector('#main-layout').childNodes[5].childNodes[1].childNodes[1].childNodes[3];
|
80
|
+
if (!usernameChildren)
|
81
|
+
return errors.allStats.unknownPlayer(username);
|
82
|
+
|
83
|
+
let playerUsername = usernameChildren.childNodes[1].childNodes[usernameChildren.childNodes[1].childNodes.length - 2].text.trim();
|
84
|
+
while (playerUsername.includes(' ')) {
|
85
|
+
playerUsername = playerUsername.split(' ')[1];
|
86
|
+
}
|
87
|
+
|
88
|
+
// const skin = dom.querySelector('#main-layout').childNodes[5].childNodes[1].childNodes[1].childNodes[1].childNodes[1].attributes.src;
|
89
|
+
const skin = dom.querySelector('#main-layout').childNodes[5].childNodes[1].childNodes[1].childNodes[1].childNodes[1].rawAttrs.match(/^src="(.*)"$/)[1];
|
90
|
+
const playerId = href.match(/^https?:\/\/(www\.)?funcraft\.\w{1,3}(\/\w+){2}\/(\d+)\/\w+$/i)[3];
|
91
|
+
|
92
|
+
|
93
|
+
const allStats = {};
|
94
|
+
allStats.code = 0;
|
95
|
+
allStats.error = null;
|
96
|
+
allStats.infos = {};
|
97
|
+
allStats.infos.username = playerUsername;
|
98
|
+
allStats.infos.skin = skin;
|
99
|
+
allStats.infos.userId = playerId;
|
100
|
+
|
101
|
+
for (let numGame = 0; numGame < games.length; numGame++) {
|
102
|
+
const gameName = games[numGame];
|
103
|
+
const rows = dom.querySelector('#player-stats').childNodes[5].childNodes[numGame * 2 + 1].childNodes[1].childNodes[3].childNodes;
|
104
|
+
allStats[gameName] = {};
|
105
|
+
for (let monthDiff = 0; monthDiff < 5; monthDiff++) {
|
106
|
+
const month = monthDiff === 0 ? 0 : (((new Date()).getMonth() - monthDiff + 1) < 0 ? 12 + ((new Date()).getMonth() - monthDiff + 1) : ((new Date()).getMonth() - monthDiff + 1)) % 12 + 1;
|
107
|
+
const monthName = month === 0 ? 'always' : months[month - 1];
|
108
|
+
const datas = [];
|
109
|
+
for (let i = 3; i < rows.length; i++) {
|
110
|
+
const row = rows[i];
|
111
|
+
if (row.childNodes.length > 0) {
|
112
|
+
let contentRow;
|
113
|
+
if (monthDiff !== 0)
|
114
|
+
contentRow = row.childNodes[5].childNodes[monthDiff * 2 - 1].text;
|
115
|
+
else
|
116
|
+
contentRow = row.childNodes[3].text
|
117
|
+
if (contentRow.trim().replace("-", "") == '')
|
118
|
+
datas.push(0);
|
119
|
+
else if (contentRow.trim().match(/\d+[a-z]\s+\d+[a-z]/mi)) {
|
120
|
+
const elems = contentRow.trim().match(/(\d+)[a-z]\s+(\d+)[a-z]/mi);
|
121
|
+
datas.push(parseInt(elems[1], 10) * 60 + parseInt(elems[2], 10));
|
122
|
+
}
|
123
|
+
else
|
124
|
+
datas.push(parseInt(contentRow.trim().replace(/\s+/gi, ""), 10));
|
125
|
+
}
|
126
|
+
}
|
127
|
+
|
128
|
+
if (datas[2] === 0)
|
129
|
+
allStats[gameName][monthName] = null;
|
130
|
+
else {
|
131
|
+
const stats = {};
|
132
|
+
stats.code = 0;
|
133
|
+
stats.error = null;
|
134
|
+
|
135
|
+
stats.username = playerUsername;
|
136
|
+
|
137
|
+
statsFromData(stats, datas, month, numGame);
|
138
|
+
|
139
|
+
stats.skin = skin;
|
140
|
+
stats.userId = playerId;
|
141
|
+
|
142
|
+
allStats[gameName][monthName] = stats;
|
143
|
+
}
|
144
|
+
}
|
145
|
+
}
|
146
|
+
|
147
|
+
return allStats;
|
148
|
+
}
|
149
|
+
|
150
|
+
|
151
|
+
/**
|
152
|
+
* Get infos from html body
|
153
|
+
* @param {string} body
|
154
|
+
* @param {string} href
|
155
|
+
* @param {{ username: string }} data
|
156
|
+
* @returns {import('./').InfosResponse}
|
157
|
+
*/
|
158
|
+
function infos(body, href, { username }) {
|
159
|
+
const dom = HTMLParser.parse(body);
|
160
|
+
|
161
|
+
const infos = {};
|
162
|
+
infos.code = 0;
|
163
|
+
infos.error = null;
|
164
|
+
|
165
|
+
const usernameChildren = dom.querySelector('#main-layout').childNodes[5].childNodes[1].childNodes[1].childNodes[3];
|
166
|
+
if (!usernameChildren)
|
167
|
+
return errors.infos.unknownPlayer(username);
|
168
|
+
infos.grade = usernameChildren.childNodes[1].text.trim().split(/\s+/gi)[0];
|
169
|
+
|
170
|
+
let playerUsername = usernameChildren.childNodes[1].childNodes[usernameChildren.childNodes[1].childNodes.length - 2].text.trim();
|
171
|
+
while (playerUsername.includes(' ')) {
|
172
|
+
playerUsername = playerUsername.split(' ')[1];
|
173
|
+
}
|
174
|
+
infos.username = playerUsername;
|
175
|
+
|
176
|
+
infos.userId = href.match(/^https?:\/\/(www\.)?funcraft\.\w{1,3}(\/\w+){2}\/(\d+)\/\w+$/i)[3];
|
177
|
+
infos.skin = dom.querySelector('#main-layout').childNodes[5].childNodes[1].childNodes[1].childNodes[1].childNodes[1].rawAttrs.match(/^src="(.*)"$/)[1];
|
178
|
+
|
179
|
+
infos.inscription = parseFCDate(dom.querySelector('#main-layout').childNodes[5].childNodes[1].childNodes[1].childNodes[3].childNodes[3].childNodes[1].childNodes[3].rawAttrs.match(/(^title=|\stitle=)"([^"]*)"/)[2]);
|
180
|
+
infos.lastConnection = parseFCDate(dom.querySelector('#main-layout').childNodes[5].childNodes[1].childNodes[1].childNodes[3].childNodes[3].childNodes[3].childNodes[3].rawAttrs.match(/(^title=|\stitle=)"([^"]*)"/)[2]);
|
181
|
+
infos.gloires = parseFCInt(dom.querySelector('#main-layout').childNodes[5].childNodes[1].childNodes[1].childNodes[3].childNodes[5].childNodes[1].text.trim().replace(/\s+/gi, ""));
|
182
|
+
infos.gameCount = parseFCInt(dom.querySelector('#main-layout').childNodes[5].childNodes[1].childNodes[1].childNodes[3].childNodes[5].childNodes[3].text.trim().replace(/\s+/gi, ""));
|
183
|
+
|
184
|
+
let points = 0;
|
185
|
+
let winCount = 0;
|
186
|
+
let defeatCount = 0;
|
187
|
+
let tempsJeu = 0;
|
188
|
+
let kills = 0;
|
189
|
+
let deathCount = 0;
|
190
|
+
let rows = dom.querySelector('#player-stats').childNodes[5].childNodes;
|
191
|
+
let numrow = 0;
|
192
|
+
for (let row of rows) {
|
193
|
+
if (row.text.trim() !== "") {
|
194
|
+
let numcol = 1;
|
195
|
+
points += parseFCInt(row.childNodes[1].childNodes[3].childNodes[(++numcol) * 2 + 1].childNodes[3].text.trim().replace(/\s+/gi, ""));
|
196
|
+
const partie = parseFCInt(row.childNodes[1].childNodes[3].childNodes[(++numcol) * 2 + 1].childNodes[3].text.trim().replace(/\s+/gi, ""));
|
197
|
+
const victoire = parseFCInt(row.childNodes[1].childNodes[3].childNodes[(++numcol) * 2 + 1].childNodes[3].text.trim().replace(/\s+/gi, ""));
|
198
|
+
if (!Number.isNaN(victoire))
|
199
|
+
winCount += victoire;
|
200
|
+
if (numrow == 3 || numrow == 4 || numrow == 5 || numrow == 6 || numrow == 7 || numrow == 9)
|
201
|
+
defeatCount += (partie - victoire);
|
202
|
+
else
|
203
|
+
defeatCount += parseFCInt(row.childNodes[1].childNodes[3].childNodes[(++numcol) * 2 + 1].childNodes[3].text.trim().replace(/\s+/gi, ""));
|
204
|
+
const temps = row.childNodes[1].childNodes[3].childNodes[(++numcol) * 2 + 1].childNodes[3].text.trim().replace(/\s+/gi, "");
|
205
|
+
if (temps !== "-")
|
206
|
+
tempsJeu += parseInt(temps.split("h")[0], 10) * 60 + parseInt(temps.split("h")[1].replace(/m$/g, ""), 10);
|
207
|
+
kills += parseFCInt(row.childNodes[1].childNodes[3].childNodes[(++numcol) * 2 + 1].childNodes[3].text.trim().replace(/\s+/gi, ""));
|
208
|
+
deathCount += parseFCInt(row.childNodes[1].childNodes[3].childNodes[(++numcol) * 2 + 1].childNodes[3].text.trim().replace(/\s+/gi, ""));
|
209
|
+
numrow++;
|
210
|
+
}
|
211
|
+
}
|
212
|
+
|
213
|
+
infos.points = points;
|
214
|
+
infos.winCount = winCount;
|
215
|
+
infos.defeatCount = defeatCount;
|
216
|
+
infos.gameTime = tempsJeu;
|
217
|
+
infos.kills = kills;
|
218
|
+
infos.deathCount = deathCount;
|
219
|
+
|
220
|
+
const ban = dom.querySelector("div.player-alert");
|
221
|
+
if (ban) {
|
222
|
+
if (ban.text.trim().match(/temporairement/mi))
|
223
|
+
infos.ban = "TEMP";
|
224
|
+
else
|
225
|
+
infos.ban = "DEF";
|
226
|
+
}
|
227
|
+
else
|
228
|
+
infos.ban = "NONE";
|
229
|
+
|
230
|
+
return infos;
|
231
|
+
}
|
232
|
+
|
233
|
+
|
234
|
+
/**
|
235
|
+
* Get friends from html body
|
236
|
+
* @param {string} body
|
237
|
+
* @returns {{
|
238
|
+
* code: number,
|
239
|
+
* error: string,
|
240
|
+
* friends: {
|
241
|
+
* nom: string,
|
242
|
+
* skin: string
|
243
|
+
* }[]
|
244
|
+
* }}
|
245
|
+
*/
|
246
|
+
function friends(body) {
|
247
|
+
const fDom = HTMLParser.parse(body);
|
248
|
+
const heads = fDom.querySelector("div.players-heads");
|
249
|
+
if (!heads)
|
250
|
+
return errors.friends.unknownPlayer();
|
251
|
+
|
252
|
+
const fRows = heads ? heads.childNodes : [];
|
253
|
+
const friends = [];
|
254
|
+
for (let row of fRows) {
|
255
|
+
if (row.tagName && row.childNodes[1] && row.childNodes[1].childNodes[1]) {
|
256
|
+
friends.push({
|
257
|
+
nom: row.rawAttrs.match(/(^title=|\stitle=)"([^"]*)"/)[2],
|
258
|
+
skin: row.childNodes[1].childNodes[1].rawAttrs.match(/(^src=|\ssrc=)"([^"]*)"/)[2]
|
259
|
+
});
|
260
|
+
}
|
261
|
+
}
|
262
|
+
|
263
|
+
return {
|
264
|
+
code: 0,
|
265
|
+
error: null,
|
266
|
+
friends
|
267
|
+
};
|
268
|
+
}
|
269
|
+
|
270
|
+
|
271
|
+
/**
|
272
|
+
* Get stats table from html body
|
273
|
+
* @param {string} body
|
274
|
+
* @param {{ period: string, game: string }} data
|
275
|
+
* @returns {{
|
276
|
+
* code: number,
|
277
|
+
* error: string,
|
278
|
+
* table: import('./').StatsResponse[]
|
279
|
+
* }}
|
280
|
+
*/
|
281
|
+
function table(body, { period, game }) {
|
282
|
+
const dom = HTMLParser.parse(body);
|
283
|
+
if (!dom.querySelector('.leaderboard-table'))
|
284
|
+
return errors.table.noStatistics();
|
285
|
+
const usernameChildren = dom.querySelector('.leaderboard-table').childNodes[3];
|
286
|
+
const result = [];
|
287
|
+
for (let raw of usernameChildren.childNodes) {
|
288
|
+
if (raw.rawTagName !== 'tr')
|
289
|
+
continue;
|
290
|
+
|
291
|
+
const stats = {};
|
292
|
+
stats.code = 0;
|
293
|
+
stats.error = null;
|
294
|
+
|
295
|
+
const userUrlMatch = raw.childNodes[period === 'always' ? 3 : 5].childNodes[1].rawAttrs.match(/^href="https?:\/\/(www\.)?funcraft\.\w{1,3}(\/\w+){2}\/(\d+)(\/\w+)?"$/i);
|
296
|
+
stats.userId = userUrlMatch[3];
|
297
|
+
|
298
|
+
if (userUrlMatch[4]) {
|
299
|
+
const username = raw.childNodes[period === 'always' ? 3 : 5].childNodes[1].childNodes[0].rawText.trim();
|
300
|
+
stats.username = username;
|
301
|
+
}
|
302
|
+
else
|
303
|
+
stats.username = "";
|
304
|
+
|
305
|
+
const datas = [];
|
306
|
+
for (let cell of raw.childNodes) {
|
307
|
+
if (cell.rawTagName !== 'td')
|
308
|
+
continue;
|
309
|
+
|
310
|
+
const contentRow = cell.childNodes[0] ? cell.childNodes[0].rawText : '0';
|
311
|
+
if (contentRow.trim() === '')
|
312
|
+
continue;
|
313
|
+
else if (contentRow.trim().replace("-", "") == '')
|
314
|
+
datas.push(0);
|
315
|
+
else if (contentRow.trim().match(/\d+[a-z]\s+\d+[a-z]/mi)) {
|
316
|
+
const elems = contentRow.trim().match(/(\d+)[a-z]\s+(\d+)[a-z]/mi);
|
317
|
+
datas.push(parseInt(elems[1], 10) * 60 + parseInt(elems[2], 10));
|
318
|
+
}
|
319
|
+
else
|
320
|
+
datas.push(parseInt(contentRow.trim().replace(/\s+/gi, ""), 10));
|
321
|
+
}
|
322
|
+
|
323
|
+
statsFromData(stats, datas, getMonth(period), getGame(game));
|
324
|
+
|
325
|
+
if (stats.rank === 1 || stats.rank === 2 || stats.rank === 3)
|
326
|
+
stats.skin = dom.querySelector('.podium-' + stats.rank).childNodes[1].childNodes[stats.rank === 1 ? 3 : 1].childNodes[1].childNodes[3].rawAttrs.match(/^src="(.*)"$/)[1];
|
327
|
+
|
328
|
+
result.push(stats);
|
329
|
+
}
|
330
|
+
|
331
|
+
return {
|
332
|
+
code: 0,
|
333
|
+
error: null,
|
334
|
+
table: result
|
335
|
+
};
|
336
|
+
}
|
337
|
+
|
338
|
+
|
339
|
+
/**
|
340
|
+
* Get head fril html body
|
341
|
+
* @param {string} body
|
342
|
+
* @param {{ username: string }} data
|
343
|
+
* @returns {{
|
344
|
+
* code: number,
|
345
|
+
* error: string,
|
346
|
+
* head: string
|
347
|
+
* }}
|
348
|
+
*/
|
349
|
+
function head(body, { username }) {
|
350
|
+
const dom = HTMLParser.parse(body);
|
351
|
+
|
352
|
+
if (!dom.querySelector('#main-layout'))
|
353
|
+
return errors.head.unknownPlayer(username);
|
354
|
+
const usernameChildren = dom.querySelector('#main-layout').childNodes[5].childNodes[1].childNodes[1].childNodes[3];
|
355
|
+
if (!usernameChildren)
|
356
|
+
return errors.head.unknownPlayer(username);
|
357
|
+
|
358
|
+
return {
|
359
|
+
code: 0,
|
360
|
+
error: null,
|
361
|
+
head: dom.querySelector('#main-layout').childNodes[5].childNodes[1].childNodes[1].childNodes[1].childNodes[1].attributes.src
|
362
|
+
};
|
363
|
+
}
|
364
|
+
|
365
|
+
|
366
|
+
|
367
|
+
/**
|
368
|
+
*
|
369
|
+
* @param {object} stats
|
370
|
+
* @param {object} datas
|
371
|
+
* @param {number} month
|
372
|
+
* @param {number} numGame
|
373
|
+
* @returns {object}
|
374
|
+
*/
|
375
|
+
function statsFromData(stats, datas, month, numGame) {
|
376
|
+
if (month !== null && month !== undefined) {
|
377
|
+
stats.month = month;
|
378
|
+
stats.monthName = month === 0 ? 'always' : months[month - 1];
|
379
|
+
}
|
380
|
+
stats.game = games[numGame];
|
381
|
+
stats.rank = datas[0];
|
382
|
+
stats.data = {};
|
383
|
+
|
384
|
+
let valColumn = 1;
|
385
|
+
stats.data.points = datas[valColumn++];
|
386
|
+
stats.data.gameCount = datas[valColumn++];
|
387
|
+
stats.data.winCount = datas[valColumn++];
|
388
|
+
if (games[numGame] === 'survival' || games[numGame] === 'skywars' || games[numGame] === 'pvpsmash' || games[numGame] === 'octogone' || games[numGame] === 'shootcraft' || games[numGame] === 'infected')
|
389
|
+
stats.data.defeatCount = stats.data.gameCount - stats.data.winCount;
|
390
|
+
else
|
391
|
+
stats.data.defeatCount = datas[valColumn++];
|
392
|
+
stats.data.gameTime = datas[valColumn++];
|
393
|
+
stats.data.kills = datas[valColumn++];
|
394
|
+
stats.data.deathCount = datas[valColumn++];
|
395
|
+
if (games[numGame] === 'rush_mdt' || games[numGame] === 'rush_retro' || games[numGame] === 'landrush')
|
396
|
+
stats.data.lits_detruits = datas[valColumn++];
|
397
|
+
if (games[numGame] === 'blitz')
|
398
|
+
stats.data.degats_nexus = datas[valColumn++];
|
399
|
+
if (games[numGame] === 'pvpsmash' || games[numGame] === 'octogone')
|
400
|
+
stats.data.degats = datas[valColumn++];
|
401
|
+
|
402
|
+
stats.stats = {};
|
403
|
+
if (stats.data.gameCount === 0)
|
404
|
+
stats.stats.winrate = 0;
|
405
|
+
else if (stats.data.winCount === 0 && stats.data.defeatCount === 0)
|
406
|
+
stats.stats.winrate = Round((stats.data.winCount / stats.data.gameCount) * 100, 3);
|
407
|
+
else
|
408
|
+
stats.stats.winrate = Round((stats.data.winCount / (stats.data.winCount + stats.data.defeatCount)) * 100, 3);
|
409
|
+
if (stats.data.deathCount === 0)
|
410
|
+
stats.stats.kd = stats.data.kills;
|
411
|
+
else
|
412
|
+
stats.stats.kd = Round(stats.data.kills / stats.data.deathCount, 3);
|
413
|
+
if (games[numGame] === 'shootcraft') {
|
414
|
+
if (stats.data.gameCount === 0)
|
415
|
+
stats.stats.ragequit = 0;
|
416
|
+
else
|
417
|
+
stats.stats.ragequit = Round((((stats.data.gameTime / stats.data.gameCount) / 5) - 1) * (-100), 3);
|
418
|
+
}
|
419
|
+
if (stats.data.gameCount === 0)
|
420
|
+
stats.stats.killsPerGame = 0;
|
421
|
+
else
|
422
|
+
stats.stats.killsPerGame = Round(stats.data.kills / stats.data.gameCount, 3);
|
423
|
+
if (stats.data.gameCount === 0)
|
424
|
+
stats.stats.deathsPerGame = 0;
|
425
|
+
else
|
426
|
+
stats.stats.deathsPerGame = Round(stats.data.deathCount / stats.data.gameCount, 3);
|
427
|
+
if (stats.data.gameCount === 0)
|
428
|
+
stats.stats.pointsPerGame = 0;
|
429
|
+
else
|
430
|
+
stats.stats.pointsPerGame = Round(stats.data.points / stats.data.gameCount, 3);
|
431
|
+
if (games[numGame] != 'shootcraft') {
|
432
|
+
if (stats.data.gameCount === 0)
|
433
|
+
stats.stats.timePerGame = 0;
|
434
|
+
else
|
435
|
+
stats.stats.timePerGame = Round(stats.data.gameTime * 60 / stats.data.gameCount, 3);
|
436
|
+
}
|
437
|
+
if (stats.data.gameCount === 0)
|
438
|
+
stats.stats.killsPerMinute = 0;
|
439
|
+
else {
|
440
|
+
if (games[numGame] === 'shootcraft')
|
441
|
+
stats.stats.killsPerMinute = Round(stats.data.kills / stats.data.gameTime, 3);
|
442
|
+
else
|
443
|
+
stats.stats.killsPerMinute = Round(stats.stats.killsPerGame / (stats.stats.timePerGame / 60), 3);
|
444
|
+
}
|
445
|
+
if (games[numGame] === 'shootcraft') {
|
446
|
+
if (stats.data.kills === 0)
|
447
|
+
stats.stats.secondsPerKill = 0;
|
448
|
+
else
|
449
|
+
stats.stats.secondsPerKill = Round((stats.data.gameTime * 60) / stats.data.kills, 3);
|
450
|
+
}
|
451
|
+
if (games[numGame] === 'rush_mdt' || games[numGame] === 'rush_retro' || games[numGame] === 'landrush') {
|
452
|
+
if (stats.data.gameCount === 0)
|
453
|
+
stats.stats.bedsPerGame = 0;
|
454
|
+
else
|
455
|
+
stats.stats.bedsPerGame = Round(stats.data.lits_detruits / stats.data.gameCount, 3);
|
456
|
+
}
|
457
|
+
if (games[numGame] === 'blitz') {
|
458
|
+
if (stats.data.gameCount === 0)
|
459
|
+
stats.stats.nexusPerGame = 0;
|
460
|
+
else
|
461
|
+
stats.stats.nexusPerGame = Round(stats.data.degats_nexus / stats.data.gameCount, 3);
|
462
|
+
}
|
463
|
+
if (games[numGame] === 'pvpsmash' || games[numGame] === 'octogone') {
|
464
|
+
if (stats.data.gameCount === 0)
|
465
|
+
stats.stats.damagePerGame = 0;
|
466
|
+
else
|
467
|
+
stats.stats.damagePerGame = Round(stats.data.degats / stats.data.gameCount, 3);
|
468
|
+
}
|
469
|
+
|
470
|
+
return stats;
|
471
|
+
}
|
472
|
+
|
473
|
+
|
474
|
+
|
475
|
+
// slt cv
|
476
|
+
function getMonthsDispo(body, href, { username }) {
|
477
|
+
const dom = HTMLParser.parse(body);
|
478
|
+
|
479
|
+
const usernameChildren = dom.querySelector('#main-layout').childNodes[5].childNodes[1].childNodes[1].childNodes[3];
|
480
|
+
if (!usernameChildren)
|
481
|
+
return errors.getMonthsDispo.unknownPlayer(username);
|
482
|
+
|
483
|
+
let playerUsername = usernameChildren.childNodes[1].childNodes[usernameChildren.childNodes[1].childNodes.length - 2].text.trim();
|
484
|
+
while (playerUsername.includes(' ')) {
|
485
|
+
playerUsername = playerUsername.split(' ')[1];
|
486
|
+
}
|
487
|
+
|
488
|
+
const skin = dom.querySelector('#main-layout').childNodes[5].childNodes[1].childNodes[1].childNodes[1].childNodes[1].rawAttrs.match(/^src="(.*)"$/)[1];
|
489
|
+
const playerId = href.match(/^https?:\/\/(www\.)?funcraft\.\w{1,3}(\/\w+){2}\/(\d+)\/\w+$/i)[3];
|
490
|
+
|
491
|
+
const allMonths = {};
|
492
|
+
allMonths.code = 0;
|
493
|
+
allMonths.error = null;
|
494
|
+
allMonths.username = playerUsername;
|
495
|
+
allMonths.skin = skin;
|
496
|
+
allMonths.userId = playerId;
|
497
|
+
allMonths.months = [];
|
498
|
+
|
499
|
+
const monthsHtml = dom.querySelector("#player-stats-period-values").childNodes;
|
500
|
+
let monthsArray = [];
|
501
|
+
// format : month-YYYY-MM
|
502
|
+
// month-2023-08
|
503
|
+
|
504
|
+
for (let i = 4; i < monthsHtml.length; i++) {
|
505
|
+
const monthHtml = monthsHtml[i];
|
506
|
+
|
507
|
+
if (monthHtml.tagName !== "LI") continue;
|
508
|
+
|
509
|
+
const yearMonth = monthHtml?.childNodes[0]?.getAttribute("data-value")?.replace("month-", "");
|
510
|
+
|
511
|
+
monthsArray.push(yearMonth);
|
512
|
+
}
|
513
|
+
|
514
|
+
return monthsArray;
|
515
|
+
}
|
516
|
+
|
517
|
+
|
518
|
+
|
519
|
+
|
520
|
+
function statsOfAllMonths(body, href, { username, period }) {
|
521
|
+
const dom = HTMLParser.parse(body);
|
522
|
+
|
523
|
+
const usernameChildren = dom.querySelector('#main-layout').childNodes[5].childNodes[1].childNodes[1].childNodes[3];
|
524
|
+
if (!usernameChildren)
|
525
|
+
return errors.statsOfAllMonths.unknownPlayer(username);
|
526
|
+
|
527
|
+
let playerUsername = usernameChildren.childNodes[1].childNodes[usernameChildren.childNodes[1].childNodes.length - 2].text.trim();
|
528
|
+
while (playerUsername.includes(' ')) {
|
529
|
+
playerUsername = playerUsername.split(' ')[1];
|
530
|
+
}
|
531
|
+
|
532
|
+
const skin = dom.querySelector('#main-layout').childNodes[5].childNodes[1].childNodes[1].childNodes[1].childNodes[1].rawAttrs.match(/^src="(.*)"$/)[1];
|
533
|
+
const playerId = href.match(/^https?:\/\/(www\.)?funcraft\.\w{1,3}(\/\w+){2}\/(\d+)\/\w+$/i)[3];
|
534
|
+
|
535
|
+
|
536
|
+
const allStats = {};
|
537
|
+
allStats.code = 0;
|
538
|
+
allStats.error = null;
|
539
|
+
allStats.fetchedTimestamp = Date.now();
|
540
|
+
allStats.infos = {};
|
541
|
+
allStats.infos.username = playerUsername;
|
542
|
+
allStats.infos.skin = skin;
|
543
|
+
allStats.infos.userId = playerId;
|
544
|
+
|
545
|
+
for (let numGame = 0; numGame < games.length; numGame++) {
|
546
|
+
// console.log(numGame, games[numGame]);
|
547
|
+
const gam = dom.querySelector('#player-stats').childNodes[5].childNodes[numGame * 2 + 1];
|
548
|
+
if (!gam) continue;
|
549
|
+
// const gameName = games[numGame];
|
550
|
+
const gameName = gam.childNodes[1].childNodes[1].textContent.trim().toLowerCase()
|
551
|
+
const rows = gam.childNodes[1].childNodes[3].childNodes;
|
552
|
+
allStats[gameName] = {};
|
553
|
+
for (let monthDiff = 0; monthDiff < (dom.querySelector("#player-stats-period-values").childNodes.length - 1) / 2 - 1; monthDiff++) {
|
554
|
+
const month = monthDiff === 0 ? 0 : (((new Date()).getMonth() - monthDiff + 1) < 0 ? 12 + ((new Date()).getMonth() - monthDiff + 1) : ((new Date()).getMonth() - monthDiff + 1)) % 12 + 1;
|
555
|
+
const thePeriodStr = (monthDiff === 0 ? "always" : rows[3].childNodes[5].childNodes[monthDiff * 2 - 1].getAttribute("data-period")),
|
556
|
+
thePeriod = thePeriodStr && thePeriodStr !== "always" ? thePeriodStr.replace("month-", "").replace(",always", "").split("-") : null,
|
557
|
+
theYear = thePeriod && parseInt(thePeriod[0]),
|
558
|
+
theMonth = thePeriod && parseInt(thePeriod[1]);
|
559
|
+
|
560
|
+
if (theYear && !(theYear in allStats[gameName])) allStats[gameName][theYear] = {};
|
561
|
+
|
562
|
+
const datas = [];
|
563
|
+
for (let i = 3; i < rows.length; i++) {
|
564
|
+
const row = rows[i];
|
565
|
+
if (row.childNodes.length > 0) {
|
566
|
+
let contentRow;
|
567
|
+
if (monthDiff !== 0)
|
568
|
+
contentRow = row.childNodes[5].childNodes[monthDiff * 2 - 1].text;
|
569
|
+
else
|
570
|
+
contentRow = row.childNodes[3].text;
|
571
|
+
if (contentRow.trim().replace("-", "") == '')
|
572
|
+
datas.push(0);
|
573
|
+
else if (contentRow.trim().match(/\d+[a-z]\s+\d+[a-z]/mi)) {
|
574
|
+
const elems = contentRow.trim().match(/(\d+)[a-z]\s+(\d+)[a-z]/mi);
|
575
|
+
datas.push(parseInt(elems[1], 10) * 60 + parseInt(elems[2], 10));
|
576
|
+
}
|
577
|
+
else
|
578
|
+
datas.push(parseInt(contentRow.trim().replace(/\s+/gi, ""), 10));
|
579
|
+
}
|
580
|
+
}
|
581
|
+
|
582
|
+
if (datas[2] === 0) {
|
583
|
+
if (theMonth)
|
584
|
+
allStats[gameName][theYear][theMonth] = null;
|
585
|
+
else if (thePeriodStr === "always")
|
586
|
+
allStats[gameName][thePeriodStr] = null;
|
587
|
+
} else {
|
588
|
+
const stats = {};
|
589
|
+
stats.code = 0;
|
590
|
+
stats.error = null;
|
591
|
+
|
592
|
+
stats.username = playerUsername;
|
593
|
+
|
594
|
+
stats.month = null;
|
595
|
+
stats.year = theYear;
|
596
|
+
|
597
|
+
statsFromData(stats, datas, month, numGame);
|
598
|
+
|
599
|
+
stats.month = theMonth;
|
600
|
+
|
601
|
+
stats.skin = skin;
|
602
|
+
stats.userId = playerId;
|
603
|
+
|
604
|
+
if (theMonth)
|
605
|
+
allStats[gameName][theYear][theMonth] = stats;
|
606
|
+
else if (thePeriodStr === "always")
|
607
|
+
allStats[gameName][thePeriodStr] = stats;
|
608
|
+
}
|
609
|
+
}
|
610
|
+
}
|
611
|
+
|
612
|
+
return allStats;
|
613
|
+
}
|
614
|
+
|
615
|
+
|
616
|
+
|
617
|
+
|
618
|
+
|
619
|
+
module.exports = {
|
620
|
+
stats,
|
621
|
+
allStats,
|
622
|
+
infos,
|
623
|
+
friends,
|
624
|
+
table,
|
625
|
+
head,
|
626
|
+
statsFromData,
|
627
|
+
getMonthsDispo,
|
628
|
+
statsOfAllMonths
|
629
|
+
};
|
@@ -0,0 +1,60 @@
|
|
1
|
+
declare function none(): {
|
2
|
+
code: number;
|
3
|
+
error: any;
|
4
|
+
};
|
5
|
+
declare function incorrectGame(): {
|
6
|
+
code: number;
|
7
|
+
error: string;
|
8
|
+
};
|
9
|
+
declare function incorrectPeriod(): {
|
10
|
+
code: number;
|
11
|
+
error: string;
|
12
|
+
};
|
13
|
+
/** @param {string} username */
|
14
|
+
declare function unknownPlayer(username: string): {
|
15
|
+
code: number;
|
16
|
+
error: string;
|
17
|
+
};
|
18
|
+
declare function noStatistics(): {
|
19
|
+
code: number;
|
20
|
+
error: string;
|
21
|
+
};
|
22
|
+
declare function connectionError(): {
|
23
|
+
code: number;
|
24
|
+
error: string;
|
25
|
+
};
|
26
|
+
export namespace stats {
|
27
|
+
export { none };
|
28
|
+
export { incorrectGame };
|
29
|
+
export { incorrectPeriod };
|
30
|
+
export { unknownPlayer };
|
31
|
+
export { noStatistics };
|
32
|
+
export { connectionError };
|
33
|
+
}
|
34
|
+
export namespace allStats {
|
35
|
+
export { none };
|
36
|
+
export { unknownPlayer };
|
37
|
+
export { connectionError };
|
38
|
+
}
|
39
|
+
export namespace infos {
|
40
|
+
export { none };
|
41
|
+
export { unknownPlayer };
|
42
|
+
export { connectionError };
|
43
|
+
}
|
44
|
+
export namespace friends {
|
45
|
+
export { none };
|
46
|
+
export { unknownPlayer };
|
47
|
+
export { connectionError };
|
48
|
+
}
|
49
|
+
export namespace table {
|
50
|
+
export { none };
|
51
|
+
export { incorrectGame };
|
52
|
+
export { noStatistics };
|
53
|
+
export { connectionError };
|
54
|
+
}
|
55
|
+
export namespace head {
|
56
|
+
export { none };
|
57
|
+
export { unknownPlayer };
|
58
|
+
export { connectionError };
|
59
|
+
}
|
60
|
+
export {};
|