fbp-language 1.1.2 → 1.1.4
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 +27 -2
- package/dist/cli.js +1 -1
- package/dist/compiler.js +111 -6
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Puis, dans le `package.json` de ton projet :
|
|
|
23
23
|
"start": "fbp run lancer"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"fbp-language": "^1.1.
|
|
26
|
+
"fbp-language": "^1.1.4"
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
```
|
|
@@ -106,6 +106,9 @@ Dans `scripts/main.fbp` :
|
|
|
106
106
|
discordConnexion()
|
|
107
107
|
discordCommande("ping", "Pong !")
|
|
108
108
|
discordCommande("bonjour", "Salut !")
|
|
109
|
+
discordCommandeEmbed("serveur-info", "Informations du serveur", "Voici les informations disponibles.", "#5865F2")
|
|
110
|
+
discordChamp("serveur-info", "Nom", "Mon serveur")
|
|
111
|
+
discordChamp("serveur-info", "Membres", "42")
|
|
109
112
|
```
|
|
110
113
|
|
|
111
114
|
Associe `lancer` à `scripts/main.fbp` dans `commandes.fbp`, puis exécute `npx fbp run lancer`. Dans le portail développeur Discord, vérifie que le bot est invité sur ton serveur avec les scopes `bot` et `applications.commands`. Seul l'intent serveur (`Guilds`) est nécessaire.
|
|
@@ -114,7 +117,7 @@ FBP enregistre les commandes déclarées comme commandes slash globales de l'app
|
|
|
114
117
|
|
|
115
118
|
## Limites actuelles
|
|
116
119
|
|
|
117
|
-
FBP est encore un interpréteur de base. Les paramètres de fonction, les conditions, les boucles, les valeurs de retour et les expressions arithmétiques ne sont pas encore implémentés. La connexion Discord utilise un token stocké dans `.env`. Les commandes slash
|
|
120
|
+
FBP est encore un interpréteur de base. Les paramètres de fonction, les conditions, les boucles, les valeurs de retour et les expressions arithmétiques ne sont pas encore implémentés. La connexion Discord utilise un token stocké dans `.env`. Les commandes slash peuvent répondre avec un texte fixe ou un embed défini avec `discordCommandeEmbed(nom, titre, description, couleur)`. Ajoute des champs avec `discordChamp(nomCommande, nomChamp, valeur)`. Les valeurs des embeds sont actuellement fixes : la récupération dynamique des informations du serveur, les options, paramètres, permissions avancées et conditions ne sont pas encore implémentés.
|
|
118
121
|
|
|
119
122
|
## Développement du dépôt
|
|
120
123
|
|
|
@@ -124,3 +127,25 @@ npm test
|
|
|
124
127
|
```
|
|
125
128
|
|
|
126
129
|
Les fichiers d'essai internes se trouvent dans `tests/fixtures`. Ils ne font pas partie du paquet publié.
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
## Informations Discord dynamiques (v1.1.4)
|
|
133
|
+
|
|
134
|
+
Les champs d'un embed peuvent récupérer les informations accessibles au moment où la commande est utilisée. Exemple :
|
|
135
|
+
|
|
136
|
+
```fbp
|
|
137
|
+
discordConnexion()
|
|
138
|
+
discordCommandeEmbed("serveur-info", "Informations du serveur", "Données actuelles", "#5865F2")
|
|
139
|
+
discordChampDynamique("serveur-info", "Nom", "discordServeur.nom")
|
|
140
|
+
discordChampDynamique("serveur-info", "Identifiant", "discordServeur.id")
|
|
141
|
+
discordChampDynamique("serveur-info", "Création", "discordServeur.creation")
|
|
142
|
+
discordChampDynamique("serveur-info", "Membres", "discordMembres.nombre")
|
|
143
|
+
discordChampDynamique("serveur-info", "Rôles", "discordRoles.nombre")
|
|
144
|
+
discordChampDynamique("serveur-info", "Salons", "discordSalons.nombre")
|
|
145
|
+
discordChampDynamique("serveur-info", "Latence", "discordLatence")
|
|
146
|
+
discordOptionUtilisateur("serveur-info", "membre", "Membre à afficher")
|
|
147
|
+
discordChampDynamique("serveur-info", "Profil", "discordProfil.nom")
|
|
148
|
+
discordChampDynamique("serveur-info", "Avatar", "discordAvatar")
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Sources disponibles : `discordServeur.nom`, `discordServeur.id`, `discordServeur.creation`, `discordMembres.nombre`, `discordProfil.nom`, `discordProfil.id`, `discordProfil.avatar`, `discordRoles.nombre`, `discordSalons.nombre`, `discordStatistiques.membres`, `discordStatistiques.roles`, `discordStatistiques.salons`, `discordLatence` et `discordAvatar`. `discordOptionUtilisateur(nomCommande, nomOption, description)` ajoute un sélecteur facultatif de membre à une commande embed. Les données accessibles dépendent des permissions et du cache Discord du bot.
|
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
8
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
9
|
const compiler_1 = require("./compiler");
|
|
10
10
|
const errors_1 = require("./errors");
|
|
11
|
-
const VERSION = "1.1.
|
|
11
|
+
const VERSION = "1.1.4";
|
|
12
12
|
function help() {
|
|
13
13
|
console.log(`FBP — French Bot Programming
|
|
14
14
|
|
package/dist/compiler.js
CHANGED
|
@@ -112,6 +112,26 @@ class FbpCompiler {
|
|
|
112
112
|
constructor(options = {}) {
|
|
113
113
|
this.output = options.onOutput ?? (value => console.log(value));
|
|
114
114
|
}
|
|
115
|
+
resolveDiscordValue(source, interaction, selectedUser, selectedMember) {
|
|
116
|
+
const guild = interaction.guild;
|
|
117
|
+
const values = {
|
|
118
|
+
"discordServeur.nom": guild?.name ?? "Serveur indisponible",
|
|
119
|
+
"discordServeur.id": guild?.id ?? "Indisponible",
|
|
120
|
+
"discordServeur.creation": guild ? "<t:" + Math.floor(guild.createdTimestamp / 1000) + ":D>" : "Indisponible",
|
|
121
|
+
"discordMembres.nombre": guild ? String(guild.memberCount) : "Indisponible",
|
|
122
|
+
"discordProfil.nom": selectedMember?.displayName ?? selectedUser?.username ?? "Sélectionne un membre",
|
|
123
|
+
"discordProfil.id": selectedUser?.id ?? "Sélectionne un membre",
|
|
124
|
+
"discordProfil.avatar": selectedUser?.displayAvatarURL({ size: 256 }) ?? "Sélectionne un membre",
|
|
125
|
+
"discordRoles.nombre": guild ? String(guild.roles.cache.size) : "Indisponible",
|
|
126
|
+
"discordSalons.nombre": guild ? String(guild.channels.cache.size) : "Indisponible",
|
|
127
|
+
"discordStatistiques.membres": guild ? String(guild.memberCount) : "Indisponible",
|
|
128
|
+
"discordStatistiques.roles": guild ? String(guild.roles.cache.size) : "Indisponible",
|
|
129
|
+
"discordStatistiques.salons": guild ? String(guild.channels.cache.size) : "Indisponible",
|
|
130
|
+
"discordLatence": String(Math.max(0, Math.round(interaction.client.ws.ping))) + " ms",
|
|
131
|
+
"discordAvatar": selectedUser?.displayAvatarURL({ size: 256 }) ?? interaction.user.displayAvatarURL({ size: 256 })
|
|
132
|
+
};
|
|
133
|
+
return source.trim() in values ? values[source.trim()] : "Source inconnue : " + source.trim();
|
|
134
|
+
}
|
|
115
135
|
runFile(filePath) {
|
|
116
136
|
const absolute = node_path_1.default.resolve(filePath);
|
|
117
137
|
if (!node_fs_1.default.existsSync(absolute))
|
|
@@ -188,10 +208,17 @@ class FbpCompiler {
|
|
|
188
208
|
this.discordClient = client;
|
|
189
209
|
client.once("ready", async () => {
|
|
190
210
|
try {
|
|
191
|
-
const commands = [...this.discordCommands.entries()].map(([commandName]) =>
|
|
192
|
-
.
|
|
193
|
-
|
|
194
|
-
|
|
211
|
+
const commands = [...this.discordCommands.entries()].map(([commandName, response]) => {
|
|
212
|
+
const builder = new discord_js_1.SlashCommandBuilder()
|
|
213
|
+
.setName(commandName)
|
|
214
|
+
.setDescription(`Commande ${commandName} du bot FBP`);
|
|
215
|
+
if (response.type === "embed" && response.userOption) {
|
|
216
|
+
builder.addUserOption(option => option.setName(response.userOption.name)
|
|
217
|
+
.setDescription(response.userOption.description)
|
|
218
|
+
.setRequired(false));
|
|
219
|
+
}
|
|
220
|
+
return builder.toJSON();
|
|
221
|
+
});
|
|
195
222
|
const manager = client.application?.commands;
|
|
196
223
|
if (!manager)
|
|
197
224
|
throw new Error("L'application Discord n'est pas disponible.");
|
|
@@ -217,7 +244,26 @@ class FbpCompiler {
|
|
|
217
244
|
if (response === undefined)
|
|
218
245
|
return;
|
|
219
246
|
try {
|
|
220
|
-
|
|
247
|
+
if (response.type === "text") {
|
|
248
|
+
await interaction.reply(response.content);
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
const embed = new discord_js_1.EmbedBuilder()
|
|
252
|
+
.setTitle(response.title)
|
|
253
|
+
.setDescription(response.description)
|
|
254
|
+
.setColor(Number.parseInt(response.color.slice(1), 16));
|
|
255
|
+
const guild = interaction.guild;
|
|
256
|
+
const selectedUser = response.userOption ? interaction.options.getUser(response.userOption.name) : null;
|
|
257
|
+
const selectedMember = selectedUser && guild ? await guild.members.fetch(selectedUser.id).catch(() => null) : null;
|
|
258
|
+
const fields = response.fields.map(field => ({
|
|
259
|
+
name: field.name,
|
|
260
|
+
value: field.dynamic ? this.resolveDiscordValue(field.value, interaction, selectedUser, selectedMember) : field.value,
|
|
261
|
+
inline: false
|
|
262
|
+
}));
|
|
263
|
+
if (fields.length)
|
|
264
|
+
embed.addFields(fields);
|
|
265
|
+
await interaction.reply({ embeds: [embed] });
|
|
266
|
+
}
|
|
221
267
|
}
|
|
222
268
|
catch (error) {
|
|
223
269
|
this.output("Erreur Discord : " + (error instanceof Error ? error.message : String(error)));
|
|
@@ -236,7 +282,66 @@ class FbpCompiler {
|
|
|
236
282
|
if (!/^[a-z0-9_-]{1,32}$/.test(commandName)) {
|
|
237
283
|
throw new errors_1.FbpError("Le nom d'une commande slash doit contenir 1 à 32 caractères : lettres minuscules, chiffres, tirets ou underscores.", undefined, line);
|
|
238
284
|
}
|
|
239
|
-
this.discordCommands.set(commandName, values[1]);
|
|
285
|
+
this.discordCommands.set(commandName, { type: "text", content: values[1] });
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
if (name === "discordCommandeEmbed") {
|
|
289
|
+
if (!this.discordClient)
|
|
290
|
+
throw new errors_1.FbpError("Appelle discordConnexion() avant de déclarer une commande.", undefined, line);
|
|
291
|
+
if (values.length !== 4 || values.some(value => typeof value !== "string")) {
|
|
292
|
+
throw new errors_1.FbpError("Utilisation : discordCommandeEmbed(nom, titre, description, couleur).", undefined, line);
|
|
293
|
+
}
|
|
294
|
+
const [commandName, title, description, color] = values;
|
|
295
|
+
if (!/^[a-z0-9_-]{1,32}$/.test(commandName))
|
|
296
|
+
throw new errors_1.FbpError("Le nom d'une commande slash doit contenir 1 à 32 caractères : lettres minuscules, chiffres, tirets ou underscores.", undefined, line);
|
|
297
|
+
if (!/^#[0-9a-fA-F]{6}$/.test(color))
|
|
298
|
+
throw new errors_1.FbpError("La couleur d'un embed doit être au format hexadécimal, par exemple #5865F2.", undefined, line);
|
|
299
|
+
this.discordCommands.set(commandName, { type: "embed", title, description, color, fields: [] });
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
if (name === "discordChampDynamique") {
|
|
303
|
+
if (values.length !== 3 || values.some(value => typeof value !== "string")) {
|
|
304
|
+
throw new errors_1.FbpError("Utilisation : discordChampDynamique(nomCommande, nomChamp, source).", undefined, line);
|
|
305
|
+
}
|
|
306
|
+
const [commandName, fieldName, source] = values;
|
|
307
|
+
const command = this.discordCommands.get(commandName);
|
|
308
|
+
if (!command || command.type !== "embed")
|
|
309
|
+
throw new errors_1.FbpError("La commande « " + commandName + " » n'est pas un embed déclaré.", undefined, line);
|
|
310
|
+
if (command.fields.length >= 25)
|
|
311
|
+
throw new errors_1.FbpError("Un embed Discord ne peut pas contenir plus de 25 champs.", undefined, line);
|
|
312
|
+
if (!fieldName.trim() || !source.trim() || fieldName.length > 256 || source.length > 1024)
|
|
313
|
+
throw new errors_1.FbpError("Un champ dynamique doit avoir un nom et une source valides.", undefined, line);
|
|
314
|
+
command.fields.push({ name: fieldName, value: source, dynamic: true });
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
if (name === "discordOptionUtilisateur") {
|
|
318
|
+
if (values.length !== 3 || values.some(value => typeof value !== "string")) {
|
|
319
|
+
throw new errors_1.FbpError("Utilisation : discordOptionUtilisateur(nomCommande, nomOption, description).", undefined, line);
|
|
320
|
+
}
|
|
321
|
+
const [commandName, optionName, description] = values;
|
|
322
|
+
const command = this.discordCommands.get(commandName);
|
|
323
|
+
if (!command || command.type !== "embed")
|
|
324
|
+
throw new errors_1.FbpError("La commande « " + commandName + " » n'est pas un embed déclaré.", undefined, line);
|
|
325
|
+
if (!/^[a-z0-9_-]{1,32}$/.test(optionName))
|
|
326
|
+
throw new errors_1.FbpError("Le nom de l'option doit contenir 1 à 32 caractères minuscules, chiffres, tirets ou underscores.", undefined, line);
|
|
327
|
+
if (!description.trim() || description.length > 100)
|
|
328
|
+
throw new errors_1.FbpError("La description de l'option doit contenir 1 à 100 caractères.", undefined, line);
|
|
329
|
+
command.userOption = { name: optionName, description };
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (name === "discordChamp") {
|
|
333
|
+
if (values.length !== 3 || values.some(value => typeof value !== "string")) {
|
|
334
|
+
throw new errors_1.FbpError("Utilisation : discordChamp(nomCommande, nomChamp, valeur).", undefined, line);
|
|
335
|
+
}
|
|
336
|
+
const [commandName, fieldName, fieldValue] = values;
|
|
337
|
+
const command = this.discordCommands.get(commandName);
|
|
338
|
+
if (!command || command.type !== "embed")
|
|
339
|
+
throw new errors_1.FbpError("La commande « " + commandName + " » n'est pas un embed déclaré.", undefined, line);
|
|
340
|
+
if (command.fields.length >= 25)
|
|
341
|
+
throw new errors_1.FbpError("Un embed Discord ne peut pas contenir plus de 25 champs.", undefined, line);
|
|
342
|
+
if (!fieldName.trim() || !fieldValue.trim() || fieldName.length > 256 || fieldValue.length > 1024)
|
|
343
|
+
throw new errors_1.FbpError("Un champ doit avoir un nom (1 à 256 caractères) et une valeur (1 à 1024 caractères).", undefined, line);
|
|
344
|
+
command.fields.push({ name: fieldName, value: fieldValue });
|
|
240
345
|
return;
|
|
241
346
|
}
|
|
242
347
|
if (name === "ping") {
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fbp-language",
|
|
3
3
|
"displayName": "FBP - French Bot Programming",
|
|
4
|
-
"description": "Langage FBP avec prise en charge des commandes slash Discord.",
|
|
5
|
-
"version": "1.1.
|
|
4
|
+
"description": "Langage FBP avec prise en charge des commandes slash et des embeds Discord.",
|
|
5
|
+
"version": "1.1.4",
|
|
6
6
|
"type": "commonjs",
|
|
7
7
|
"main": "dist/compiler.js",
|
|
8
8
|
"bin": {
|