@20syldev/api 3.4.4 → 3.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/app.js +67 -39
- package/modules/v3/hyperplanning.js +7 -3
- package/modules/v3/tic_tac_toe.js +58 -5
- package/modules/v3/utils.js +11 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
<div align="center">
|
|
2
|
-
<a href="https://api.sylvain.
|
|
2
|
+
<a href="https://api.sylvain.sh"><img src="https://api.sylvain.sh/favicon.ico" alt="Logo" width="25%" height="auto"/></a>
|
|
3
3
|
|
|
4
4
|
# API Personnelle
|
|
5
|
-
[](https://github.com/20syldev/api/releases/latest)
|
|
6
6
|
</div>
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
## À propos de l'API
|
|
11
|
-
Voici mon API personnelle, disponible sur le domaine [api.sylvain.
|
|
12
|
-
L'API est développée avec Node.js et hébergée **24h/7j**. Elle est **simple d'utilisation** et a une **documentation** disponible sur [docs.sylvain.
|
|
13
|
-
> *Une limite de **2000** requêtes **par heure** est fixée. Elle change pour certains endpoints nécessitant plus de ressources. Si vous souhaitez une **augmentation** de cette limite, n'hésitez pas à visiter la [documentation](https://docs.sylvain.
|
|
11
|
+
Voici mon API personnelle, disponible sur le domaine [api.sylvain.sh](https://api.sylvain.sh).
|
|
12
|
+
L'API est développée avec Node.js et hébergée **24h/7j**. Elle est **simple d'utilisation** et a une **documentation** disponible sur [docs.sylvain.sh](https://docs.sylvain.sh) !
|
|
13
|
+
> *Une limite de **2000** requêtes **par heure** est fixée. Elle change pour certains endpoints nécessitant plus de ressources. Si vous souhaitez une **augmentation** de cette limite, n'hésitez pas à visiter la [documentation](https://docs.sylvain.sh/pricing).*
|
|
14
14
|
|
|
15
15
|
## Installer le paquet de l'API sur votre machine
|
|
16
16
|
```console
|
|
@@ -67,12 +67,12 @@ Ensuite, **déplacez-vous** dans le dossier du projet, via un terminal **Linux**
|
|
|
67
67
|
$ cd /chemin/vers/le/projet
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
Enfin, **exécutez** le script de build, qui installera les **dépendances** et lancera le **serveur** de l'[API](https://api.sylvain.
|
|
70
|
+
Enfin, **exécutez** le script de build, qui installera les **dépendances** et lancera le **serveur** de l'[API](https://api.sylvain.sh) :
|
|
71
71
|
```console
|
|
72
72
|
$ npm run build
|
|
73
73
|
```
|
|
74
74
|
```console
|
|
75
|
-
> @20syldev/api@3.4.
|
|
75
|
+
> @20syldev/api@3.4.6 build
|
|
76
76
|
> npm install && node app.js
|
|
77
77
|
|
|
78
78
|
[...]
|
|
@@ -107,4 +107,4 @@ import {
|
|
|
107
107
|
} from '@20syldev/api/v3';
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
*Visitez la [documentation](https://docs.sylvain.
|
|
110
|
+
*Visitez la [documentation](https://docs.sylvain.sh) dédiée, vous y retrouverez des exemples de requêtes et des codes simples pour tester l'[API](https://api.sylvain.sh) !*
|
package/app.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Import modules
|
|
2
2
|
import * as apiv3 from './modules/v3.js';
|
|
3
|
+
import { envList } from './modules/v3/utils.js';
|
|
3
4
|
|
|
4
5
|
// Import dependencies
|
|
5
6
|
import cors from 'cors';
|
|
@@ -51,7 +52,8 @@ const v2 = {
|
|
|
51
52
|
name: 'tic_tac_toe',
|
|
52
53
|
children: {
|
|
53
54
|
tic_tac_toe: '/tic-tac-toe',
|
|
54
|
-
fetch: '/tic-tac-toe/fetch'
|
|
55
|
+
fetch: '/tic-tac-toe/fetch',
|
|
56
|
+
list: '/tic-tac-toe/list'
|
|
55
57
|
}
|
|
56
58
|
},
|
|
57
59
|
{ name: 'token', path: '/token' }
|
|
@@ -101,6 +103,9 @@ const ticTacToeStorage = {
|
|
|
101
103
|
rateLimits: {}
|
|
102
104
|
};
|
|
103
105
|
|
|
106
|
+
// Define documentation URL
|
|
107
|
+
const documentation = 'https://docs.sylvain.sh';
|
|
108
|
+
|
|
104
109
|
// Define global variables
|
|
105
110
|
let contributions, lastFetch = 0, requests = 0, requestLimit, resetTime = Date.now() + 3600000;
|
|
106
111
|
|
|
@@ -212,7 +217,7 @@ app.use((err, req, res, next) => {
|
|
|
212
217
|
res.status(500).jsonResponse({
|
|
213
218
|
message: 'Internal Server Error',
|
|
214
219
|
error: err.message,
|
|
215
|
-
documentation
|
|
220
|
+
documentation,
|
|
216
221
|
status: '500'
|
|
217
222
|
});
|
|
218
223
|
});
|
|
@@ -236,7 +241,7 @@ app.use('/:version', (req, res, next) => {
|
|
|
236
241
|
return res.status(404).jsonResponse({
|
|
237
242
|
message: 'Not Found',
|
|
238
243
|
error: `Invalid API version (${version}).`,
|
|
239
|
-
documentation:
|
|
244
|
+
documentation: `${documentation}/${latest}`,
|
|
240
245
|
status: '404'
|
|
241
246
|
});
|
|
242
247
|
}
|
|
@@ -247,7 +252,7 @@ app.use('/:version', (req, res, next) => {
|
|
|
247
252
|
return res.status(404).jsonResponse({
|
|
248
253
|
message: 'Not Found',
|
|
249
254
|
error: `Module not found for version ${version}.`,
|
|
250
|
-
documentation:
|
|
255
|
+
documentation: `${documentation}/${latest}`,
|
|
251
256
|
status: '404'
|
|
252
257
|
});
|
|
253
258
|
}
|
|
@@ -266,7 +271,7 @@ app.use('/:version/:endpoint', (req, res, next) => {
|
|
|
266
271
|
return res.status(404).jsonResponse({
|
|
267
272
|
message: 'Not Found',
|
|
268
273
|
error: `Endpoint '${endpoint}' does not exist in ${req.version}.`,
|
|
269
|
-
documentation:
|
|
274
|
+
documentation: `${documentation}/${versions[version.length - 1]}`,
|
|
270
275
|
status: '404'
|
|
271
276
|
});
|
|
272
277
|
}
|
|
@@ -284,7 +289,7 @@ app.get('/', (req, res) => {
|
|
|
284
289
|
}, {});
|
|
285
290
|
|
|
286
291
|
res.jsonResponse({
|
|
287
|
-
documentation
|
|
292
|
+
documentation,
|
|
288
293
|
latest: `${base}/latest`,
|
|
289
294
|
logs: `${base}/logs`,
|
|
290
295
|
versions: links
|
|
@@ -299,7 +304,7 @@ app.get('/:version', (req, res) => {
|
|
|
299
304
|
return res.status(404).jsonResponse({
|
|
300
305
|
message: 'Not Found',
|
|
301
306
|
error: `Invalid API version (${version}).`,
|
|
302
|
-
documentation:
|
|
307
|
+
documentation: `${documentation}/${Object.keys(versions).pop()}`,
|
|
303
308
|
status: '404'
|
|
304
309
|
});
|
|
305
310
|
}
|
|
@@ -326,7 +331,7 @@ app.get('/:version', (req, res) => {
|
|
|
326
331
|
|
|
327
332
|
res.jsonResponse({
|
|
328
333
|
version,
|
|
329
|
-
documentation:
|
|
334
|
+
documentation: `${documentation}/${version}`,
|
|
330
335
|
endpoints
|
|
331
336
|
});
|
|
332
337
|
});
|
|
@@ -341,7 +346,7 @@ app.get('/:version/algorithms', (req, res) => {
|
|
|
341
346
|
if (!req.module.algorithms || !req.module.algorithms[method]) {
|
|
342
347
|
return res.jsonResponse({
|
|
343
348
|
error: 'Please provide a valid algorithm (?method={algorithm})',
|
|
344
|
-
documentation:
|
|
349
|
+
documentation: `${documentation}/${version}/en/algorithms`
|
|
345
350
|
});
|
|
346
351
|
}
|
|
347
352
|
|
|
@@ -351,7 +356,7 @@ app.get('/:version/algorithms', (req, res) => {
|
|
|
351
356
|
} catch (err) {
|
|
352
357
|
res.jsonResponse({
|
|
353
358
|
error: err.message,
|
|
354
|
-
documentation:
|
|
359
|
+
documentation: `${documentation}/${version}/en/algorithms`
|
|
355
360
|
});
|
|
356
361
|
}
|
|
357
362
|
});
|
|
@@ -368,7 +373,7 @@ app.get('/:version/captcha', (req, res) => {
|
|
|
368
373
|
} catch (err) {
|
|
369
374
|
res.jsonResponse({
|
|
370
375
|
error: err.message,
|
|
371
|
-
documentation:
|
|
376
|
+
documentation: `${documentation}/${req.version}/en/captcha`
|
|
372
377
|
});
|
|
373
378
|
}
|
|
374
379
|
});
|
|
@@ -399,7 +404,7 @@ app.get('/:version/color', (req, res) => {
|
|
|
399
404
|
} catch (err) {
|
|
400
405
|
res.jsonResponse({
|
|
401
406
|
error: err.message,
|
|
402
|
-
documentation:
|
|
407
|
+
documentation: `${documentation}/${req.version}/en/color`
|
|
403
408
|
});
|
|
404
409
|
}
|
|
405
410
|
});
|
|
@@ -418,7 +423,7 @@ app.get('/:version/convert', (req, res) => {
|
|
|
418
423
|
} catch (err) {
|
|
419
424
|
res.jsonResponse({
|
|
420
425
|
error: err.message,
|
|
421
|
-
documentation:
|
|
426
|
+
documentation: `${documentation}/${req.version}/en/convert`
|
|
422
427
|
});
|
|
423
428
|
}
|
|
424
429
|
});
|
|
@@ -431,7 +436,7 @@ app.get('/:version/domain', (req, res) => {
|
|
|
431
436
|
} catch (err) {
|
|
432
437
|
res.jsonResponse({
|
|
433
438
|
error: err.message,
|
|
434
|
-
documentation:
|
|
439
|
+
documentation: `${documentation}/${req.version}/en/domain`
|
|
435
440
|
});
|
|
436
441
|
}
|
|
437
442
|
});
|
|
@@ -457,7 +462,7 @@ app.get('/:version/infos', (req, res) => {
|
|
|
457
462
|
res.jsonResponse({
|
|
458
463
|
endpoints: new Set(paths).size,
|
|
459
464
|
last_version: Object.keys(versions).pop(),
|
|
460
|
-
documentation
|
|
465
|
+
documentation,
|
|
461
466
|
github: 'https://github.com/20syldev/api',
|
|
462
467
|
creation: 'November 25th 2024',
|
|
463
468
|
});
|
|
@@ -476,7 +481,7 @@ app.get('/:version/levenshtein', (req, res) => {
|
|
|
476
481
|
} catch (err) {
|
|
477
482
|
res.jsonResponse({
|
|
478
483
|
error: err.message,
|
|
479
|
-
documentation:
|
|
484
|
+
documentation: `${documentation}/${req.version}/en/levenshtein`
|
|
480
485
|
});
|
|
481
486
|
}
|
|
482
487
|
});
|
|
@@ -489,7 +494,7 @@ app.get('/:version/personal', (req, res) => {
|
|
|
489
494
|
} catch (err) {
|
|
490
495
|
res.jsonResponse({
|
|
491
496
|
error: err.message,
|
|
492
|
-
documentation:
|
|
497
|
+
documentation: `${documentation}/${req.version}/en/personal`
|
|
493
498
|
});
|
|
494
499
|
}
|
|
495
500
|
});
|
|
@@ -506,7 +511,7 @@ app.get('/:version/qrcode', async (req, res) => {
|
|
|
506
511
|
} catch (err) {
|
|
507
512
|
res.jsonResponse({
|
|
508
513
|
error: err.message,
|
|
509
|
-
documentation:
|
|
514
|
+
documentation: `${documentation}/${req.version}/en/qrcode`
|
|
510
515
|
});
|
|
511
516
|
}
|
|
512
517
|
});
|
|
@@ -521,6 +526,11 @@ app.get('/:version/tic-tac-toe/fetch', (req, res) => {
|
|
|
521
526
|
res.jsonResponse({ error: 'This endpoint only supports POST requests.' });
|
|
522
527
|
});
|
|
523
528
|
|
|
529
|
+
// GET tic-tac-toe list error
|
|
530
|
+
app.get('/:version/tic-tac-toe/list', (req, res) => {
|
|
531
|
+
res.jsonResponse({ error: 'This endpoint only supports POST requests.' });
|
|
532
|
+
});
|
|
533
|
+
|
|
524
534
|
// Display or generate time informations
|
|
525
535
|
app.get('/:version/time', (req, res) => {
|
|
526
536
|
const { type = 'live', start, end, format, timezone } = req.query;
|
|
@@ -540,7 +550,7 @@ app.get('/:version/time', (req, res) => {
|
|
|
540
550
|
} catch (err) {
|
|
541
551
|
res.jsonResponse({
|
|
542
552
|
error: err.message,
|
|
543
|
-
documentation:
|
|
553
|
+
documentation: `${documentation}/${req.version}/en/time`
|
|
544
554
|
});
|
|
545
555
|
}
|
|
546
556
|
});
|
|
@@ -558,7 +568,7 @@ app.get('/:version/username', (req, res) => {
|
|
|
558
568
|
} catch (err) {
|
|
559
569
|
res.jsonResponse({
|
|
560
570
|
error: err.message,
|
|
561
|
-
documentation:
|
|
571
|
+
documentation: `${documentation}/${req.version}/en/username`
|
|
562
572
|
});
|
|
563
573
|
}
|
|
564
574
|
});
|
|
@@ -632,31 +642,35 @@ app.get('/:version/website', async (req, res) => {
|
|
|
632
642
|
flowers: process.env.FLOWERS,
|
|
633
643
|
g_2048: process.env.G_2048,
|
|
634
644
|
gemsync: process.env.GEMSYNC,
|
|
645
|
+
gft: process.env.GFT,
|
|
635
646
|
gitsite: process.env.GITSITE,
|
|
636
647
|
lebonchar: process.env.LEBONCHAR,
|
|
637
648
|
logs: process.env.LOGS,
|
|
638
649
|
logvault: process.env.LOGVAULT,
|
|
639
650
|
lyah: process.env.LYAH,
|
|
640
651
|
minify: process.env.MINIFY,
|
|
652
|
+
mn: process.env.MN,
|
|
641
653
|
monitoring: process.env.MONITORING,
|
|
642
654
|
morpion: process.env.MORPION,
|
|
643
655
|
nitrogen: process.env.NITROGEN,
|
|
644
656
|
old_database: process.env.OLD_DATABASE,
|
|
645
657
|
password: process.env.PASSWORD,
|
|
646
658
|
php: process.env.PHP,
|
|
659
|
+
planning: process.env.PLANNING,
|
|
647
660
|
ping: process.env.PING,
|
|
648
661
|
portfolio: process.env.PORTFOLIO,
|
|
649
662
|
python_api: process.env.PYTHON_API,
|
|
650
663
|
readme: process.env.README,
|
|
651
664
|
timestamp: process.env.TIMESTAMP,
|
|
652
665
|
terminal: process.env.TERMINAL,
|
|
666
|
+
valentine: process.env.VALENTINE,
|
|
653
667
|
wrkit: process.env.WRKIT,
|
|
654
668
|
zpki: process.env.ZPKI
|
|
655
669
|
},
|
|
656
|
-
patched_projects:
|
|
657
|
-
updated_projects:
|
|
658
|
-
new_projects:
|
|
659
|
-
sub_domains:
|
|
670
|
+
patched_projects: envList('PATCH'),
|
|
671
|
+
updated_projects: envList('RECENT'),
|
|
672
|
+
new_projects: envList('NEW'),
|
|
673
|
+
sub_domains: envList('DOMAINS'),
|
|
660
674
|
stats: {
|
|
661
675
|
1: process.env.STATS1,
|
|
662
676
|
2: process.env.STATS2,
|
|
@@ -667,8 +681,8 @@ app.get('/:version/website', async (req, res) => {
|
|
|
667
681
|
this_month: contributions.month.toString(),
|
|
668
682
|
last_year: contributions.year.toString(),
|
|
669
683
|
},
|
|
670
|
-
|
|
671
|
-
active: process.env.ACTIVE
|
|
684
|
+
tag: process.env.TAG,
|
|
685
|
+
active: process.env.ACTIVE === 'true'
|
|
672
686
|
});
|
|
673
687
|
});
|
|
674
688
|
|
|
@@ -676,7 +690,7 @@ app.get('/:version/website', async (req, res) => {
|
|
|
676
690
|
|
|
677
691
|
// Store chat messages
|
|
678
692
|
app.post('/:version/chat', (req, res) => {
|
|
679
|
-
const { username, message, timestamp, session, token } = req.body;
|
|
693
|
+
const { username, message, timestamp, session, token } = req.body || {};
|
|
680
694
|
|
|
681
695
|
if (!username) return res.jsonResponse({ error: 'Please provide a username (?username={username})' });
|
|
682
696
|
if (!message) return res.jsonResponse({ error: 'Please provide a message (&message={message})' });
|
|
@@ -699,7 +713,7 @@ app.post('/:version/chat', (req, res) => {
|
|
|
699
713
|
|
|
700
714
|
// Display a private chat with a token
|
|
701
715
|
app.post('/:version/chat/private', (req, res) => {
|
|
702
|
-
const { username, token } = req.body;
|
|
716
|
+
const { username, token } = req.body || {};
|
|
703
717
|
|
|
704
718
|
if (!username) return res.jsonResponse({ error: 'Please provide a username (?username={username})' });
|
|
705
719
|
if (!token) return res.jsonResponse({ error: 'Please provide a valid token (&token={key}).' });
|
|
@@ -718,12 +732,12 @@ app.post('/:version/chat/private', (req, res) => {
|
|
|
718
732
|
|
|
719
733
|
// Generate hash
|
|
720
734
|
app.post('/:version/hash', (req, res) => {
|
|
721
|
-
const { text, method } = req.body;
|
|
735
|
+
const { text, method } = req.body || {};
|
|
722
736
|
|
|
723
737
|
if (!text) return res.jsonResponse({ error: 'Please provide a text (?text={text})' });
|
|
724
738
|
if (!method) return res.jsonResponse({
|
|
725
739
|
error: 'Please provide a valid hash algorithm (&method={algorithm})',
|
|
726
|
-
documentation:
|
|
740
|
+
documentation: `${documentation}/${req.version}/en/hash`
|
|
727
741
|
});
|
|
728
742
|
|
|
729
743
|
try {
|
|
@@ -732,31 +746,31 @@ app.post('/:version/hash', (req, res) => {
|
|
|
732
746
|
} catch (err) {
|
|
733
747
|
res.jsonResponse({
|
|
734
748
|
error: err.message,
|
|
735
|
-
documentation:
|
|
749
|
+
documentation: `${documentation}/${req.version}/en/hash`
|
|
736
750
|
})
|
|
737
751
|
}
|
|
738
752
|
});
|
|
739
753
|
|
|
740
754
|
// Display a planning from an ICS file
|
|
741
755
|
app.post('/:version/hyperplanning', async (req, res) => {
|
|
742
|
-
const { url, detail } = req.body;
|
|
756
|
+
const { url, detail } = req.body || {};
|
|
743
757
|
|
|
744
758
|
if (!url) return res.jsonResponse({ error: 'Please provide a valid ICS file URL (?url={URL})' });
|
|
745
759
|
|
|
746
760
|
try {
|
|
747
|
-
const hyperplanning = req.module.hyperplanning(url, detail);
|
|
761
|
+
const hyperplanning = await req.module.hyperplanning(url, detail);
|
|
748
762
|
res.jsonResponse(hyperplanning);
|
|
749
763
|
} catch (err){
|
|
750
764
|
res.jsonResponse({
|
|
751
|
-
error:
|
|
752
|
-
documentation:
|
|
765
|
+
error: err.message,
|
|
766
|
+
documentation: `${documentation}/${req.version}/en/hyperplanning`
|
|
753
767
|
});
|
|
754
768
|
}
|
|
755
769
|
});
|
|
756
770
|
|
|
757
771
|
// Store tic tac toe games
|
|
758
772
|
app.post('/:version/tic-tac-toe', (req, res) => {
|
|
759
|
-
const { username, move, session, game } = req.body;
|
|
773
|
+
const { username, move, session, game } = req.body || {};
|
|
760
774
|
|
|
761
775
|
if (!username) return res.jsonResponse({ error: 'Please provide a username (?username={username})' });
|
|
762
776
|
if (!move) return res.jsonResponse({ error: 'Please provide a valid move (&move={move})' });
|
|
@@ -779,7 +793,8 @@ app.post('/:version/tic-tac-toe', (req, res) => {
|
|
|
779
793
|
|
|
780
794
|
// Display a tic tac toe game with a token
|
|
781
795
|
app.post('/:version/tic-tac-toe/fetch', (req, res) => {
|
|
782
|
-
const { username, game } = req.body;
|
|
796
|
+
const { username, game } = req.body || {};
|
|
797
|
+
const privateGame = (req.body || {}).private;
|
|
783
798
|
|
|
784
799
|
if (!username) return res.jsonResponse({ error: 'Please provide a username (?username={username})' });
|
|
785
800
|
|
|
@@ -787,6 +802,19 @@ app.post('/:version/tic-tac-toe/fetch', (req, res) => {
|
|
|
787
802
|
const result = req.module.tic_tac_toe('fetch', {
|
|
788
803
|
username,
|
|
789
804
|
game,
|
|
805
|
+
private: privateGame,
|
|
806
|
+
storage: ticTacToeStorage
|
|
807
|
+
});
|
|
808
|
+
res.jsonResponse(result);
|
|
809
|
+
} catch (err) {
|
|
810
|
+
res.jsonResponse({ error: err.message });
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
// List public tic tac toe games
|
|
815
|
+
app.post('/:version/tic-tac-toe/list', (req, res) => {
|
|
816
|
+
try {
|
|
817
|
+
const result = req.module.tic_tac_toe('list', {
|
|
790
818
|
storage: ticTacToeStorage
|
|
791
819
|
});
|
|
792
820
|
res.jsonResponse(result);
|
|
@@ -797,7 +825,7 @@ app.post('/:version/tic-tac-toe/fetch', (req, res) => {
|
|
|
797
825
|
|
|
798
826
|
// Generate Token
|
|
799
827
|
app.post('/:version/token', (req, res) => {
|
|
800
|
-
let { len, type } = req.body;
|
|
828
|
+
let { len, type } = req.body || {};
|
|
801
829
|
|
|
802
830
|
len = parseInt(len || 24, 10);
|
|
803
831
|
type = type ? type.toLowerCase() : 'alpha';
|
|
@@ -812,7 +840,7 @@ app.post('/:version/token', (req, res) => {
|
|
|
812
840
|
} catch (err) {
|
|
813
841
|
return res.jsonResponse({
|
|
814
842
|
error: err.message,
|
|
815
|
-
documentation:
|
|
843
|
+
documentation: `${documentation}/${req.version}/en/token`
|
|
816
844
|
});
|
|
817
845
|
}
|
|
818
846
|
});
|
|
@@ -21,12 +21,12 @@ export default async function hyperplanning(url, detail) {
|
|
|
21
21
|
.getAllSubcomponents('vevent')
|
|
22
22
|
.map(e => {
|
|
23
23
|
const evt = new ical.Event(e);
|
|
24
|
-
const summary = evt.summary.split(' ').filter(part => part !== '-');
|
|
24
|
+
const summary = (evt.summary || '').split(' ').filter(part => part !== '-');
|
|
25
25
|
const start = formatDate(evt.startDate.toJSDate());
|
|
26
26
|
const end = formatDate(evt.endDate.toJSDate());
|
|
27
27
|
|
|
28
28
|
if (detail === 'full') {
|
|
29
|
-
const desc = evt.description.split('\n').map(l => l.trim());
|
|
29
|
+
const desc = (evt.description || '').split('\n').map(l => l.trim());
|
|
30
30
|
const extract = (p) => (desc.find(l => l.startsWith(p)) || '').replace(p, '').trim();
|
|
31
31
|
|
|
32
32
|
return {
|
|
@@ -42,7 +42,11 @@ export default async function hyperplanning(url, detail) {
|
|
|
42
42
|
|
|
43
43
|
if (detail === 'list') return { summary, start, end };
|
|
44
44
|
|
|
45
|
-
return {
|
|
45
|
+
return {
|
|
46
|
+
summary: evt.summary || '',
|
|
47
|
+
start,
|
|
48
|
+
end
|
|
49
|
+
};
|
|
46
50
|
})
|
|
47
51
|
.sort((a, b) => new Date(a.start) - new Date(b.start))
|
|
48
52
|
.filter(e => new Date(e.end) >= new Date());
|
|
@@ -3,7 +3,7 @@ import { randomBytes } from 'crypto';
|
|
|
3
3
|
/**
|
|
4
4
|
* Manages a Tic-Tac-Toe game session
|
|
5
5
|
*
|
|
6
|
-
* @param {string} action - The action to perform ('play' or '
|
|
6
|
+
* @param {string} action - The action to perform ('play', 'fetch', or 'list')
|
|
7
7
|
* @param {Object} params - The parameters for the action
|
|
8
8
|
* @returns {Object} - The result of the action
|
|
9
9
|
* @throws {Error} - If parameters are invalid
|
|
@@ -20,6 +20,9 @@ export default function tic_tac_toe(action, params = {}) {
|
|
|
20
20
|
// Reference the storage properties
|
|
21
21
|
const { games, sessions, rateLimits } = storage;
|
|
22
22
|
|
|
23
|
+
// List public games
|
|
24
|
+
if (action === 'list') return listGames(games);
|
|
25
|
+
|
|
23
26
|
// Input validation for common parameters
|
|
24
27
|
if (!params.username) throw new Error('Please provide a username');
|
|
25
28
|
|
|
@@ -40,7 +43,7 @@ export default function tic_tac_toe(action, params = {}) {
|
|
|
40
43
|
} else if (action === 'fetch') {
|
|
41
44
|
return fetchGame(params, games, u);
|
|
42
45
|
} else {
|
|
43
|
-
throw new Error('Invalid action. Use "play" or "
|
|
46
|
+
throw new Error('Invalid action. Use "play", "fetch", or "list"');
|
|
44
47
|
}
|
|
45
48
|
}
|
|
46
49
|
|
|
@@ -63,7 +66,12 @@ function playMove(params, games, sessions, u, now) {
|
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
// Initialize game if needed
|
|
66
|
-
games[game] = games[game] || {
|
|
69
|
+
games[game] = games[game] || {
|
|
70
|
+
moves: [],
|
|
71
|
+
players: [],
|
|
72
|
+
private: params.private || false,
|
|
73
|
+
creation: Date.now()
|
|
74
|
+
};
|
|
67
75
|
const moves = games[game].moves;
|
|
68
76
|
|
|
69
77
|
// Check if game is full
|
|
@@ -112,11 +120,12 @@ function playMove(params, games, sessions, u, now) {
|
|
|
112
120
|
*/
|
|
113
121
|
function fetchGame(params, games, u) {
|
|
114
122
|
const id = params.game || generateGameId();
|
|
115
|
-
|
|
116
123
|
if (!games[id]) {
|
|
117
124
|
games[id] = {
|
|
118
125
|
moves: [],
|
|
119
|
-
players: []
|
|
126
|
+
players: [],
|
|
127
|
+
private: params.private || false,
|
|
128
|
+
creation: Date.now()
|
|
120
129
|
};
|
|
121
130
|
}
|
|
122
131
|
if (!games[id].players.includes(u)) {
|
|
@@ -125,6 +134,7 @@ function fetchGame(params, games, u) {
|
|
|
125
134
|
|
|
126
135
|
const moves = games[id].moves;
|
|
127
136
|
const players = games[id].players;
|
|
137
|
+
const privateGame = games[id].private;
|
|
128
138
|
const lastPlayer = moves.length ? moves[moves.length - 1].username : null;
|
|
129
139
|
const turn = players.find(p => p !== lastPlayer) || players[0];
|
|
130
140
|
const status = players.length >= 2 ? 'ready' : 'waiting';
|
|
@@ -136,10 +146,53 @@ function fetchGame(params, games, u) {
|
|
|
136
146
|
players,
|
|
137
147
|
turn,
|
|
138
148
|
status,
|
|
149
|
+
private: privateGame,
|
|
139
150
|
...result
|
|
140
151
|
};
|
|
141
152
|
}
|
|
142
153
|
|
|
154
|
+
/**
|
|
155
|
+
* List all public games in progress
|
|
156
|
+
*/
|
|
157
|
+
function listGames(games) {
|
|
158
|
+
const publicGames = [];
|
|
159
|
+
const now = Date.now();
|
|
160
|
+
|
|
161
|
+
for (const [gameId, game] of Object.entries(games)) {
|
|
162
|
+
// Only include public games
|
|
163
|
+
if (!game.private) {
|
|
164
|
+
const result = game.moves.length ? checkGame(game.moves) : {};
|
|
165
|
+
const isFinished = result.winner || result.tie;
|
|
166
|
+
|
|
167
|
+
// Only include active games (not finished)
|
|
168
|
+
if (!isFinished) {
|
|
169
|
+
const lastPlayer = game.moves.length ? game.moves[game.moves.length - 1].username : null;
|
|
170
|
+
const turn = game.players.find(p => p !== lastPlayer) || game.players[0];
|
|
171
|
+
const status = game.players.length >= 2 ? 'ready' : 'waiting';
|
|
172
|
+
|
|
173
|
+
publicGames.push({
|
|
174
|
+
id: gameId,
|
|
175
|
+
players: game.players,
|
|
176
|
+
playersCount: game.players.length,
|
|
177
|
+
moves: game.moves.length,
|
|
178
|
+
turn,
|
|
179
|
+
status,
|
|
180
|
+
creation: game.creation || now
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Sort by creation time (newest first)
|
|
187
|
+
publicGames.sort((a, b) => b.creation - a.creation);
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
message: 'Public games available',
|
|
191
|
+
count: publicGames.length,
|
|
192
|
+
games: publicGames
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
143
196
|
/**
|
|
144
197
|
* Generate a random game ID
|
|
145
198
|
*/
|
package/modules/v3/utils.js
CHANGED
|
@@ -42,6 +42,17 @@ export function formatDate(date) {
|
|
|
42
42
|
return new Date(date.getTime() - date.getTimezoneOffset() * 60000).toISOString().replace('Z', '');
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Parse a space-separated env variable into an array, or return null.
|
|
47
|
+
*
|
|
48
|
+
* @param {string} key - The environment variable name.
|
|
49
|
+
* @returns {string[]|null} - The parsed array or null.
|
|
50
|
+
*/
|
|
51
|
+
export function envList(key) {
|
|
52
|
+
const v = process.env[key];
|
|
53
|
+
return v && v !== 'undefined' ? v.split(' ') : null;
|
|
54
|
+
}
|
|
55
|
+
|
|
45
56
|
/**
|
|
46
57
|
* Manage rate limiting for users
|
|
47
58
|
*
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "3.4.
|
|
2
|
+
"version": "3.4.6",
|
|
3
3
|
"name": "@20syldev/api",
|
|
4
|
-
"description": "Node.js API with multiple features. Check the documentation at https://docs.sylvain.
|
|
4
|
+
"description": "Node.js API with multiple features. Check the documentation at https://docs.sylvain.sh",
|
|
5
5
|
"main": "app.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
@@ -49,5 +49,5 @@
|
|
|
49
49
|
"bugs": {
|
|
50
50
|
"url": "https://github.com/20syldev/api/issues"
|
|
51
51
|
},
|
|
52
|
-
"homepage": "https://api.sylvain.
|
|
52
|
+
"homepage": "https://api.sylvain.sh"
|
|
53
53
|
}
|