Nubis, un framework open source d’Eytak
Nubis est un framework TypeScript pour les bots Discord. Écris un fichier et tu as une commande slash, typée d’après ce que tu as écrit, avec des tests qui tournent sans Discord.
$ npx create-nubis my-botimport { command, hasPermission, option } from "nubis";
import { PermissionFlagsBits } from "discord-api-types/v10";
import { confirmBan } from "../../components/confirm-ban";
export default command({
description: "Ban a member",
ephemeral: true,
options: {
user: option.user({ required: true }),
days: option.integer({
choices: [
{ name: "One day", value: 1 },
{ name: "A week", value: 7 },
],
}),
},
guards: [hasPermission(PermissionFlagsBits.BanMembers)],
run: ({ options, reply }) =>
reply({
content: `Ban ${options.user.username}?`,
components: [confirmBan({ userId: options.user.id })],
}),
});
adaa utilisé /mod ban
my-botApp
Ban alice?
Toi seul peux voir ceci
commands/mod/ban.ts/mod ban
Choisis un fichier. Le chemin est la commande.Types
Les valeurs des options, les unions de choix, l’état des composants et les champs des modales sont déduits de leurs définitions. Rien ne se cherche par une chaîne.
days: option.integer({
choices: [
{ name: "One day", value: 1 },
{ name: "A week", value: 7 },
],
}),déduit
options.days: 1 | 7 | undefined
export const confirmBan = button({
id: "confirm-ban",
label: "Confirm",
state: z.object({ userId: z.string() }),
onClick: ({ state }) => …déduit
state.userId: string
fields: {
reason: textInput({
label: "Reason",
required: true,
}),
contact: textInput({ label: "Contact" }),
},déduit
values.contact: string | undefined
Les règles de Discord
Des noms de commande de 32 caractères au plus, 25 options et 25 choix au maximum, des custom_id de 100 caractères, 5 rangées de composants. Une définition que Discord refuserait échoue dès son chargement, et l’erreur dit où.
days: option.integer({
choices: [
{ name: "One day", value: 1 },
{ name: "A week and a half", value: 7.5 },
],
}),$ nubis dev
[nubis] Synced commands:
+ ping
+ mod
[nubis] commands/mod/ban.ts changed, restarting.
[nubis] /mod ban option "days": integer choices must be whole numbers.
[nubis] Fix the error and save; I'll try again.
Tests
nubis/testing exécute les commandes, boutons, menus de sélection, modales et menus comme Discord le ferait. Pas de token, pas de réseau, pas de serveur de test.
import { PermissionFlagsBits } from "discord-api-types/v10";
import { fake, testCommand } from "nubis/testing";
import ban from "../commands/mod/ban";
test("asks for confirmation", async () => {
const result = await testCommand(ban)
.as({ permissions: PermissionFlagsBits.BanMembers })
.run({ user: fake.user("alice") });
expect(result.replies).toEqual(["Ban alice?"]);
expect(result.ephemeral).toBe(true);
});
test("members without the permission are turned away", async () => {
const result = await testCommand(ban).run({ user: fake.user("alice") });
expect(result.status).toBe("denied");
});$ bun test
tests/ban.test.ts:
✓ asks for confirmation
✓ members without the permission are turned away
2 pass
0 fail
Réponses
Discord laisse 3 secondes à un bot pour répondre. Appelle reply() et Nubis s’occupe du reste : le message d’attente, la modification et les messages de suivi.
0 s
Ton handler commence à préparer le rapport.
2 s
Après 2 secondes sans réponse, avant la fin du délai de 3 secondes de Discord.
Quand c’est prêt
La première réponse remplace « réfléchit… ».
Dans les 15 min
Les réponses suivantes sont de nouveaux messages, dans l’ordre où tu les envoies.
Limite de Discord · 3 s