@20syldev/api 3.3.9 → 3.4.1
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 +55 -5
- package/app.js +327 -683
- 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 +6 -2
package/app.js
CHANGED
|
@@ -1,85 +1,109 @@
|
|
|
1
|
+
// Import modules
|
|
2
|
+
import * as apiv3 from './modules/v3.js';
|
|
3
|
+
|
|
4
|
+
// Import dependencies
|
|
1
5
|
import cors from 'cors';
|
|
2
6
|
import dotenv from 'dotenv';
|
|
3
7
|
import express from 'express';
|
|
4
8
|
import fetch from 'node-fetch';
|
|
5
|
-
import ical from 'ical.js';
|
|
6
|
-
import { createCanvas } from 'canvas';
|
|
7
|
-
import { randomBytes, getHashes, createHash } from 'crypto';
|
|
8
9
|
import { urlencoded, json } from 'express';
|
|
9
|
-
import { factorial } from 'mathjs';
|
|
10
10
|
import { dirname, join } from 'path';
|
|
11
|
-
import { toDataURL } from 'qrcode';
|
|
12
11
|
import { fileURLToPath } from 'url';
|
|
13
|
-
import { v4 } from 'uuid';
|
|
14
12
|
|
|
15
13
|
const __filename = fileURLToPath(import.meta.url);
|
|
16
14
|
const __dirname = dirname(__filename);
|
|
17
15
|
const app = express();
|
|
18
16
|
|
|
19
17
|
// Define allowed versions & endpoints for each version
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
+
}
|
|
25
84
|
};
|
|
26
85
|
|
|
27
|
-
//
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
86
|
+
// API storage
|
|
87
|
+
const logs = [], ipLimits = {};
|
|
88
|
+
|
|
89
|
+
// Chat storage
|
|
90
|
+
const chatStorage = {
|
|
91
|
+
messages: [],
|
|
92
|
+
privateChats: {},
|
|
93
|
+
sessions: {},
|
|
94
|
+
rateLimits: {}
|
|
33
95
|
};
|
|
34
|
-
const genIP = () => `${Math.floor(Math.random() * 256)}.${Math.floor(Math.random() * 256)}.${Math.floor(Math.random() * 256)}.${Math.floor(Math.random() * 256)}`;
|
|
35
|
-
const genToken = (chars, length) => Array.from({ length }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
|
|
36
|
-
const random = (arr) => arr[Math.floor(Math.random() * arr.length)];
|
|
37
96
|
|
|
38
|
-
//
|
|
39
|
-
const
|
|
97
|
+
// Tic-Tac-Toe storage
|
|
98
|
+
const ticTacToeStorage = {
|
|
99
|
+
games: {},
|
|
100
|
+
sessions: {},
|
|
101
|
+
rateLimits: {}
|
|
102
|
+
};
|
|
40
103
|
|
|
41
104
|
// Define global variables
|
|
42
105
|
let contributions, lastFetch = 0, requests = 0, requestLimit, resetTime = Date.now() + 3600000;
|
|
43
106
|
|
|
44
|
-
// ----------- ----------- MAIN FUNCTIONS ----------- ----------- //
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Check the game result of a Tic-Tac-Toe game.
|
|
48
|
-
*
|
|
49
|
-
* @param {Array} moves - The moves of the game.
|
|
50
|
-
* @returns {Object} - The result of the game.
|
|
51
|
-
*/
|
|
52
|
-
function checkGame(moves) {
|
|
53
|
-
let board = Array(3).fill().map(() => Array(3).fill(null));
|
|
54
|
-
let playerSymbols = {};
|
|
55
|
-
let playersOrder = [];
|
|
56
|
-
|
|
57
|
-
moves.forEach(({ username, move }) => {
|
|
58
|
-
if (!playerSymbols[username]) {
|
|
59
|
-
playersOrder.push(username);
|
|
60
|
-
playerSymbols[username] = playersOrder.length === 1 ? 'X' : 'O';
|
|
61
|
-
}
|
|
62
|
-
let [row, col] = move.split('-').map(Number);
|
|
63
|
-
board[row - 1][col - 1] = playerSymbols[username];
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
const checkWinner = (symbol) => {
|
|
67
|
-
for (let i = 0; i < 3; i++) {
|
|
68
|
-
if (board[i][0] === symbol && board[i][1] === symbol && board[i][2] === symbol) return true;
|
|
69
|
-
if (board[0][i] === symbol && board[1][i] === symbol && board[2][i] === symbol) return true;
|
|
70
|
-
}
|
|
71
|
-
if (board[0][0] === symbol && board[1][1] === symbol && board[2][2] === symbol) return true;
|
|
72
|
-
if (board[0][2] === symbol && board[1][1] === symbol && board[2][0] === symbol) return true;
|
|
73
|
-
return false;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
let winner = Object.keys(playerSymbols).find(player => checkWinner(playerSymbols[player]));
|
|
77
|
-
let isTie = !winner && moves.length === 9;
|
|
78
|
-
let loser = winner && playersOrder.length === 2 ? playersOrder.find(player => player !== winner) : null;
|
|
79
|
-
|
|
80
|
-
return { winner, loser, tie: isTie };
|
|
81
|
-
}
|
|
82
|
-
|
|
83
107
|
// ----------- ----------- MIDDLEWARES SETUP ----------- ----------- //
|
|
84
108
|
|
|
85
109
|
dotenv.config();
|
|
@@ -196,21 +220,38 @@ app.use((err, req, res, next) => {
|
|
|
196
220
|
// Check if version exists
|
|
197
221
|
app.use('/:version', (req, res, next) => {
|
|
198
222
|
const { version } = req.params;
|
|
199
|
-
const latest = versions
|
|
223
|
+
const latest = Object.keys(versions).pop();
|
|
200
224
|
const endpoint = req.originalUrl.split('/').slice(2).join('/');
|
|
201
225
|
|
|
226
|
+
req.version = version;
|
|
227
|
+
req.latest = latest;
|
|
228
|
+
|
|
202
229
|
if (['latest', 'fr', 'en'].includes(version)) {
|
|
203
230
|
return res.redirect(endpoint ? `/${latest}/${endpoint}` : `/${latest}`);
|
|
204
231
|
}
|
|
205
232
|
|
|
206
|
-
if (
|
|
233
|
+
if (version === 'logs') return res.jsonResponse(logs);
|
|
234
|
+
|
|
235
|
+
if (!versions[version] && version !== 'logs') {
|
|
207
236
|
return res.status(404).jsonResponse({
|
|
208
237
|
message: 'Not Found',
|
|
209
238
|
error: `Invalid API version (${version}).`,
|
|
210
|
-
documentation: `https://docs.sylvain.pro/${
|
|
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}`,
|
|
211
251
|
status: '404'
|
|
212
252
|
});
|
|
213
253
|
}
|
|
254
|
+
|
|
214
255
|
next();
|
|
215
256
|
});
|
|
216
257
|
|
|
@@ -218,11 +259,14 @@ app.use('/:version', (req, res, next) => {
|
|
|
218
259
|
app.use('/:version/:endpoint', (req, res, next) => {
|
|
219
260
|
const { version, endpoint } = req.params;
|
|
220
261
|
|
|
221
|
-
|
|
262
|
+
req.version = version;
|
|
263
|
+
req.endpoint = endpoint;
|
|
264
|
+
|
|
265
|
+
if (!req.endpoint) {
|
|
222
266
|
return res.status(404).jsonResponse({
|
|
223
267
|
message: 'Not Found',
|
|
224
|
-
error: `Endpoint '${endpoint}' does not exist in ${version}.`,
|
|
225
|
-
documentation: `https://docs.sylvain.pro/${versions.
|
|
268
|
+
error: `Endpoint '${endpoint}' does not exist in ${req.version}.`,
|
|
269
|
+
documentation: `https://docs.sylvain.pro/${versions[version.length - 1]}`,
|
|
226
270
|
status: '404'
|
|
227
271
|
});
|
|
228
272
|
}
|
|
@@ -233,117 +277,60 @@ app.use('/:version/:endpoint', (req, res, next) => {
|
|
|
233
277
|
|
|
234
278
|
// Main route
|
|
235
279
|
app.get('/', (req, res) => {
|
|
236
|
-
|
|
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
|
+
|
|
237
286
|
res.jsonResponse({
|
|
238
287
|
documentation: 'https://docs.sylvain.pro',
|
|
239
|
-
latest:
|
|
240
|
-
logs:
|
|
241
|
-
versions:
|
|
242
|
-
v1: 'https://api.sylvain.pro/v1',
|
|
243
|
-
v2: 'https://api.sylvain.pro/v2',
|
|
244
|
-
v3: 'https://api.sylvain.pro/v3'
|
|
245
|
-
}
|
|
288
|
+
latest: `${base}/latest`,
|
|
289
|
+
logs: `${base}/logs`,
|
|
290
|
+
versions: links
|
|
246
291
|
});
|
|
247
292
|
});
|
|
248
293
|
|
|
249
|
-
// Display
|
|
250
|
-
app.get('
|
|
251
|
-
|
|
252
|
-
version: 'v1',
|
|
253
|
-
documentation: 'https://docs.sylvain.pro/v1',
|
|
254
|
-
endpoints: {
|
|
255
|
-
get: {
|
|
256
|
-
algorithm: '/v1/algorithms?method={algorithm}&value={value}(&value2={value2})',
|
|
257
|
-
captcha: '/v1/captcha?text={text}',
|
|
258
|
-
color: '/v1/color',
|
|
259
|
-
convert: '/v1/convert?value={value}&from={unit}&to={unit}',
|
|
260
|
-
domain: '/v1/domain',
|
|
261
|
-
infos: '/v1/infos',
|
|
262
|
-
personal: '/v1/personal',
|
|
263
|
-
qrcode: '/v1/qrcode?url={URL}',
|
|
264
|
-
username: '/v1/username'
|
|
265
|
-
},
|
|
266
|
-
post: {
|
|
267
|
-
token: '/v1/token'
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
|
-
});
|
|
294
|
+
// Display version information
|
|
295
|
+
app.get('/:version', (req, res) => {
|
|
296
|
+
const { version } = req.params;
|
|
272
297
|
|
|
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
|
-
token: '/v2/token'
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
});
|
|
305
|
-
});
|
|
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/${Object.keys(versions).pop()}`,
|
|
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
|
+
}, {});
|
|
306
326
|
|
|
307
|
-
// Display v3 endpoints
|
|
308
|
-
app.get('/v3', (req, res) => {
|
|
309
327
|
res.jsonResponse({
|
|
310
|
-
version
|
|
311
|
-
documentation:
|
|
312
|
-
endpoints
|
|
313
|
-
get: {
|
|
314
|
-
algorithm: '/v3/algorithms?method={algorithm}&value={value}(&value2={value2})',
|
|
315
|
-
captcha: '/v3/captcha?text={text}',
|
|
316
|
-
chat: '/v3/chat',
|
|
317
|
-
color: '/v3/color',
|
|
318
|
-
convert: '/v3/convert?value={value}&from={unit}&to={unit}',
|
|
319
|
-
domain: '/v3/domain',
|
|
320
|
-
infos: '/v3/infos',
|
|
321
|
-
levenshtein: '/v3/levenshtein?str1={string}&str2={string}',
|
|
322
|
-
personal: '/v3/personal',
|
|
323
|
-
qrcode: '/v3/qrcode?url={URL}',
|
|
324
|
-
time: '/v3/time(?type={type}&start={timestamp}&end={timestamp}&format={format}&timezone={timezone})',
|
|
325
|
-
username: '/v3/username'
|
|
326
|
-
},
|
|
327
|
-
post: {
|
|
328
|
-
chat: {
|
|
329
|
-
chat: '/v3/chat',
|
|
330
|
-
private: '/v3/chat/private'
|
|
331
|
-
},
|
|
332
|
-
hash: '/v3/hash',
|
|
333
|
-
hyperplanning: '/v3/hyperplanning',
|
|
334
|
-
tic_tac_toe: {
|
|
335
|
-
tic_tac_toe: '/v3/tic-tac-toe',
|
|
336
|
-
fetch: '/v3/tic-tac-toe/fetch'
|
|
337
|
-
},
|
|
338
|
-
token: '/v3/token'
|
|
339
|
-
}
|
|
340
|
-
}
|
|
328
|
+
version,
|
|
329
|
+
documentation: `https://docs.sylvain.pro/${version}`,
|
|
330
|
+
endpoints
|
|
341
331
|
});
|
|
342
332
|
});
|
|
343
333
|
|
|
344
|
-
// Display logs
|
|
345
|
-
app.get('/logs', (req, res) => res.jsonResponse(logs));
|
|
346
|
-
|
|
347
334
|
// ----------- ----------- GET ENDPOINTS ----------- ----------- //
|
|
348
335
|
|
|
349
336
|
// Algorithms
|
|
@@ -351,144 +338,52 @@ app.get('/:version/algorithms', (req, res) => {
|
|
|
351
338
|
const { method, value, value2 } = req.query;
|
|
352
339
|
const { version } = req.params;
|
|
353
340
|
|
|
354
|
-
if (!
|
|
341
|
+
if (!req.module.algorithms || !req.module.algorithms[method]) {
|
|
355
342
|
return res.jsonResponse({
|
|
356
343
|
error: 'Please provide a valid algorithm (?method={algorithm})',
|
|
357
344
|
documentation: `https://docs.sylvain.pro/${version}/en/algorithms`
|
|
358
345
|
});
|
|
359
346
|
}
|
|
360
|
-
if (!value) return res.jsonResponse({ error: 'Please provide a valid value (&value={value})' });
|
|
361
|
-
|
|
362
|
-
if (method === 'anagram') {
|
|
363
|
-
if (!value2) return res.jsonResponse({ error: 'Please provide a second value (&value2={value})' });
|
|
364
|
-
return res.jsonResponse({ answer: value.split('').sort().join('') === value2.split('').sort().join('') });
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
if (method === 'bubblesort') {
|
|
368
|
-
const arr = value.split(',').map(Number);
|
|
369
|
-
const n = arr.length;
|
|
370
|
-
for (let i = 0; i < n-1; i++) {
|
|
371
|
-
for (let j = 0; j < n-i-1; j++) {
|
|
372
|
-
if (arr[j] > arr[j + 1]) [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
return res.jsonResponse({ answer: arr });
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
if (method === 'factorial') {
|
|
379
|
-
if (isNaN(value) || value < 0 || value > 170) return res.jsonResponse({ error: 'Please provide a valid number between 0 and 170.' });
|
|
380
|
-
return res.jsonResponse({ answer: factorial(value) });
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
if (method === 'fibonacci') {
|
|
384
|
-
let fib = [0, 1];
|
|
385
|
-
if (isNaN(value) || value < 0 || value > 1000) return res.jsonResponse({ error: 'Please provide a valid number between 0 and 1000.' });
|
|
386
|
-
for (let i = 2; i < parseInt(value); i++) fib.push(fib[i - 1] + fib[i - 2]);
|
|
387
|
-
return res.jsonResponse({ answer: fib.slice(0, parseInt(value)) });
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
if (method === 'gcd') {
|
|
391
|
-
if (!value2) return res.jsonResponse({ error: 'Please provide a second value (&value2={value})' });
|
|
392
|
-
if (isNaN(value) || isNaN(value2)) return res.jsonResponse({ error: 'Invalid numbers.' });
|
|
393
|
-
return res.jsonResponse({ answer: gcd(value, value2) });
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
if (method === 'isprime') {
|
|
397
|
-
let isPrime = true;
|
|
398
|
-
if (isNaN(value) || value < 1) return res.jsonResponse({ error: 'Please provide a valid number greater than or equal to 1.' });
|
|
399
|
-
for (let i = 2; i <= Math.sqrt(value); i++) {
|
|
400
|
-
if (value % i === 0) {
|
|
401
|
-
isPrime = false;
|
|
402
|
-
break;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
return res.jsonResponse({ answer: isPrime });
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
if (method === 'palindrome') return res.jsonResponse({ answer: value === value.split('').reverse().join('') });
|
|
409
|
-
|
|
410
|
-
if (method === 'primefactors') {
|
|
411
|
-
let num = value;
|
|
412
|
-
let factors = [];
|
|
413
|
-
if (isNaN(num) || num < 2 || num > 100000) return res.jsonResponse({ error: 'Please provide a valid number between 2 and 100 000.' });
|
|
414
|
-
for (let i = 2; i <= num; i++) {
|
|
415
|
-
while (num % i === 0) {
|
|
416
|
-
factors.push(i);
|
|
417
|
-
num /= i;
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
return res.jsonResponse({ answer: factors });
|
|
421
|
-
}
|
|
422
347
|
|
|
423
|
-
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
break;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
if (isPrime) primes.push(i);
|
|
435
|
-
}
|
|
436
|
-
return res.jsonResponse({ answer: primes });
|
|
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
|
+
});
|
|
437
356
|
}
|
|
438
|
-
|
|
439
|
-
if (method === 'reverse') return res.jsonResponse({ answer: value.split('').reverse().join('') });
|
|
440
357
|
});
|
|
441
358
|
|
|
442
359
|
// Generate captcha
|
|
443
360
|
app.get('/:version/captcha', (req, res) => {
|
|
444
|
-
const
|
|
445
|
-
|
|
446
|
-
if (!captcha) return res.jsonResponse({ error: 'Please provide a valid argument (?text={text})' });
|
|
447
|
-
|
|
448
|
-
const size = 60, font = '60px Comic Sans Ms', width = captcha.length * size, height = 120;
|
|
449
|
-
const canvas = createCanvas(width, height), ctx = canvas.getContext('2d');
|
|
450
|
-
|
|
451
|
-
ctx.fillStyle = 'white';
|
|
452
|
-
ctx.fillRect(0, 0, width, height);
|
|
453
|
-
|
|
454
|
-
for (let i = 0; i < 20; i++) {
|
|
455
|
-
ctx.strokeStyle = 'rgba(0, 0, 0, 0.3)';
|
|
456
|
-
ctx.beginPath();
|
|
457
|
-
ctx.moveTo(Math.random() * width, Math.random() * height);
|
|
458
|
-
ctx.lineTo(Math.random() * width, Math.random() * height);
|
|
459
|
-
ctx.lineWidth = Math.random() * 2;
|
|
460
|
-
ctx.stroke();
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
let x = (canvas.width + 20 - width) / 2;
|
|
464
|
-
for (let i = 0; i < captcha.length; i++) {
|
|
465
|
-
const offsetX = Math.cos(i * 0.3) * 10, y = height / 2.5 + Math.floor(Math.random() * (height / 2));
|
|
466
|
-
|
|
467
|
-
ctx.font = font;
|
|
468
|
-
ctx.fillStyle = `rgb(${Math.floor(Math.random() * 192)}, ${Math.floor(Math.random() * 192)}, ${Math.floor(Math.random() * 192)})`;
|
|
361
|
+
const text = req.query.text;
|
|
469
362
|
|
|
470
|
-
|
|
471
|
-
ctx.translate(x + size / 2, y);
|
|
472
|
-
ctx.rotate((Math.random() - 0.5) * 0.5);
|
|
473
|
-
ctx.fillText(captcha[i], -size / 2 + offsetX, 0);
|
|
474
|
-
ctx.restore();
|
|
363
|
+
if (!text) return res.jsonResponse({ error: 'Please provide a valid argument (?text={text})' });
|
|
475
364
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
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
|
+
});
|
|
482
373
|
}
|
|
483
|
-
|
|
484
|
-
res.set('Content-Type', 'image/png');
|
|
485
|
-
res.send(canvas.toBuffer('image/png'));
|
|
486
374
|
});
|
|
487
375
|
|
|
488
376
|
// Display stored data
|
|
489
377
|
app.get('/:version/chat', (req, res) => {
|
|
490
|
-
|
|
491
|
-
|
|
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
|
+
}
|
|
492
387
|
});
|
|
493
388
|
|
|
494
389
|
// GET private chat error
|
|
@@ -498,35 +393,15 @@ app.get('/:version/chat/private', (req, res) => {
|
|
|
498
393
|
|
|
499
394
|
// Generate color
|
|
500
395
|
app.get('/:version/color', (req, res) => {
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
const max = Math.max(r, g, b), min = Math.min(r, g, b), v = max / 255, s = max ? (max - min) / max : 0;
|
|
511
|
-
let h = max === min ? 0 : { [r]: (g - b) / (max - min), [g]: 2 + (b - r) / (max - min), [b]: 4 + (r - g) / (max - min) }[max];
|
|
512
|
-
return [h * 60 % 360, s * 100, v * 100];
|
|
513
|
-
})();
|
|
514
|
-
const hwb = (() => {
|
|
515
|
-
const [h] = hsv, whiteness = Math.min(r, g, b) / 255, blackness = 1 - Math.max(r, g, b) / 255;
|
|
516
|
-
return [h, whiteness * 100, blackness * 100];
|
|
517
|
-
})();
|
|
518
|
-
const cmyk = (() => {
|
|
519
|
-
const k = 1 - Math.max(r, g, b) / 255, c = (1 - r / 255 - k) / (1 - k) || 0, m = (1 - g / 255 - k) / (1 - k) || 0, y = (1 - b / 255 - k) / (1 - k) || 0;
|
|
520
|
-
return [c, m, y, k].map(x => x * 100);
|
|
521
|
-
})();
|
|
522
|
-
res.jsonResponse({
|
|
523
|
-
hex: `#${[r, g, b].map(x => x.toString(16).padStart(2, '0')).join('')}`,
|
|
524
|
-
rgb: `rgb(${r}, ${g}, ${b})`,
|
|
525
|
-
hsl: `hsl(${hsl[0].toFixed(1)}, ${hsl[1].toFixed(1)}%, ${hsl[2].toFixed(1)}%)`,
|
|
526
|
-
hsv: `hsv(${hsv[0].toFixed(1)}, ${hsv[1].toFixed(1)}%, ${hsv[2].toFixed(1)}%)`,
|
|
527
|
-
hwb: `hwb(${hwb[0].toFixed(1)}, ${hwb[1].toFixed(1)}%, ${hwb[2].toFixed(1)}%)`,
|
|
528
|
-
cmyk: `cmyk(${cmyk.map(x => x.toFixed(1)).join('%, ')}%)`
|
|
529
|
-
});
|
|
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
|
+
}
|
|
530
405
|
});
|
|
531
406
|
|
|
532
407
|
// Convert units
|
|
@@ -537,50 +412,28 @@ app.get('/:version/convert', (req, res) => {
|
|
|
537
412
|
if (!from) return res.jsonResponse({ error: 'Please provide a valid source unit (&from={unit})' });
|
|
538
413
|
if (!to) return res.jsonResponse({ error: 'Please provide a valid target unit (&to={unit})' });
|
|
539
414
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
res.jsonResponse({ from, to, value: parseFloat(value), result: convert(parseFloat(value)) });
|
|
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
|
+
}
|
|
550
424
|
});
|
|
551
425
|
|
|
552
426
|
// Generate domain informations
|
|
553
427
|
app.get('/:version/domain', (req, res) => {
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
res.jsonResponse({
|
|
565
|
-
domain,
|
|
566
|
-
full_domain: fulldomain,
|
|
567
|
-
ip_address: ips,
|
|
568
|
-
ssl_certified: Math.random() > 0.5,
|
|
569
|
-
hosting_provider: random(['AWS', 'Bluehost', 'DigitalOcean', 'GitHub', 'HostGator', 'Render', 'SiteGround']),
|
|
570
|
-
dns_servers: dns,
|
|
571
|
-
dns_provider: random(['AWS Route 53', 'Cloudflare', 'GoDaddy', 'Google DNS', 'Namecheap']),
|
|
572
|
-
traffic: `${Math.floor(Math.random() * 10000)} visits/day`,
|
|
573
|
-
seo_score: Math.floor(Math.random() * 100),
|
|
574
|
-
page_rank: Math.floor(Math.random() * 10),
|
|
575
|
-
country: random(['Australia', 'Canada', 'France', 'Germany', 'India', 'Japan', 'UK', 'USA']),
|
|
576
|
-
website_type: random(['Blog', 'Community', 'Corporate', 'Educational', 'E-commerce', 'Personal', 'Portfolio']),
|
|
577
|
-
random_name: domain.split('.')[0],
|
|
578
|
-
random_subdomain: fulldomain.split('.')[0],
|
|
579
|
-
random_tld: domain.split('.').pop(),
|
|
580
|
-
backlinks_count: Math.floor(Math.random() * 1000),
|
|
581
|
-
creation_date: new Date(Date.now() - Math.floor(Math.random() * 10000000000)).toISOString(),
|
|
582
|
-
expiration_date: new Date(Date.now() + Math.floor(Math.random() * 10000000000)).toISOString(),
|
|
583
|
-
});
|
|
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
|
+
}
|
|
584
437
|
});
|
|
585
438
|
|
|
586
439
|
// GET planning error
|
|
@@ -595,9 +448,15 @@ app.get('/:version/hash', (req, res) => {
|
|
|
595
448
|
|
|
596
449
|
// Display API informations
|
|
597
450
|
app.get('/:version/infos', (req, res) => {
|
|
451
|
+
const endpoints = Object.values(versions[req.version].endpoints).flat();
|
|
452
|
+
|
|
453
|
+
const paths = endpoints.flatMap(e => e.children
|
|
454
|
+
? Object.values(e.children)
|
|
455
|
+
: (e.path ? [e.path] : []));
|
|
456
|
+
|
|
598
457
|
res.jsonResponse({
|
|
599
|
-
endpoints:
|
|
600
|
-
last_version: versions.
|
|
458
|
+
endpoints: new Set(paths).size,
|
|
459
|
+
last_version: Object.keys(versions).pop(),
|
|
601
460
|
documentation: 'https://docs.sylvain.pro',
|
|
602
461
|
github: 'https://github.com/20syldev/api',
|
|
603
462
|
creation: 'November 25th 2024',
|
|
@@ -611,134 +470,28 @@ app.get('/:version/levenshtein', (req, res) => {
|
|
|
611
470
|
if (!str1 || typeof str1 !== 'string') return res.jsonResponse({ error: 'Please provide a first string (?str1={string})' });
|
|
612
471
|
if (!str2 || typeof str2 !== 'string') return res.jsonResponse({ error: 'Please provide a second string (&str2={string})' });
|
|
613
472
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
return m[a.length][b.length];
|
|
625
|
-
};
|
|
626
|
-
|
|
627
|
-
res.jsonResponse({ str1, str2, distance: lev(str1, str2) });
|
|
473
|
+
try {
|
|
474
|
+
const result = req.module.levenshtein(str1, str2);
|
|
475
|
+
res.jsonResponse(result);
|
|
476
|
+
} catch (err) {
|
|
477
|
+
res.jsonResponse({
|
|
478
|
+
error: err.message,
|
|
479
|
+
documentation: `https://docs.sylvain.pro/${req.version}/en/levenshtein`
|
|
480
|
+
});
|
|
481
|
+
}
|
|
628
482
|
});
|
|
629
483
|
|
|
630
484
|
// Generate personal data
|
|
631
485
|
app.get('/:version/personal', (req, res) => {
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
{
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
{ name: 'Daniel Brown', social: 'daniel_brown', email: 'daniel@example.com', country: 'JP' },
|
|
640
|
-
{ name: 'Sophia Wilson', social: 'sophia_wilson', email: 'sophia@example.com', country: 'BR' },
|
|
641
|
-
{ name: 'James Taylor', social: 'james_taylor', email: 'james@example.com', country: 'CA' },
|
|
642
|
-
{ name: 'Olivia Thomas', social: 'olivia_thomas', email: 'olivia@example.com', country: 'AU' }
|
|
643
|
-
];
|
|
644
|
-
|
|
645
|
-
const countries = {
|
|
646
|
-
US: { tel: '123-456-7890', code: '1', lang: 'English' },
|
|
647
|
-
FR: { tel: '06 78 90 12 34', code: '33', lang: 'French' },
|
|
648
|
-
UK: { tel: '7911 123456', code: '44', lang: 'English' },
|
|
649
|
-
ES: { tel: '678 901 234', code: '34', lang: 'Spanish' },
|
|
650
|
-
DE: { tel: '163 555 1584', code: '49', lang: 'German' },
|
|
651
|
-
IT: { tel: '345 678 9012', code: '39', lang: 'Italian' },
|
|
652
|
-
JP: { tel: '080-1234-5678', code: '81', lang: 'Japanese' },
|
|
653
|
-
BR: { tel: '(11) 98765-4321', code: '55', lang: 'Portuguese' },
|
|
654
|
-
CA: { tel: '416-123-4567', code: '1', lang: 'English' },
|
|
655
|
-
AU: { tel: '0412 345 678', code: '61', lang: 'English' }
|
|
656
|
-
};
|
|
657
|
-
|
|
658
|
-
const jobs = ['Writer', 'Artist', 'Musician', 'Explorer', 'Scientist', 'Engineer', 'Athlete', 'Doctor'];
|
|
659
|
-
const hobbies = ['Reading', 'Traveling', 'Gaming', 'Cooking', 'Fitness', 'Music', 'Photography', 'Writing'];
|
|
660
|
-
const cities = ['New York', 'Paris', 'London', 'Madrid', 'Berlin', 'Rome', 'Tokyo', 'Los Angeles', 'Sydney', 'São Paulo', 'Toronto'];
|
|
661
|
-
const streets = ['Main St', '2nd Ave', 'Broadway', 'Park Lane', 'Elm St', 'Sunset Blvd', 'Maple St', 'Highland Rd'];
|
|
662
|
-
|
|
663
|
-
const card = Array.from({ length: 4 }, () => Math.floor(Math.random() * 9000) + 1000).join(' ');
|
|
664
|
-
const cvc = Math.floor(Math.random() * 900) + 100;
|
|
665
|
-
const expiration = `${String(Math.floor(Math.random() * 12) + 1).padStart(2, '0')}/${(new Date().getFullYear() + Math.floor(Math.random() * 3)).toString().slice(-2)}`;
|
|
666
|
-
|
|
667
|
-
const person = random(people);
|
|
668
|
-
const social = person.social;
|
|
669
|
-
const country = person.country;
|
|
670
|
-
const phone = countries[country].tel;
|
|
671
|
-
const lang = countries[country].lang;
|
|
672
|
-
|
|
673
|
-
const age = Math.floor(Math.random() * 50) + 18;
|
|
674
|
-
const birthday = new Date(Date.now() - Math.floor((Math.random() * 50 + 18) * 365.25 * 24 * 60 * 60 * 1000)).toISOString();
|
|
675
|
-
|
|
676
|
-
let emergencyContacts = [], yearIncome = Math.floor(Math.random() * 100000), subscriptions = [], pets = [], vehicles = [];
|
|
677
|
-
let civilStatus = 'Single';
|
|
678
|
-
let children = 0;
|
|
679
|
-
|
|
680
|
-
if (age >= 21 && Math.random() > 0.7) civilStatus = 'Married';
|
|
681
|
-
|
|
682
|
-
if (civilStatus === 'Married' && age >= 25) children = Math.floor(Math.random() * 4);
|
|
683
|
-
|
|
684
|
-
while (emergencyContacts.length < Math.floor(Math.random() * 3) + 1) {
|
|
685
|
-
let emergencyContact = random(people);
|
|
686
|
-
while (emergencyContact.email === person.email || emergencyContacts.some(e => e.email === emergencyContact.email)) {
|
|
687
|
-
emergencyContact = random(people);
|
|
688
|
-
}
|
|
689
|
-
emergencyContacts.push({
|
|
690
|
-
name: emergencyContact.name,
|
|
691
|
-
relationship: random(['Spouse', 'Parent', 'Sibling', 'Friend']),
|
|
692
|
-
phone: `+${countries[country].code} ${countries[country].tel}`
|
|
486
|
+
try {
|
|
487
|
+
const result = req.module.personal();
|
|
488
|
+
res.jsonResponse(result);
|
|
489
|
+
} catch (err) {
|
|
490
|
+
res.jsonResponse({
|
|
491
|
+
error: err.message,
|
|
492
|
+
documentation: `https://docs.sylvain.pro/${req.version}/en/personal`
|
|
693
493
|
});
|
|
694
494
|
}
|
|
695
|
-
|
|
696
|
-
while (subscriptions.length < Math.floor(Math.random() * 3) + 1) {
|
|
697
|
-
let subscription = random(['Netflix', 'Spotify', 'Amazon Prime', 'Disney+', 'Hulu']);
|
|
698
|
-
if (!subscriptions.includes(subscription)) subscriptions.push(subscription);
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
while (pets.length < Math.floor(Math.random() * 3) + 1) {
|
|
702
|
-
let pet = random(['Dog', 'Cat', 'Fish', 'Bird', 'None']);
|
|
703
|
-
if (!pets.includes(pet)) pets.push(pet);
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
while (vehicles.length < Math.floor(Math.random() * 3) + 1) {
|
|
707
|
-
let vehicle = random(['Car', 'Bike', 'Motorcycle', 'Bus', 'None']);
|
|
708
|
-
if (!vehicles.includes(vehicle)) vehicles.push(vehicle);
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
res.jsonResponse({
|
|
712
|
-
name: person.name,
|
|
713
|
-
email: person.email,
|
|
714
|
-
localisation: country,
|
|
715
|
-
phone: `+${countries[country].code} ${phone}`,
|
|
716
|
-
job: random(jobs),
|
|
717
|
-
hobbies: random(hobbies),
|
|
718
|
-
language: lang,
|
|
719
|
-
card,
|
|
720
|
-
cvc,
|
|
721
|
-
expiration,
|
|
722
|
-
address: `${Math.floor(Math.random() * 9999)} ${random(streets)}, ${random(cities)}`,
|
|
723
|
-
birthday,
|
|
724
|
-
civil_status: civilStatus,
|
|
725
|
-
children,
|
|
726
|
-
vehicle: vehicles,
|
|
727
|
-
social_profiles: {
|
|
728
|
-
twitter: `@${social}`,
|
|
729
|
-
facebook: `facebook.com/${social}`,
|
|
730
|
-
linkedin: `linkedin.com/in/${social}`,
|
|
731
|
-
instagram: `instagram.com/${social}`
|
|
732
|
-
},
|
|
733
|
-
year_income: `${yearIncome} USD/year`,
|
|
734
|
-
month_income: `${(yearIncome / 12).toFixed(2)} USD/month`,
|
|
735
|
-
education: random(['High School', 'Bachelor\'s', 'Master\'s', 'PhD']),
|
|
736
|
-
work_experience: `${Math.floor(Math.random() * 20)} years`,
|
|
737
|
-
health_status: random(['Healthy', 'Minor Issues', 'Chronic Conditions']),
|
|
738
|
-
emergency_contacts: emergencyContacts,
|
|
739
|
-
subscriptions,
|
|
740
|
-
pets,
|
|
741
|
-
});
|
|
742
495
|
});
|
|
743
496
|
|
|
744
497
|
// Generate QR Code
|
|
@@ -747,8 +500,15 @@ app.get('/:version/qrcode', async (req, res) => {
|
|
|
747
500
|
|
|
748
501
|
if (!url) return res.jsonResponse({ error: 'Please provide a valid url (?url={URL})' });
|
|
749
502
|
|
|
750
|
-
try {
|
|
751
|
-
|
|
503
|
+
try {
|
|
504
|
+
const result = await req.module.qrcode(url);
|
|
505
|
+
res.jsonResponse(result);
|
|
506
|
+
} catch (err) {
|
|
507
|
+
res.jsonResponse({
|
|
508
|
+
error: err.message,
|
|
509
|
+
documentation: `https://docs.sylvain.pro/${req.version}/en/qrcode`
|
|
510
|
+
});
|
|
511
|
+
}
|
|
752
512
|
});
|
|
753
513
|
|
|
754
514
|
// GET tic-tac-toe game error
|
|
@@ -774,44 +534,15 @@ app.get('/:version/time', (req, res) => {
|
|
|
774
534
|
if (format && !validFormats.includes(format)) return res.jsonResponse({ error: 'Please provide a valid format (?format={format})' });
|
|
775
535
|
if (timezone && !validTimezones.includes(timezone)) return res.jsonResponse({ error: 'Please provide a valid timezone (?timezone={timezone})' });
|
|
776
536
|
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
year: date.getFullYear(),
|
|
786
|
-
month: date.getMonth() + 1,
|
|
787
|
-
day: date.getDate(),
|
|
788
|
-
hour: date.getHours(),
|
|
789
|
-
minute: date.getMinutes(),
|
|
790
|
-
second: date.getSeconds(),
|
|
791
|
-
ms: date.getMilliseconds(),
|
|
792
|
-
dayOfWeek: date.getDay(),
|
|
793
|
-
dayOfYear: Math.floor((date - new Date(date.getFullYear(), 0, 0)) / 86400000),
|
|
794
|
-
weekNumber: Math.ceil((((date - new Date(date.getFullYear(), 0, 0)) / 86400000) + 1) / 7),
|
|
795
|
-
timezone: timezoneOption,
|
|
796
|
-
timezoneOffset: date.getTimezoneOffset()
|
|
797
|
-
};
|
|
798
|
-
};
|
|
799
|
-
|
|
800
|
-
if (type === 'random') {
|
|
801
|
-
const startDate = new Date(start || '1900-01-01').getTime();
|
|
802
|
-
const endDate = new Date(end || '2100-12-31').getTime();
|
|
803
|
-
const randomDate = new Date(start ? startDate : startDate + Math.random() * (endDate - startDate));
|
|
804
|
-
const timezoneOption = timezone || validTimezones[Math.floor(Math.random() * 5)];
|
|
805
|
-
const formats = getTimeFormats(randomDate, timezoneOption);
|
|
806
|
-
|
|
807
|
-
return res.jsonResponse(format && formats[format] ? { date: formats[format] } : formats);
|
|
537
|
+
try {
|
|
538
|
+
const time = req.module.time(type, start, end, format, timezone);
|
|
539
|
+
res.jsonResponse(time);
|
|
540
|
+
} catch (err) {
|
|
541
|
+
res.jsonResponse({
|
|
542
|
+
error: err.message,
|
|
543
|
+
documentation: `https://docs.sylvain.pro/${req.version}/en/time`
|
|
544
|
+
});
|
|
808
545
|
}
|
|
809
|
-
|
|
810
|
-
const now = new Date();
|
|
811
|
-
const timezoneOption = timezone || 'UTC';
|
|
812
|
-
const formats = getTimeFormats(now, timezoneOption);
|
|
813
|
-
|
|
814
|
-
res.jsonResponse(format && formats[format] ? { date: formats[format] } : formats);
|
|
815
546
|
});
|
|
816
547
|
|
|
817
548
|
// GET token error
|
|
@@ -821,24 +552,15 @@ app.get('/:version/token', (req, res) => {
|
|
|
821
552
|
|
|
822
553
|
// Generate username
|
|
823
554
|
app.get('/:version/username', (req, res) => {
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
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 });
|
|
555
|
+
try {
|
|
556
|
+
const result = req.module.username();
|
|
557
|
+
res.jsonResponse(result);
|
|
558
|
+
} catch (err) {
|
|
559
|
+
res.jsonResponse({
|
|
560
|
+
error: err.message,
|
|
561
|
+
documentation: `https://docs.sylvain.pro/${req.version}/en/username`
|
|
562
|
+
});
|
|
563
|
+
}
|
|
842
564
|
});
|
|
843
565
|
|
|
844
566
|
// Display informations for owner's website
|
|
@@ -910,6 +632,7 @@ app.get('/:version/website', async (req, res) => {
|
|
|
910
632
|
flowers: process.env.FLOWERS,
|
|
911
633
|
gemsync: process.env.GEMSYNC,
|
|
912
634
|
gitsite: process.env.GITSITE,
|
|
635
|
+
lebonchar: process.env.LEBONCHAR,
|
|
913
636
|
logs: process.env.LOGS,
|
|
914
637
|
logvault: process.env.LOGVAULT,
|
|
915
638
|
lyah: process.env.LYAH,
|
|
@@ -955,33 +678,19 @@ app.post('/:version/chat', (req, res) => {
|
|
|
955
678
|
if (!message) return res.jsonResponse({ error: 'Please provide a message (&message={message})' });
|
|
956
679
|
if (!session) return res.jsonResponse({ error: 'Please provide a valid session ID (&session={ID})' });
|
|
957
680
|
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
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);
|
|
681
|
+
try {
|
|
682
|
+
const result = req.module.chat('message', {
|
|
683
|
+
username,
|
|
684
|
+
message,
|
|
685
|
+
timestamp,
|
|
686
|
+
session,
|
|
687
|
+
token,
|
|
688
|
+
storage: chatStorage
|
|
689
|
+
});
|
|
690
|
+
res.jsonResponse(result);
|
|
691
|
+
} catch (err) {
|
|
692
|
+
res.jsonResponse({ error: err.message });
|
|
977
693
|
}
|
|
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
694
|
});
|
|
986
695
|
|
|
987
696
|
// Display a private chat with a token
|
|
@@ -991,36 +700,37 @@ app.post('/:version/chat/private', (req, res) => {
|
|
|
991
700
|
if (!username) return res.jsonResponse({ error: 'Please provide a username (?username={username})' });
|
|
992
701
|
if (!token) return res.jsonResponse({ error: 'Please provide a valid token (&token={key}).' });
|
|
993
702
|
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
703
|
+
try {
|
|
704
|
+
const messages = req.module.chat('private', {
|
|
705
|
+
username,
|
|
706
|
+
token,
|
|
707
|
+
storage: chatStorage
|
|
708
|
+
});
|
|
709
|
+
res.jsonResponse(messages);
|
|
710
|
+
} catch (err) {
|
|
711
|
+
res.jsonResponse({ error: err.message });
|
|
1000
712
|
}
|
|
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
713
|
});
|
|
1007
714
|
|
|
1008
715
|
// Generate hash
|
|
1009
716
|
app.post('/:version/hash', (req, res) => {
|
|
1010
717
|
const { text, method } = req.body;
|
|
1011
|
-
const { version } = req.params;
|
|
1012
718
|
|
|
1013
719
|
if (!text) return res.jsonResponse({ error: 'Please provide a text (?text={text})' });
|
|
1014
720
|
if (!method) return res.jsonResponse({
|
|
1015
721
|
error: 'Please provide a valid hash algorithm (&method={algorithm})',
|
|
1016
|
-
documentation: `https://docs.sylvain.pro/${version}/en/hash`
|
|
722
|
+
documentation: `https://docs.sylvain.pro/${req.version}/en/hash`
|
|
1017
723
|
});
|
|
1018
724
|
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
725
|
+
try {
|
|
726
|
+
const result = req.module.hash(text, method);
|
|
727
|
+
res.jsonResponse(result);
|
|
728
|
+
} catch (err) {
|
|
729
|
+
res.jsonResponse({
|
|
730
|
+
error: err.message,
|
|
731
|
+
documentation: `https://docs.sylvain.pro/${req.version}/en/hash`
|
|
732
|
+
})
|
|
733
|
+
}
|
|
1024
734
|
});
|
|
1025
735
|
|
|
1026
736
|
// Display a planning from an ICS file
|
|
@@ -1030,40 +740,14 @@ app.post('/:version/hyperplanning', async (req, res) => {
|
|
|
1030
740
|
if (!url) return res.jsonResponse({ error: 'Please provide a valid ICS file URL (?url={URL})' });
|
|
1031
741
|
|
|
1032
742
|
try {
|
|
1033
|
-
const
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
.
|
|
1038
|
-
.
|
|
1039
|
-
|
|
1040
|
-
|
|
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.' }); }
|
|
743
|
+
const hyperplanning = req.module.hyperplanning(url, detail);
|
|
744
|
+
res.jsonResponse(hyperplanning);
|
|
745
|
+
} catch (err){
|
|
746
|
+
res.jsonResponse({
|
|
747
|
+
error: 'Failed to parse ICS file.',
|
|
748
|
+
documentation: `https://docs.sylvain.pro/${req.version}/en/hyperplanning`
|
|
749
|
+
});
|
|
750
|
+
}
|
|
1067
751
|
});
|
|
1068
752
|
|
|
1069
753
|
// Store tic tac toe games
|
|
@@ -1075,46 +759,18 @@ app.post('/:version/tic-tac-toe', (req, res) => {
|
|
|
1075
759
|
if (!session) return res.jsonResponse({ error: 'Please provide a valid session ID (&session={ID})' });
|
|
1076
760
|
if (!game) return res.jsonResponse({ error: 'Please provide a valid game ID (&game={ID})' });
|
|
1077
761
|
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
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
|
|
762
|
+
try {
|
|
763
|
+
const result = req.module.tic_tac_toe('play', {
|
|
764
|
+
username,
|
|
765
|
+
move,
|
|
766
|
+
session,
|
|
767
|
+
game,
|
|
768
|
+
storage: ticTacToeStorage
|
|
1108
769
|
});
|
|
770
|
+
res.jsonResponse(result);
|
|
771
|
+
} catch (err) {
|
|
772
|
+
res.jsonResponse({ error: err.message });
|
|
1109
773
|
}
|
|
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
774
|
});
|
|
1119
775
|
|
|
1120
776
|
// Display a tic tac toe game with a token
|
|
@@ -1123,25 +779,16 @@ app.post('/:version/tic-tac-toe/fetch', (req, res) => {
|
|
|
1123
779
|
|
|
1124
780
|
if (!username) return res.jsonResponse({ error: 'Please provide a username (?username={username})' });
|
|
1125
781
|
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
782
|
+
try {
|
|
783
|
+
const result = req.module.tic_tac_toe('fetch', {
|
|
784
|
+
username,
|
|
785
|
+
game,
|
|
786
|
+
storage: ticTacToeStorage
|
|
787
|
+
});
|
|
788
|
+
res.jsonResponse(result);
|
|
789
|
+
} catch (err) {
|
|
790
|
+
res.jsonResponse({ error: err.message });
|
|
1133
791
|
}
|
|
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
792
|
});
|
|
1146
793
|
|
|
1147
794
|
// Generate Token
|
|
@@ -1155,18 +802,15 @@ app.post('/:version/token', (req, res) => {
|
|
|
1155
802
|
if (len > 4096) return res.jsonResponse({ error: 'Length cannot exceed 4096.' });
|
|
1156
803
|
if (len < 12) return res.jsonResponse({ error: 'Length cannot be less than 12.' });
|
|
1157
804
|
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
}[type] || genToken('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', len);
|
|
1168
|
-
|
|
1169
|
-
res.jsonResponse({ token });
|
|
805
|
+
try {
|
|
806
|
+
const token = req.module.token(len, type);
|
|
807
|
+
res.jsonResponse({ token });
|
|
808
|
+
} catch (err) {
|
|
809
|
+
return res.jsonResponse({
|
|
810
|
+
error: err.message,
|
|
811
|
+
documentation: `https://docs.sylvain.pro/${req.version}/en/token`
|
|
812
|
+
});
|
|
813
|
+
}
|
|
1170
814
|
});
|
|
1171
815
|
|
|
1172
816
|
// ----------- ----------- SERVER SETUP ----------- ----------- //
|