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/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 {};