@20syldev/api 3.3.9 → 3.4.0
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/.github/FUNDING.yml +1 -1
- package/.github/workflows/publish.yml +27 -27
- package/LICENSE +27 -27
- package/README.md +110 -60
- package/app.js +817 -1174
- package/modules/v3/algorithms.js +218 -0
- package/modules/v3/captcha.js +55 -0
- package/modules/v3/chat.js +109 -0
- package/modules/v3/color.js +41 -0
- package/modules/v3/convert.js +39 -0
- package/modules/v3/domain.js +39 -0
- package/modules/v3/hash.js +16 -0
- package/modules/v3/hyperplanning.js +51 -0
- package/modules/v3/levenshtein.js +46 -0
- package/modules/v3/personal.js +127 -0
- package/modules/v3/qrcode.js +20 -0
- package/modules/v3/tic_tac_toe.js +167 -0
- package/modules/v3/time.js +87 -0
- package/modules/v3/token.js +48 -0
- package/modules/v3/username.js +32 -0
- package/modules/v3/utils.js +66 -0
- package/modules/v3.js +15 -0
- package/package.json +53 -49
- package/robots.txt +73 -73
package/app.js
CHANGED
|
@@ -1,1174 +1,817 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
import { dirname, join } from 'path';
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
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
|
-
if (
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
next();
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
//
|
|
186
|
-
app.use((
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
const
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
});
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
res.jsonResponse({
|
|
550
|
-
});
|
|
551
|
-
|
|
552
|
-
// Generate
|
|
553
|
-
app.get('/:version/
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
const
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
res.jsonResponse({ error: '
|
|
757
|
-
});
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
const
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
if (
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
const
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
app.get('/:version/token', (req, res) => {
|
|
819
|
-
res.jsonResponse({ error: 'This endpoint only supports POST requests.' });
|
|
820
|
-
});
|
|
821
|
-
|
|
822
|
-
// Generate username
|
|
823
|
-
app.get('/:version/username', (req, res) => {
|
|
824
|
-
const adj = ['Happy', 'Silly', 'Clever', 'Creative', 'Brave', 'Gentle', 'Kind', 'Funny', 'Wise', 'Charming', 'Sincere', 'Resourceful', 'Patient', 'Energetic', 'Adventurous', 'Ambitious', 'Courageous', 'Courteous', 'Determined'];
|
|
825
|
-
const ani = ['Cat', 'Dog', 'Tiger', 'Elephant', 'Monkey', 'Penguin', 'Dolphin', 'Lion', 'Bear', 'Fox', 'Owl', 'Giraffe', 'Zebra', 'Koala', 'Rabbit', 'Squirrel', 'Panda', 'Horse', 'Wolf', 'Eagle'];
|
|
826
|
-
const job = ['Writer', 'Artist', 'Musician', 'Explorer', 'Scientist', 'Engineer', 'Athlete', 'Chef', 'Doctor', 'Teacher', 'Lawyer', 'Entrepreneur', 'Actor', 'Dancer', 'Photographer', 'Architect', 'Pilot', 'Designer', 'Journalist', 'Veterinarian'];
|
|
827
|
-
|
|
828
|
-
const nombre = Math.floor(Math.random() * 100);
|
|
829
|
-
const choix = {
|
|
830
|
-
adj_num: () => random(adj) + nombre,
|
|
831
|
-
ani_num: () => random(ani) + nombre,
|
|
832
|
-
pro_num: () => random(job) + nombre,
|
|
833
|
-
adj_ani: () => random(adj) + random(ani),
|
|
834
|
-
adj_ani_num: () => random(adj) + random(ani) + nombre,
|
|
835
|
-
adj_pro: () => random(adj) + random(job),
|
|
836
|
-
pro_ani: () => random(job) + random(ani),
|
|
837
|
-
pro_ani_num: () => random(job) + random(ani) + nombre
|
|
838
|
-
};
|
|
839
|
-
|
|
840
|
-
const username = choix[random(Object.keys(choix))]();
|
|
841
|
-
res.jsonResponse({ adjective: adj, animal: ani, job, number: nombre, username });
|
|
842
|
-
});
|
|
843
|
-
|
|
844
|
-
// Display informations for owner's website
|
|
845
|
-
app.get('/:version/website', async (req, res) => {
|
|
846
|
-
const currentTime = Date.now();
|
|
847
|
-
|
|
848
|
-
if (currentTime - lastFetch >= 10 * 60 * 1000) {
|
|
849
|
-
try {
|
|
850
|
-
const username = '20syldev';
|
|
851
|
-
const token = process.env.GITHUB_TOKEN;
|
|
852
|
-
const today = new Date().toISOString().split('T')[0];
|
|
853
|
-
const monthFirst = new Date(new Date().getFullYear(), new Date().getMonth(), 1).toISOString().split('T')[0];
|
|
854
|
-
const lastYear = new Date(new Date().setFullYear(new Date().getFullYear() - 1)).toISOString().split('T')[0];
|
|
855
|
-
|
|
856
|
-
const query = `
|
|
857
|
-
{
|
|
858
|
-
user(login: "${username}") {
|
|
859
|
-
contributionsCollection(from: "${today}T00:00:00Z") {
|
|
860
|
-
contributionCalendar {
|
|
861
|
-
totalContributions
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
contributions_month: contributionsCollection(from: "${monthFirst}T00:00:00Z") {
|
|
865
|
-
contributionCalendar {
|
|
866
|
-
totalContributions
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
contributions_year: contributionsCollection(from: "${lastYear}T00:00:00Z") {
|
|
870
|
-
contributionCalendar {
|
|
871
|
-
totalContributions
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
}`;
|
|
876
|
-
|
|
877
|
-
const apiResponse = await fetch('https://api.github.com/graphql', {
|
|
878
|
-
method: 'POST',
|
|
879
|
-
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
|
|
880
|
-
body: JSON.stringify({ query })
|
|
881
|
-
});
|
|
882
|
-
|
|
883
|
-
if (!apiResponse.ok) throw new Error('Error fetching data.');
|
|
884
|
-
|
|
885
|
-
const data = await apiResponse.json();
|
|
886
|
-
const user = data?.data?.user;
|
|
887
|
-
|
|
888
|
-
contributions = {
|
|
889
|
-
today: user?.contributionsCollection?.contributionCalendar?.totalContributions || 0,
|
|
890
|
-
month: user?.contributions_month?.contributionCalendar?.totalContributions || 0,
|
|
891
|
-
year: user?.contributions_year?.contributionCalendar?.totalContributions || 0,
|
|
892
|
-
};
|
|
893
|
-
|
|
894
|
-
lastFetch = currentTime;
|
|
895
|
-
} catch { contributions = { today: 0, month: 0, year: 0 }; }
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
res.jsonResponse({
|
|
899
|
-
versions: {
|
|
900
|
-
api: process.env.API,
|
|
901
|
-
cdn: process.env.CDN,
|
|
902
|
-
coop_api: process.env.COOP_API,
|
|
903
|
-
coop_status: process.env.COOP_STATUS,
|
|
904
|
-
chat: process.env.CHAT,
|
|
905
|
-
digit: process.env.DIGIT,
|
|
906
|
-
doc_coopbot: process.env.DOC_COOPBOT,
|
|
907
|
-
docs: process.env.DOCS,
|
|
908
|
-
donut: process.env.DONUT,
|
|
909
|
-
drawio_plugin: process.env.DRAWIO_PLUGIN,
|
|
910
|
-
flowers: process.env.FLOWERS,
|
|
911
|
-
gemsync: process.env.GEMSYNC,
|
|
912
|
-
gitsite: process.env.GITSITE,
|
|
913
|
-
logs: process.env.LOGS,
|
|
914
|
-
logvault: process.env.LOGVAULT,
|
|
915
|
-
lyah: process.env.LYAH,
|
|
916
|
-
minify: process.env.MINIFY,
|
|
917
|
-
morpion: process.env.MORPION,
|
|
918
|
-
nitrogen: process.env.NITROGEN,
|
|
919
|
-
old_database: process.env.OLD_DATABASE,
|
|
920
|
-
php: process.env.PHP,
|
|
921
|
-
ping: process.env.PING,
|
|
922
|
-
portfolio: process.env.PORTFOLIO,
|
|
923
|
-
python_api: process.env.PYTHON_API,
|
|
924
|
-
readme: process.env.README,
|
|
925
|
-
terminal: process.env.TERMINAL,
|
|
926
|
-
wrkit: process.env.WRKIT,
|
|
927
|
-
zpki: process.env.ZPKI
|
|
928
|
-
},
|
|
929
|
-
patched_projects: process.env.PATCH?.split(' ') || [],
|
|
930
|
-
updated_projects: process.env.RECENT?.split(' ') || [],
|
|
931
|
-
new_projects: process.env.NEW?.split(' ') || [],
|
|
932
|
-
sub_domains: process.env.DOMAINS?.split(' ') || [],
|
|
933
|
-
stats: {
|
|
934
|
-
1: process.env.STATS1,
|
|
935
|
-
2: process.env.STATS2,
|
|
936
|
-
3: process.env.STATS3,
|
|
937
|
-
4: process.env.STATS4,
|
|
938
|
-
5: Object.keys(ipLimits).length,
|
|
939
|
-
today: contributions.today.toString(),
|
|
940
|
-
this_month: contributions.month.toString(),
|
|
941
|
-
last_year: contributions.year.toString(),
|
|
942
|
-
},
|
|
943
|
-
notif_tag: process.env.TAG,
|
|
944
|
-
active: process.env.ACTIVE
|
|
945
|
-
});
|
|
946
|
-
});
|
|
947
|
-
|
|
948
|
-
// ----------- ----------- POST ENDPOINTS ----------- ----------- //
|
|
949
|
-
|
|
950
|
-
// Store chat messages
|
|
951
|
-
app.post('/:version/chat', (req, res) => {
|
|
952
|
-
const { username, message, timestamp, session, token } = req.body;
|
|
953
|
-
|
|
954
|
-
if (!username) return res.jsonResponse({ error: 'Please provide a username (?username={username})' });
|
|
955
|
-
if (!message) return res.jsonResponse({ error: 'Please provide a message (&message={message})' });
|
|
956
|
-
if (!session) return res.jsonResponse({ error: 'Please provide a valid session ID (&session={ID})' });
|
|
957
|
-
|
|
958
|
-
const u = username.toLowerCase(), now = Date.now();
|
|
959
|
-
const msg = { username, message, timestamp: timestamp || new Date().toISOString() };
|
|
960
|
-
|
|
961
|
-
rateLimits[u] = (rateLimits[u] || []).filter(ts => now - ts < 10000);
|
|
962
|
-
if (rateLimits[u].length > 50) {
|
|
963
|
-
const remainingTime = Math.ceil((rateLimits[u][0] + 10000 - now) / 1000);
|
|
964
|
-
return res.jsonResponse({ error: `Rate limit exceeded. Try again in ${remainingTime} seconds.` });
|
|
965
|
-
}
|
|
966
|
-
rateLimits[u].push(now);
|
|
967
|
-
|
|
968
|
-
if (sessions[u] && sessions[u].user !== session) return res.jsonResponse({ error: 'Session ID mismatch' });
|
|
969
|
-
|
|
970
|
-
if (token) {
|
|
971
|
-
privateChats[token] = privateChats[token] || [];
|
|
972
|
-
privateChats[token].push(msg);
|
|
973
|
-
setTimeout(() => { delete privateChats[token]; }, 3600000);
|
|
974
|
-
} else {
|
|
975
|
-
chat.push(msg);
|
|
976
|
-
setTimeout(() => chat.splice(chat.indexOf(msg), 1), 3600000);
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
sessions[u] = sessions[u] || { user: session, last: now };
|
|
980
|
-
sessions[u].last = now;
|
|
981
|
-
|
|
982
|
-
setTimeout(() => { if (now - sessions[u].last >= 3600000) delete sessions[u]; }, 3600000);
|
|
983
|
-
|
|
984
|
-
res.jsonResponse({ message: 'Message sent successfully' });
|
|
985
|
-
});
|
|
986
|
-
|
|
987
|
-
// Display a private chat with a token
|
|
988
|
-
app.post('/:version/chat/private', (req, res) => {
|
|
989
|
-
const { username, token } = req.body;
|
|
990
|
-
|
|
991
|
-
if (!username) return res.jsonResponse({ error: 'Please provide a username (?username={username})' });
|
|
992
|
-
if (!token) return res.jsonResponse({ error: 'Please provide a valid token (&token={key}).' });
|
|
993
|
-
|
|
994
|
-
const u = username.toLowerCase(), now = Date.now();
|
|
995
|
-
|
|
996
|
-
rateLimits[u] = (rateLimits[u] || []).filter(ts => now - ts < 10000);
|
|
997
|
-
if (rateLimits[u].length > 50) {
|
|
998
|
-
const remainingTime = Math.ceil((rateLimits[u][0] + 10000 - now) / 1000);
|
|
999
|
-
return res.jsonResponse({ error: `Rate limit exceeded. Try again in ${remainingTime} seconds.` });
|
|
1000
|
-
}
|
|
1001
|
-
rateLimits[u].push(now);
|
|
1002
|
-
|
|
1003
|
-
if (privateChats[token]) return res.jsonResponse(privateChats[token]);
|
|
1004
|
-
|
|
1005
|
-
return res.jsonResponse({ error: 'Invalid or expired token.' });
|
|
1006
|
-
});
|
|
1007
|
-
|
|
1008
|
-
// Generate hash
|
|
1009
|
-
app.post('/:version/hash', (req, res) => {
|
|
1010
|
-
const { text, method } = req.body;
|
|
1011
|
-
const { version } = req.params;
|
|
1012
|
-
|
|
1013
|
-
if (!text) return res.jsonResponse({ error: 'Please provide a text (?text={text})' });
|
|
1014
|
-
if (!method) return res.jsonResponse({
|
|
1015
|
-
error: 'Please provide a valid hash algorithm (&method={algorithm})',
|
|
1016
|
-
documentation: `https://docs.sylvain.pro/${version}/en/hash`
|
|
1017
|
-
});
|
|
1018
|
-
|
|
1019
|
-
const methods = getHashes();
|
|
1020
|
-
if (!methods.includes(method)) return res.jsonResponse({ error: `Unsupported method. Use one of: ${methods.join(', ')}` });
|
|
1021
|
-
|
|
1022
|
-
const hash = createHash(method).update(text).digest('hex');
|
|
1023
|
-
res.jsonResponse({ method, hash });
|
|
1024
|
-
});
|
|
1025
|
-
|
|
1026
|
-
// Display a planning from an ICS file
|
|
1027
|
-
app.post('/:version/hyperplanning', async (req, res) => {
|
|
1028
|
-
const { url, detail } = req.body;
|
|
1029
|
-
|
|
1030
|
-
if (!url) return res.jsonResponse({ error: 'Please provide a valid ICS file URL (?url={URL})' });
|
|
1031
|
-
|
|
1032
|
-
try {
|
|
1033
|
-
const response = await fetch(url);
|
|
1034
|
-
if (!response.ok || !(response.headers.get('content-type') || '').includes('text/calendar')) return res.jsonResponse({ error: 'Invalid ICS file format.' });
|
|
1035
|
-
|
|
1036
|
-
const events = new ical.Component(ical.parse(await response.text()))
|
|
1037
|
-
.getAllSubcomponents('vevent')
|
|
1038
|
-
.map(e => {
|
|
1039
|
-
const evt = new ical.Event(e);
|
|
1040
|
-
const summary = evt.summary.split(' ').filter(part => part !== '-');
|
|
1041
|
-
const start = formatDate(evt.startDate.toJSDate());
|
|
1042
|
-
const end = formatDate(evt.endDate.toJSDate());
|
|
1043
|
-
|
|
1044
|
-
if (detail === 'full') {
|
|
1045
|
-
const desc = evt.description.split('\n').map(l => l.trim());
|
|
1046
|
-
const extract = (p) => (desc.find(l => l.startsWith(p)) || '').replace(p, '').trim();
|
|
1047
|
-
|
|
1048
|
-
return {
|
|
1049
|
-
summary,
|
|
1050
|
-
subject: extract('Matière :'),
|
|
1051
|
-
teacher: extract('Enseignant :'),
|
|
1052
|
-
classes: extract('Promotions :').split(', ').map(c => c.trim()),
|
|
1053
|
-
type: extract('Salle :') || undefined,
|
|
1054
|
-
start,
|
|
1055
|
-
end
|
|
1056
|
-
};
|
|
1057
|
-
}
|
|
1058
|
-
if (detail === 'list') return { summary, start, end };
|
|
1059
|
-
|
|
1060
|
-
return { summary: evt.summary, start, end };
|
|
1061
|
-
})
|
|
1062
|
-
.sort((a, b) => new Date(a.start) - new Date(b.start))
|
|
1063
|
-
.filter(e => new Date(e.end) >= new Date());
|
|
1064
|
-
|
|
1065
|
-
res.jsonResponse(events);
|
|
1066
|
-
} catch { res.jsonResponse({ error: 'Failed to parse ICS file.' }); }
|
|
1067
|
-
});
|
|
1068
|
-
|
|
1069
|
-
// Store tic tac toe games
|
|
1070
|
-
app.post('/:version/tic-tac-toe', (req, res) => {
|
|
1071
|
-
const { username, move, session, game } = req.body;
|
|
1072
|
-
|
|
1073
|
-
if (!username) return res.jsonResponse({ error: 'Please provide a username (?username={username})' });
|
|
1074
|
-
if (!move) return res.jsonResponse({ error: 'Please provide a valid move (&move={move})' });
|
|
1075
|
-
if (!session) return res.jsonResponse({ error: 'Please provide a valid session ID (&session={ID})' });
|
|
1076
|
-
if (!game) return res.jsonResponse({ error: 'Please provide a valid game ID (&game={ID})' });
|
|
1077
|
-
|
|
1078
|
-
const u = username.toLowerCase(), now = Date.now();
|
|
1079
|
-
const play = { username, move, session };
|
|
1080
|
-
const validMoves = ['1-1', '1-2', '1-3', '2-1', '2-2', '2-3', '3-1', '3-2', '3-3'];
|
|
1081
|
-
|
|
1082
|
-
if (!validMoves.includes(move)) return res.jsonResponse({ error: 'Invalid move. Please provide a valid move (e.g., 1-1, 2-2, 3-3).' });
|
|
1083
|
-
|
|
1084
|
-
rateLimits[u] = (rateLimits[u] || []).filter(ts => now - ts < 10000);
|
|
1085
|
-
if (rateLimits[u].length > 50) {
|
|
1086
|
-
const remainingTime = Math.ceil((rateLimits[u][0] + 10000 - now) / 1000);
|
|
1087
|
-
return res.jsonResponse({ error: `Rate limit exceeded. Try again in ${remainingTime} seconds.` });
|
|
1088
|
-
}
|
|
1089
|
-
rateLimits[u].push(now);
|
|
1090
|
-
|
|
1091
|
-
if (sessions[u] && sessions[u].user !== session) return res.jsonResponse({ error: 'Session ID mismatch' });
|
|
1092
|
-
|
|
1093
|
-
games[game] = games[game] || [];
|
|
1094
|
-
|
|
1095
|
-
const players = [...new Set(games[game].map(play => play.username))];
|
|
1096
|
-
if (players.length >= 2 && !players.includes(username)) return res.jsonResponse({ error: 'Game is full, you can only watch.' });
|
|
1097
|
-
if (games[game].length > 0 && games[game][games[game].length - 1].username === username) return res.jsonResponse({ error: 'Please wait for the other player to make a move.' });
|
|
1098
|
-
if (games[game].some(play => play.move === move)) return res.jsonResponse({ error: 'Move already made. Please choose a different move.' });
|
|
1099
|
-
|
|
1100
|
-
games[game].push(play);
|
|
1101
|
-
|
|
1102
|
-
const result = checkGame(games[game]);
|
|
1103
|
-
if (result.winner || result.tie) {
|
|
1104
|
-
setTimeout(() => delete games[game], 600000);
|
|
1105
|
-
return res.jsonResponse({
|
|
1106
|
-
message: `Move sent successfully. ${result.winner ? result.winner + ' wins. ' + result.loser + ' loses.' : 'It\'s a tie.'}`,
|
|
1107
|
-
...result
|
|
1108
|
-
});
|
|
1109
|
-
}
|
|
1110
|
-
if (!result.winner && !result.tie) setTimeout(() => delete games[game], 3600000);
|
|
1111
|
-
|
|
1112
|
-
sessions[u] = sessions[u] || { user: session, last: now };
|
|
1113
|
-
sessions[u].last = now;
|
|
1114
|
-
|
|
1115
|
-
setTimeout(() => { if (now - sessions[u].last >= 3600000) delete sessions[u]; }, 3600000);
|
|
1116
|
-
|
|
1117
|
-
res.jsonResponse({ message: 'Move sent successfully' });
|
|
1118
|
-
});
|
|
1119
|
-
|
|
1120
|
-
// Display a tic tac toe game with a token
|
|
1121
|
-
app.post('/:version/tic-tac-toe/fetch', (req, res) => {
|
|
1122
|
-
const { username, game } = req.body;
|
|
1123
|
-
|
|
1124
|
-
if (!username) return res.jsonResponse({ error: 'Please provide a username (?username={username})' });
|
|
1125
|
-
|
|
1126
|
-
const ID = game || genID();
|
|
1127
|
-
const u = username.toLowerCase(), now = Date.now();
|
|
1128
|
-
|
|
1129
|
-
rateLimits[u] = (rateLimits[u] || []).filter(ts => now - ts < 10000);
|
|
1130
|
-
if (rateLimits[u].length > 50) {
|
|
1131
|
-
const remainingTime = Math.ceil((rateLimits[u][0] + 10000 - now) / 1000);
|
|
1132
|
-
return res.jsonResponse({ error: `Rate limit exceeded. Try again in ${remainingTime} seconds.` });
|
|
1133
|
-
}
|
|
1134
|
-
rateLimits[u].push(now);
|
|
1135
|
-
|
|
1136
|
-
if (!games[ID]) games[ID] = [];
|
|
1137
|
-
|
|
1138
|
-
const data = games[ID];
|
|
1139
|
-
const last = data.length ? data[data.length - 1].username : null;
|
|
1140
|
-
const players = [...new Set(data.map(p => p.username))];
|
|
1141
|
-
const turn = players.find(p => p !== last);
|
|
1142
|
-
const result = data.length ? checkGame(data) : {};
|
|
1143
|
-
|
|
1144
|
-
res.jsonResponse({ game: data, turn, ID, ...result });
|
|
1145
|
-
});
|
|
1146
|
-
|
|
1147
|
-
// Generate Token
|
|
1148
|
-
app.post('/:version/token', (req, res) => {
|
|
1149
|
-
let { len, type } = req.body;
|
|
1150
|
-
|
|
1151
|
-
len = parseInt(len || 24, 10);
|
|
1152
|
-
type = type ? type.toLowerCase() : 'alpha';
|
|
1153
|
-
|
|
1154
|
-
if (isNaN(len) || len < 0) return res.jsonResponse({ error: 'Invalid number.' });
|
|
1155
|
-
if (len > 4096) return res.jsonResponse({ error: 'Length cannot exceed 4096.' });
|
|
1156
|
-
if (len < 12) return res.jsonResponse({ error: 'Length cannot be less than 12.' });
|
|
1157
|
-
|
|
1158
|
-
const token = {
|
|
1159
|
-
alpha: genToken('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', len),
|
|
1160
|
-
alphanum: genToken('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', len),
|
|
1161
|
-
base64: randomBytes(len).toString('base64').slice(0, len),
|
|
1162
|
-
hex: randomBytes(len).toString('hex').slice(0, len),
|
|
1163
|
-
num: genToken('0123456789', len),
|
|
1164
|
-
punct: genToken('!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~', len),
|
|
1165
|
-
urlsafe: genToken('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_', len),
|
|
1166
|
-
uuid: v4().replace(/-/g, '').slice(0, len)
|
|
1167
|
-
}[type] || genToken('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', len);
|
|
1168
|
-
|
|
1169
|
-
res.jsonResponse({ token });
|
|
1170
|
-
});
|
|
1171
|
-
|
|
1172
|
-
// ----------- ----------- SERVER SETUP ----------- ----------- //
|
|
1173
|
-
|
|
1174
|
-
app.listen(3000, () => console.log('API is running on\n - http://127.0.0.1:3000\n - http://localhost:3000'));
|
|
1
|
+
// Import modules
|
|
2
|
+
import * as apiv3 from './modules/v3.js';
|
|
3
|
+
|
|
4
|
+
// Import dependencies
|
|
5
|
+
import cors from 'cors';
|
|
6
|
+
import dotenv from 'dotenv';
|
|
7
|
+
import express from 'express';
|
|
8
|
+
import fetch from 'node-fetch';
|
|
9
|
+
import { urlencoded, json } from 'express';
|
|
10
|
+
import { dirname, join } from 'path';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = dirname(__filename);
|
|
15
|
+
const app = express();
|
|
16
|
+
|
|
17
|
+
// Define allowed versions & endpoints for each version
|
|
18
|
+
const v1 = {
|
|
19
|
+
get: [
|
|
20
|
+
{ name: 'algorithms', path: '/algorithms?method={algorithm}&value={value}(&value2={value2})' },
|
|
21
|
+
{ name: 'captcha', path: '/captcha?text={text}' },
|
|
22
|
+
{ name: 'color', path: '/color' },
|
|
23
|
+
{ name: 'convert', path: '/convert?value={value}&from={unit}&to={unit}' },
|
|
24
|
+
{ name: 'domain', path: '/domain' },
|
|
25
|
+
{ name: 'infos', path: '/infos' },
|
|
26
|
+
{ name: 'personal', path: '/personal' },
|
|
27
|
+
{ name: 'qrcode', path: '/qrcode?url={URL}' },
|
|
28
|
+
{ name: 'username', path: '/username' },
|
|
29
|
+
{ name: 'website', path: '/website' }
|
|
30
|
+
],
|
|
31
|
+
post: [
|
|
32
|
+
{ name: 'token', path: '/token' }
|
|
33
|
+
]
|
|
34
|
+
};
|
|
35
|
+
const v2 = {
|
|
36
|
+
get: [
|
|
37
|
+
...v1.get,
|
|
38
|
+
{ name: 'chat', path: '/chat' }
|
|
39
|
+
],
|
|
40
|
+
post: [
|
|
41
|
+
...v1.post,
|
|
42
|
+
{
|
|
43
|
+
name: 'chat',
|
|
44
|
+
children: {
|
|
45
|
+
chat: '/chat',
|
|
46
|
+
private: '/chat/private'
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
{ name: 'hash', path: '/hash' },
|
|
50
|
+
{
|
|
51
|
+
name: 'tic_tac_toe',
|
|
52
|
+
children: {
|
|
53
|
+
tic_tac_toe: '/tic-tac-toe',
|
|
54
|
+
fetch: '/tic-tac-toe/fetch'
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{ name: 'token', path: '/token' }
|
|
58
|
+
]
|
|
59
|
+
};
|
|
60
|
+
const v3 = {
|
|
61
|
+
get: [
|
|
62
|
+
...v2.get,
|
|
63
|
+
{ name: 'levenshtein', path: '/levenshtein?str1={string}&str2={string}' },
|
|
64
|
+
{ name: 'time', path: '/time(?type={type}&start={timestamp}&end={timestamp}&format={format}&timezone={timezone})' },
|
|
65
|
+
],
|
|
66
|
+
post: [
|
|
67
|
+
...v2.post,
|
|
68
|
+
{ name: 'hyperplanning', path: '/hyperplanning' }
|
|
69
|
+
]
|
|
70
|
+
};
|
|
71
|
+
const versions = {
|
|
72
|
+
v1: {
|
|
73
|
+
endpoints: v1,
|
|
74
|
+
modules: apiv3
|
|
75
|
+
},
|
|
76
|
+
v2: {
|
|
77
|
+
endpoints: v2,
|
|
78
|
+
modules: apiv3
|
|
79
|
+
},
|
|
80
|
+
v3: {
|
|
81
|
+
endpoints: v3,
|
|
82
|
+
modules: apiv3
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// API storage
|
|
87
|
+
const logs = [], ipLimits = {};
|
|
88
|
+
|
|
89
|
+
// Chat storage
|
|
90
|
+
const chatStorage = {
|
|
91
|
+
messages: [],
|
|
92
|
+
privateChats: {},
|
|
93
|
+
sessions: {},
|
|
94
|
+
rateLimits: {}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Tic-Tac-Toe storage
|
|
98
|
+
const ticTacToeStorage = {
|
|
99
|
+
games: {},
|
|
100
|
+
sessions: {},
|
|
101
|
+
rateLimits: {}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// Define global variables
|
|
105
|
+
let contributions, lastFetch = 0, requests = 0, requestLimit, resetTime = Date.now() + 3600000;
|
|
106
|
+
|
|
107
|
+
// ----------- ----------- MIDDLEWARES SETUP ----------- ----------- //
|
|
108
|
+
|
|
109
|
+
dotenv.config();
|
|
110
|
+
|
|
111
|
+
// CORS & Express setup
|
|
112
|
+
app.set('trust proxy', 1);
|
|
113
|
+
app.use(cors({ methods: ['GET', 'POST'] }));
|
|
114
|
+
app.use(urlencoded({ extended: true }));
|
|
115
|
+
app.use(json());
|
|
116
|
+
|
|
117
|
+
// Set favicon for API
|
|
118
|
+
app.use('/favicon.ico', express.static(join(__dirname, 'src', 'favicon.ico')));
|
|
119
|
+
|
|
120
|
+
// Display robots.txt
|
|
121
|
+
app.use('/robots.txt', express.static(join(__dirname, 'robots.txt')));
|
|
122
|
+
|
|
123
|
+
// Return formatted JSON
|
|
124
|
+
app.use((req, res, next) => {
|
|
125
|
+
res.setHeader('Content-Type', 'application/json');
|
|
126
|
+
res.jsonResponse = (data) => {
|
|
127
|
+
res.send(JSON.stringify(data, null, 2));
|
|
128
|
+
};
|
|
129
|
+
next();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Too many requests
|
|
133
|
+
app.use((req, res, next) => {
|
|
134
|
+
const ip = req.headers['cf-connecting-ip'] || req.socket.remoteAddress;
|
|
135
|
+
const token = req.headers.authorization?.split(' ')[1] || '';
|
|
136
|
+
|
|
137
|
+
const now = Date.now();
|
|
138
|
+
const minute = Math.floor(now / 60000) % 60;
|
|
139
|
+
const hour = Math.floor(now / 3600000) % 24;
|
|
140
|
+
|
|
141
|
+
const business = process.env.BUSINESS_TOKEN_LIST?.split(' ') || [];
|
|
142
|
+
const pro = process.env.PRO_TOKEN_LIST?.split(' ') || [];
|
|
143
|
+
const advanced = process.env.ADVANCED_TOKEN_LIST?.split(' ') || [];
|
|
144
|
+
|
|
145
|
+
if (token && ![...business, ...pro, ...advanced].includes(token) || token === 'undefined') {
|
|
146
|
+
return res.status(401).jsonResponse({
|
|
147
|
+
message: 'Unauthorized',
|
|
148
|
+
error: 'Invalid token.',
|
|
149
|
+
status: '401'
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (business.includes(token) && !business.includes('undefined')) requestLimit = process.env.BUSINESS_LIMIT;
|
|
154
|
+
else if (pro.includes(token) && !pro.includes('undefined')) requestLimit = process.env.PRO_LIMIT;
|
|
155
|
+
else if (advanced.includes(token) && !advanced.includes('undefined')) requestLimit = process.env.ADVANCED_LIMIT;
|
|
156
|
+
else requestLimit = process.env.DEFAULT_LIMIT;
|
|
157
|
+
|
|
158
|
+
if (now > resetTime) requests = 0, resetTime = now + 3600000;
|
|
159
|
+
if (++requests > Math.max(process.env.GLOBAL_LIMIT, requestLimit)) {
|
|
160
|
+
return res.status(429).jsonResponse({ message: 'Too Many Requests' });
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (['__proto__', 'constructor', 'prototype'].includes(ip)) {
|
|
164
|
+
return res.status(400).jsonResponse({ message: 'Invalid IP address' });
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (!ipLimits[ip]) ipLimits[ip] = {};
|
|
168
|
+
if (!ipLimits[ip][hour]) ipLimits[ip][hour] = {};
|
|
169
|
+
ipLimits[ip][hour][minute] = (ipLimits[ip][hour][minute] || 0) + 1;
|
|
170
|
+
|
|
171
|
+
if (ipLimits[ip][hour][minute] > requestLimit) {
|
|
172
|
+
return res.status(429).jsonResponse({
|
|
173
|
+
message: 'Too Many Requests (IP limited), authenticate to increase the limit',
|
|
174
|
+
error: `You have exceeded the limit of ${requestLimit} requests per hour.`,
|
|
175
|
+
reset: `Reset in ${((resetTime - now) / 60000).toFixed(0)} minutes.`,
|
|
176
|
+
status: '429'
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
Object.keys(ipLimits[ip]).forEach(h => { if (h != hour) delete ipLimits[ip][h]; });
|
|
181
|
+
|
|
182
|
+
next();
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Save and send logs
|
|
186
|
+
app.use((req, res, next) => {
|
|
187
|
+
if (req.method === 'HEAD') return next();
|
|
188
|
+
if (req.originalUrl === '/logs') return next();
|
|
189
|
+
|
|
190
|
+
const ip = req.headers['cf-connecting-ip'] || req.socket.remoteAddress;
|
|
191
|
+
|
|
192
|
+
const startTime = Date.now();
|
|
193
|
+
const timestamp = new Date().toISOString();
|
|
194
|
+
const method = req.method;
|
|
195
|
+
const url = req.originalUrl;
|
|
196
|
+
const platform = req.headers['sec-ch-ua-platform']?.replace(/"/g, '');
|
|
197
|
+
|
|
198
|
+
res.on('finish', () => {
|
|
199
|
+
const status = res.statusCode === 304 ? 200 : res.statusCode;
|
|
200
|
+
const duration = `${Date.now() - startTime}ms`;
|
|
201
|
+
|
|
202
|
+
logs.push({ timestamp, method, url, status, duration, platform });
|
|
203
|
+
console.log(`[${new Date().toISOString()}] ${method} ${url} ${res.statusCode} - ${duration} - ${ip}`);
|
|
204
|
+
if (logs.length > 1000) logs.shift();
|
|
205
|
+
});
|
|
206
|
+
next();
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Internal Server Error
|
|
210
|
+
app.use((err, req, res, next) => {
|
|
211
|
+
console.error(err.stack);
|
|
212
|
+
res.status(500).jsonResponse({
|
|
213
|
+
message: 'Internal Server Error',
|
|
214
|
+
error: err.message,
|
|
215
|
+
documentation: 'https://docs.sylvain.pro',
|
|
216
|
+
status: '500'
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// Check if version exists
|
|
221
|
+
app.use('/:version', (req, res, next) => {
|
|
222
|
+
const { version } = req.params;
|
|
223
|
+
const latest = Object.keys(versions).pop();
|
|
224
|
+
const endpoint = req.originalUrl.split('/').slice(2).join('/');
|
|
225
|
+
|
|
226
|
+
req.version = version;
|
|
227
|
+
req.latest = latest;
|
|
228
|
+
|
|
229
|
+
if (['latest', 'fr', 'en'].includes(version)) {
|
|
230
|
+
return res.redirect(endpoint ? `/${latest}/${endpoint}` : `/${latest}`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (version === 'logs') return res.jsonResponse(logs);
|
|
234
|
+
|
|
235
|
+
if (!versions[version] && version !== 'logs') {
|
|
236
|
+
return res.status(404).jsonResponse({
|
|
237
|
+
message: 'Not Found',
|
|
238
|
+
error: `Invalid API version (${version}).`,
|
|
239
|
+
documentation: `https://docs.sylvain.pro/${latest}`,
|
|
240
|
+
status: '404'
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
req.module = versions[version].modules;
|
|
245
|
+
|
|
246
|
+
if (!req.module) {
|
|
247
|
+
return res.status(404).jsonResponse({
|
|
248
|
+
message: 'Not Found',
|
|
249
|
+
error: `Module not found for version ${version}.`,
|
|
250
|
+
documentation: `https://docs.sylvain.pro/${latest}`,
|
|
251
|
+
status: '404'
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
next();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Check if endpoint exists
|
|
259
|
+
app.use('/:version/:endpoint', (req, res, next) => {
|
|
260
|
+
const { version, endpoint } = req.params;
|
|
261
|
+
|
|
262
|
+
req.version = version;
|
|
263
|
+
req.endpoint = endpoint;
|
|
264
|
+
|
|
265
|
+
if (!req.endpoint) {
|
|
266
|
+
return res.status(404).jsonResponse({
|
|
267
|
+
message: 'Not Found',
|
|
268
|
+
error: `Endpoint '${endpoint}' does not exist in ${req.version}.`,
|
|
269
|
+
documentation: `https://docs.sylvain.pro/${versions[version.length - 1]}`,
|
|
270
|
+
status: '404'
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
next();
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// ----------- ----------- MAIN ENDPOINTS ----------- ----------- //
|
|
277
|
+
|
|
278
|
+
// Main route
|
|
279
|
+
app.get('/', (req, res) => {
|
|
280
|
+
const base = `${req.protocol}://${req.get('host')}`;
|
|
281
|
+
const links = Object.keys(versions).reduce((link, version) => {
|
|
282
|
+
link[version] = `${base}/${version}`;
|
|
283
|
+
return link;
|
|
284
|
+
}, {});
|
|
285
|
+
|
|
286
|
+
res.jsonResponse({
|
|
287
|
+
documentation: 'https://docs.sylvain.pro',
|
|
288
|
+
latest: `${base}/latest`,
|
|
289
|
+
logs: `${base}/logs`,
|
|
290
|
+
versions: links
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// Display version information
|
|
295
|
+
app.get('/:version', (req, res) => {
|
|
296
|
+
const { version } = req.params;
|
|
297
|
+
|
|
298
|
+
if (!versions[version]) {
|
|
299
|
+
return res.status(404).jsonResponse({
|
|
300
|
+
message: 'Not Found',
|
|
301
|
+
error: `Invalid API version (${version}).`,
|
|
302
|
+
documentation: `https://docs.sylvain.pro/${versions[versions.length - 1]}`,
|
|
303
|
+
status: '404'
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const endpoints = Object.keys(versions[version].endpoints).reduce((acc, method) => {
|
|
308
|
+
acc[method] = versions[version].endpoints[method]
|
|
309
|
+
.filter(({ name }) => name !== 'website')
|
|
310
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
311
|
+
.reduce((group, endpoint) => {
|
|
312
|
+
if (endpoint.children) {
|
|
313
|
+
group[endpoint.name] = Object.keys(endpoint.children)
|
|
314
|
+
.sort((a, b) => a.localeCompare(b))
|
|
315
|
+
.reduce((childGroup, childName) => {
|
|
316
|
+
childGroup[childName] = `/${version}${endpoint.children[childName]}`;
|
|
317
|
+
return childGroup;
|
|
318
|
+
}, {});
|
|
319
|
+
} else {
|
|
320
|
+
group[endpoint.name] = `/${version}${endpoint.path}`;
|
|
321
|
+
}
|
|
322
|
+
return group;
|
|
323
|
+
}, {});
|
|
324
|
+
return acc;
|
|
325
|
+
}, {});
|
|
326
|
+
|
|
327
|
+
res.jsonResponse({
|
|
328
|
+
version,
|
|
329
|
+
documentation: `https://docs.sylvain.pro/${version}`,
|
|
330
|
+
endpoints
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// ----------- ----------- GET ENDPOINTS ----------- ----------- //
|
|
335
|
+
|
|
336
|
+
// Algorithms
|
|
337
|
+
app.get('/:version/algorithms', (req, res) => {
|
|
338
|
+
const { method, value, value2 } = req.query;
|
|
339
|
+
const { version } = req.params;
|
|
340
|
+
|
|
341
|
+
if (!req.module.algorithms || !req.module.algorithms[method]) {
|
|
342
|
+
return res.jsonResponse({
|
|
343
|
+
error: 'Please provide a valid algorithm (?method={algorithm})',
|
|
344
|
+
documentation: `https://docs.sylvain.pro/${version}/en/algorithms`
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
try {
|
|
349
|
+
const answer = req.module.algorithms[method](value, value2);
|
|
350
|
+
res.jsonResponse({ answer });
|
|
351
|
+
} catch (err) {
|
|
352
|
+
res.jsonResponse({
|
|
353
|
+
error: err.message,
|
|
354
|
+
documentation: `https://docs.sylvain.pro/${version}/en/algorithms`
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
// Generate captcha
|
|
360
|
+
app.get('/:version/captcha', (req, res) => {
|
|
361
|
+
const text = req.query.text;
|
|
362
|
+
|
|
363
|
+
if (!text) return res.jsonResponse({ error: 'Please provide a valid argument (?text={text})' });
|
|
364
|
+
|
|
365
|
+
try {
|
|
366
|
+
const result = req.module.captcha(text);
|
|
367
|
+
res.type('png').send(result);
|
|
368
|
+
} catch (err) {
|
|
369
|
+
res.jsonResponse({
|
|
370
|
+
error: err.message,
|
|
371
|
+
documentation: `https://docs.sylvain.pro/${req.version}/en/captcha`
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
// Display stored data
|
|
377
|
+
app.get('/:version/chat', (req, res) => {
|
|
378
|
+
try {
|
|
379
|
+
const messages = req.module.chat('fetch', {
|
|
380
|
+
username: 'system',
|
|
381
|
+
storage: chatStorage
|
|
382
|
+
});
|
|
383
|
+
res.jsonResponse(messages);
|
|
384
|
+
} catch (err) {
|
|
385
|
+
res.jsonResponse({ error: err.message });
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
// GET private chat error
|
|
390
|
+
app.get('/:version/chat/private', (req, res) => {
|
|
391
|
+
res.jsonResponse({ error: 'This endpoint only supports POST requests.' });
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
// Generate color
|
|
395
|
+
app.get('/:version/color', (req, res) => {
|
|
396
|
+
try {
|
|
397
|
+
const result = req.module.color();
|
|
398
|
+
res.jsonResponse(result);
|
|
399
|
+
} catch (err) {
|
|
400
|
+
res.jsonResponse({
|
|
401
|
+
error: err.message,
|
|
402
|
+
documentation: `https://docs.sylvain.pro/${req.version}/en/color`
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
// Convert units
|
|
408
|
+
app.get('/:version/convert', (req, res) => {
|
|
409
|
+
const { value, from, to } = req.query;
|
|
410
|
+
|
|
411
|
+
if (!value || isNaN(value)) return res.jsonResponse({ error: 'Please provide a valid value (?value={value})' });
|
|
412
|
+
if (!from) return res.jsonResponse({ error: 'Please provide a valid source unit (&from={unit})' });
|
|
413
|
+
if (!to) return res.jsonResponse({ error: 'Please provide a valid target unit (&to={unit})' });
|
|
414
|
+
|
|
415
|
+
try {
|
|
416
|
+
const result = req.module.convert(value, from, to);
|
|
417
|
+
res.jsonResponse(result);
|
|
418
|
+
} catch (err) {
|
|
419
|
+
res.jsonResponse({
|
|
420
|
+
error: err.message,
|
|
421
|
+
documentation: `https://docs.sylvain.pro/${req.version}/en/convert`
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
// Generate domain informations
|
|
427
|
+
app.get('/:version/domain', (req, res) => {
|
|
428
|
+
try {
|
|
429
|
+
const result = req.module.domain();
|
|
430
|
+
res.jsonResponse(result);
|
|
431
|
+
} catch (err) {
|
|
432
|
+
res.jsonResponse({
|
|
433
|
+
error: err.message,
|
|
434
|
+
documentation: `https://docs.sylvain.pro/${req.version}/en/domain`
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
// GET planning error
|
|
440
|
+
app.get('/:version/hyperplanning', (req, res) => {
|
|
441
|
+
res.jsonResponse({ error: 'This endpoint only supports POST requests.' });
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
// GET hash error
|
|
445
|
+
app.get('/:version/hash', (req, res) => {
|
|
446
|
+
res.jsonResponse({ error: 'This endpoint only supports POST requests.' });
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
// Display API informations
|
|
450
|
+
app.get('/:version/infos', (req, res) => {
|
|
451
|
+
const endpoints = Object.values(versions[req.version].endpoints).flat().reduce((total, endpoint) => {
|
|
452
|
+
if (endpoint.children) return total + Object.keys(endpoint.children).length;
|
|
453
|
+
return total + 1;
|
|
454
|
+
}, 0);
|
|
455
|
+
|
|
456
|
+
res.jsonResponse({
|
|
457
|
+
endpoints,
|
|
458
|
+
last_version: Object.keys(versions).pop(),
|
|
459
|
+
documentation: 'https://docs.sylvain.pro',
|
|
460
|
+
github: 'https://github.com/20syldev/api',
|
|
461
|
+
creation: 'November 25th 2024',
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
// Calculate Levenshtein distance
|
|
466
|
+
app.get('/:version/levenshtein', (req, res) => {
|
|
467
|
+
const { str1, str2 } = req.query;
|
|
468
|
+
|
|
469
|
+
if (!str1 || typeof str1 !== 'string') return res.jsonResponse({ error: 'Please provide a first string (?str1={string})' });
|
|
470
|
+
if (!str2 || typeof str2 !== 'string') return res.jsonResponse({ error: 'Please provide a second string (&str2={string})' });
|
|
471
|
+
|
|
472
|
+
try {
|
|
473
|
+
const result = req.module.levenshtein(str1, str2);
|
|
474
|
+
res.jsonResponse(result);
|
|
475
|
+
} catch (err) {
|
|
476
|
+
res.jsonResponse({
|
|
477
|
+
error: err.message,
|
|
478
|
+
documentation: `https://docs.sylvain.pro/${req.version}/en/levenshtein`
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
// Generate personal data
|
|
484
|
+
app.get('/:version/personal', (req, res) => {
|
|
485
|
+
try {
|
|
486
|
+
const result = req.module.personal();
|
|
487
|
+
res.jsonResponse(result);
|
|
488
|
+
} catch (err) {
|
|
489
|
+
res.jsonResponse({
|
|
490
|
+
error: err.message,
|
|
491
|
+
documentation: `https://docs.sylvain.pro/${req.version}/en/personal`
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
// Generate QR Code
|
|
497
|
+
app.get('/:version/qrcode', async (req, res) => {
|
|
498
|
+
const { url } = req.query;
|
|
499
|
+
|
|
500
|
+
if (!url) return res.jsonResponse({ error: 'Please provide a valid url (?url={URL})' });
|
|
501
|
+
|
|
502
|
+
try {
|
|
503
|
+
const result = await req.module.qrcode(url);
|
|
504
|
+
res.jsonResponse(result);
|
|
505
|
+
} catch (err) {
|
|
506
|
+
res.jsonResponse({
|
|
507
|
+
error: err.message,
|
|
508
|
+
documentation: `https://docs.sylvain.pro/${req.version}/en/qrcode`
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
// GET tic-tac-toe game error
|
|
514
|
+
app.get('/:version/tic-tac-toe', (req, res) => {
|
|
515
|
+
res.jsonResponse({ error: 'This endpoint only supports POST requests.' });
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
// GET tic-tac-toe fetch error
|
|
519
|
+
app.get('/:version/tic-tac-toe/fetch', (req, res) => {
|
|
520
|
+
res.jsonResponse({ error: 'This endpoint only supports POST requests.' });
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
// Display or generate time informations
|
|
524
|
+
app.get('/:version/time', (req, res) => {
|
|
525
|
+
const { type = 'live', start, end, format, timezone } = req.query;
|
|
526
|
+
|
|
527
|
+
const validFormats = ['iso', 'utc', 'timestamp', 'locale', 'date', 'time', 'year', 'month', 'day', 'hour', 'minute', 'second', 'ms', 'dayOfWeek', 'dayOfYear', 'weekNumber', 'timezone', 'timezoneOffset'];
|
|
528
|
+
const validTimezones = ['UTC', 'America/New_York', 'Europe/Paris', 'Asia/Tokyo', 'Australia/Sydney'];
|
|
529
|
+
|
|
530
|
+
if (type !== 'live' && type !== 'random') return res.jsonResponse({ error: 'Please provide a valid type (?type={type})' });
|
|
531
|
+
if (start && !Date.parse(start)) return res.jsonResponse({ error: 'Please provide a valid start date (?start={YYYY-MM-DD})' });
|
|
532
|
+
if (end && !Date.parse(end)) return res.jsonResponse({ error: 'Please provide a valid end date (?end={YYYY-MM-DD})' });
|
|
533
|
+
if (format && !validFormats.includes(format)) return res.jsonResponse({ error: 'Please provide a valid format (?format={format})' });
|
|
534
|
+
if (timezone && !validTimezones.includes(timezone)) return res.jsonResponse({ error: 'Please provide a valid timezone (?timezone={timezone})' });
|
|
535
|
+
|
|
536
|
+
try {
|
|
537
|
+
const time = req.module.time(type, start, end, format, timezone);
|
|
538
|
+
res.jsonResponse(time);
|
|
539
|
+
} catch (err) {
|
|
540
|
+
res.jsonResponse({
|
|
541
|
+
error: err.message,
|
|
542
|
+
documentation: `https://docs.sylvain.pro/${req.version}/en/time`
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
// GET token error
|
|
548
|
+
app.get('/:version/token', (req, res) => {
|
|
549
|
+
res.jsonResponse({ error: 'This endpoint only supports POST requests.' });
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
// Generate username
|
|
553
|
+
app.get('/:version/username', (req, res) => {
|
|
554
|
+
try {
|
|
555
|
+
const result = req.module.username();
|
|
556
|
+
res.jsonResponse(result);
|
|
557
|
+
} catch (err) {
|
|
558
|
+
res.jsonResponse({
|
|
559
|
+
error: err.message,
|
|
560
|
+
documentation: `https://docs.sylvain.pro/${req.version}/en/username`
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
// Display informations for owner's website
|
|
566
|
+
app.get('/:version/website', async (req, res) => {
|
|
567
|
+
const currentTime = Date.now();
|
|
568
|
+
|
|
569
|
+
if (currentTime - lastFetch >= 10 * 60 * 1000) {
|
|
570
|
+
try {
|
|
571
|
+
const username = '20syldev';
|
|
572
|
+
const token = process.env.GITHUB_TOKEN;
|
|
573
|
+
const today = new Date().toISOString().split('T')[0];
|
|
574
|
+
const monthFirst = new Date(new Date().getFullYear(), new Date().getMonth(), 1).toISOString().split('T')[0];
|
|
575
|
+
const lastYear = new Date(new Date().setFullYear(new Date().getFullYear() - 1)).toISOString().split('T')[0];
|
|
576
|
+
|
|
577
|
+
const query = `
|
|
578
|
+
{
|
|
579
|
+
user(login: "${username}") {
|
|
580
|
+
contributionsCollection(from: "${today}T00:00:00Z") {
|
|
581
|
+
contributionCalendar {
|
|
582
|
+
totalContributions
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
contributions_month: contributionsCollection(from: "${monthFirst}T00:00:00Z") {
|
|
586
|
+
contributionCalendar {
|
|
587
|
+
totalContributions
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
contributions_year: contributionsCollection(from: "${lastYear}T00:00:00Z") {
|
|
591
|
+
contributionCalendar {
|
|
592
|
+
totalContributions
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}`;
|
|
597
|
+
|
|
598
|
+
const apiResponse = await fetch('https://api.github.com/graphql', {
|
|
599
|
+
method: 'POST',
|
|
600
|
+
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
|
|
601
|
+
body: JSON.stringify({ query })
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
if (!apiResponse.ok) throw new Error('Error fetching data.');
|
|
605
|
+
|
|
606
|
+
const data = await apiResponse.json();
|
|
607
|
+
const user = data?.data?.user;
|
|
608
|
+
|
|
609
|
+
contributions = {
|
|
610
|
+
today: user?.contributionsCollection?.contributionCalendar?.totalContributions || 0,
|
|
611
|
+
month: user?.contributions_month?.contributionCalendar?.totalContributions || 0,
|
|
612
|
+
year: user?.contributions_year?.contributionCalendar?.totalContributions || 0,
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
lastFetch = currentTime;
|
|
616
|
+
} catch { contributions = { today: 0, month: 0, year: 0 }; }
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
res.jsonResponse({
|
|
620
|
+
versions: {
|
|
621
|
+
api: process.env.API,
|
|
622
|
+
cdn: process.env.CDN,
|
|
623
|
+
coop_api: process.env.COOP_API,
|
|
624
|
+
coop_status: process.env.COOP_STATUS,
|
|
625
|
+
chat: process.env.CHAT,
|
|
626
|
+
digit: process.env.DIGIT,
|
|
627
|
+
doc_coopbot: process.env.DOC_COOPBOT,
|
|
628
|
+
docs: process.env.DOCS,
|
|
629
|
+
donut: process.env.DONUT,
|
|
630
|
+
drawio_plugin: process.env.DRAWIO_PLUGIN,
|
|
631
|
+
flowers: process.env.FLOWERS,
|
|
632
|
+
gemsync: process.env.GEMSYNC,
|
|
633
|
+
gitsite: process.env.GITSITE,
|
|
634
|
+
lebonchar: process.env.LEBONCHAR,
|
|
635
|
+
logs: process.env.LOGS,
|
|
636
|
+
logvault: process.env.LOGVAULT,
|
|
637
|
+
lyah: process.env.LYAH,
|
|
638
|
+
minify: process.env.MINIFY,
|
|
639
|
+
morpion: process.env.MORPION,
|
|
640
|
+
nitrogen: process.env.NITROGEN,
|
|
641
|
+
old_database: process.env.OLD_DATABASE,
|
|
642
|
+
php: process.env.PHP,
|
|
643
|
+
ping: process.env.PING,
|
|
644
|
+
portfolio: process.env.PORTFOLIO,
|
|
645
|
+
python_api: process.env.PYTHON_API,
|
|
646
|
+
readme: process.env.README,
|
|
647
|
+
terminal: process.env.TERMINAL,
|
|
648
|
+
wrkit: process.env.WRKIT,
|
|
649
|
+
zpki: process.env.ZPKI
|
|
650
|
+
},
|
|
651
|
+
patched_projects: process.env.PATCH?.split(' ') || [],
|
|
652
|
+
updated_projects: process.env.RECENT?.split(' ') || [],
|
|
653
|
+
new_projects: process.env.NEW?.split(' ') || [],
|
|
654
|
+
sub_domains: process.env.DOMAINS?.split(' ') || [],
|
|
655
|
+
stats: {
|
|
656
|
+
1: process.env.STATS1,
|
|
657
|
+
2: process.env.STATS2,
|
|
658
|
+
3: process.env.STATS3,
|
|
659
|
+
4: process.env.STATS4,
|
|
660
|
+
5: Object.keys(ipLimits).length,
|
|
661
|
+
today: contributions.today.toString(),
|
|
662
|
+
this_month: contributions.month.toString(),
|
|
663
|
+
last_year: contributions.year.toString(),
|
|
664
|
+
},
|
|
665
|
+
notif_tag: process.env.TAG,
|
|
666
|
+
active: process.env.ACTIVE
|
|
667
|
+
});
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
// ----------- ----------- POST ENDPOINTS ----------- ----------- //
|
|
671
|
+
|
|
672
|
+
// Store chat messages
|
|
673
|
+
app.post('/:version/chat', (req, res) => {
|
|
674
|
+
const { username, message, timestamp, session, token } = req.body;
|
|
675
|
+
|
|
676
|
+
if (!username) return res.jsonResponse({ error: 'Please provide a username (?username={username})' });
|
|
677
|
+
if (!message) return res.jsonResponse({ error: 'Please provide a message (&message={message})' });
|
|
678
|
+
if (!session) return res.jsonResponse({ error: 'Please provide a valid session ID (&session={ID})' });
|
|
679
|
+
|
|
680
|
+
try {
|
|
681
|
+
const result = req.module.chat('message', {
|
|
682
|
+
username,
|
|
683
|
+
message,
|
|
684
|
+
timestamp,
|
|
685
|
+
session,
|
|
686
|
+
token,
|
|
687
|
+
storage: chatStorage
|
|
688
|
+
});
|
|
689
|
+
res.jsonResponse(result);
|
|
690
|
+
} catch (err) {
|
|
691
|
+
res.jsonResponse({ error: err.message });
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
// Display a private chat with a token
|
|
696
|
+
app.post('/:version/chat/private', (req, res) => {
|
|
697
|
+
const { username, token } = req.body;
|
|
698
|
+
|
|
699
|
+
if (!username) return res.jsonResponse({ error: 'Please provide a username (?username={username})' });
|
|
700
|
+
if (!token) return res.jsonResponse({ error: 'Please provide a valid token (&token={key}).' });
|
|
701
|
+
|
|
702
|
+
try {
|
|
703
|
+
const messages = req.module.chat('private', {
|
|
704
|
+
username,
|
|
705
|
+
token,
|
|
706
|
+
storage: chatStorage
|
|
707
|
+
});
|
|
708
|
+
res.jsonResponse(messages);
|
|
709
|
+
} catch (err) {
|
|
710
|
+
res.jsonResponse({ error: err.message });
|
|
711
|
+
}
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
// Generate hash
|
|
715
|
+
app.post('/:version/hash', (req, res) => {
|
|
716
|
+
const { text, method } = req.body;
|
|
717
|
+
|
|
718
|
+
if (!text) return res.jsonResponse({ error: 'Please provide a text (?text={text})' });
|
|
719
|
+
if (!method) return res.jsonResponse({
|
|
720
|
+
error: 'Please provide a valid hash algorithm (&method={algorithm})',
|
|
721
|
+
documentation: `https://docs.sylvain.pro/${req.version}/en/hash`
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
try {
|
|
725
|
+
const result = req.module.hash(text, method);
|
|
726
|
+
res.jsonResponse(result);
|
|
727
|
+
} catch (err) {
|
|
728
|
+
res.jsonResponse({
|
|
729
|
+
error: err.message,
|
|
730
|
+
documentation: `https://docs.sylvain.pro/${req.version}/en/hash`
|
|
731
|
+
})
|
|
732
|
+
}
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
// Display a planning from an ICS file
|
|
736
|
+
app.post('/:version/hyperplanning', async (req, res) => {
|
|
737
|
+
const { url, detail } = req.body;
|
|
738
|
+
|
|
739
|
+
if (!url) return res.jsonResponse({ error: 'Please provide a valid ICS file URL (?url={URL})' });
|
|
740
|
+
|
|
741
|
+
try {
|
|
742
|
+
const hyperplanning = req.module.hyperplanning(url, detail);
|
|
743
|
+
res.jsonResponse(hyperplanning);
|
|
744
|
+
} catch (err){
|
|
745
|
+
res.jsonResponse({
|
|
746
|
+
error: 'Failed to parse ICS file.',
|
|
747
|
+
documentation: `https://docs.sylvain.pro/${req.version}/en/hyperplanning`
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
// Store tic tac toe games
|
|
753
|
+
app.post('/:version/tic-tac-toe', (req, res) => {
|
|
754
|
+
const { username, move, session, game } = req.body;
|
|
755
|
+
|
|
756
|
+
if (!username) return res.jsonResponse({ error: 'Please provide a username (?username={username})' });
|
|
757
|
+
if (!move) return res.jsonResponse({ error: 'Please provide a valid move (&move={move})' });
|
|
758
|
+
if (!session) return res.jsonResponse({ error: 'Please provide a valid session ID (&session={ID})' });
|
|
759
|
+
if (!game) return res.jsonResponse({ error: 'Please provide a valid game ID (&game={ID})' });
|
|
760
|
+
|
|
761
|
+
try {
|
|
762
|
+
const result = req.module.tic_tac_toe('play', {
|
|
763
|
+
username,
|
|
764
|
+
move,
|
|
765
|
+
session,
|
|
766
|
+
game,
|
|
767
|
+
storage: ticTacToeStorage
|
|
768
|
+
});
|
|
769
|
+
res.jsonResponse(result);
|
|
770
|
+
} catch (err) {
|
|
771
|
+
res.jsonResponse({ error: err.message });
|
|
772
|
+
}
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
// Display a tic tac toe game with a token
|
|
776
|
+
app.post('/:version/tic-tac-toe/fetch', (req, res) => {
|
|
777
|
+
const { username, game } = req.body;
|
|
778
|
+
|
|
779
|
+
if (!username) return res.jsonResponse({ error: 'Please provide a username (?username={username})' });
|
|
780
|
+
|
|
781
|
+
try {
|
|
782
|
+
const result = req.module.tic_tac_toe('fetch', {
|
|
783
|
+
username,
|
|
784
|
+
game,
|
|
785
|
+
storage: ticTacToeStorage
|
|
786
|
+
});
|
|
787
|
+
res.jsonResponse(result);
|
|
788
|
+
} catch (err) {
|
|
789
|
+
res.jsonResponse({ error: err.message });
|
|
790
|
+
}
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
// Generate Token
|
|
794
|
+
app.post('/:version/token', (req, res) => {
|
|
795
|
+
let { len, type } = req.body;
|
|
796
|
+
|
|
797
|
+
len = parseInt(len || 24, 10);
|
|
798
|
+
type = type ? type.toLowerCase() : 'alpha';
|
|
799
|
+
|
|
800
|
+
if (isNaN(len) || len < 0) return res.jsonResponse({ error: 'Invalid number.' });
|
|
801
|
+
if (len > 4096) return res.jsonResponse({ error: 'Length cannot exceed 4096.' });
|
|
802
|
+
if (len < 12) return res.jsonResponse({ error: 'Length cannot be less than 12.' });
|
|
803
|
+
|
|
804
|
+
try {
|
|
805
|
+
const token = req.module.token(len, type);
|
|
806
|
+
res.jsonResponse({ token });
|
|
807
|
+
} catch (err) {
|
|
808
|
+
return res.jsonResponse({
|
|
809
|
+
error: err.message,
|
|
810
|
+
documentation: `https://docs.sylvain.pro/${req.version}/en/token`
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
// ----------- ----------- SERVER SETUP ----------- ----------- //
|
|
816
|
+
|
|
817
|
+
app.listen(3000, () => console.log('API is running on\n - http://127.0.0.1:3000\n - http://localhost:3000'));
|