Commands with the Plumbing Done
The router converts arguments, runs guards and cooldowns and builds the help from your definitions

Each Key Is a Command
A prefix command is a message that starts with a prefix, such as !ping. Pass your commands to runBot, and its command router reads the name after the prefix and runs the matching execute
If a reply fails, the router reports it with the command's name, and every other command keeps working
import { runBot } from "@neontechspace/fluxerly"await runBot({ token: process.env.FLUXER_BOT_TOKEN, commands: { prefix: "!", commands: { ping: { description: "Check that the bot can reply", execute: ({ reply }) => reply("Pong!"), }, about: { description: "Describe this bot", execute: ({ reply }) => reply("Built with Fluxerly"), }, }, },})Arguments Arrive Typed
Describe each argument once, and the router converts the words after the name before execute runs. With parseQuoted a quoted name counts as one argument, and a duration such as 10m arrives as milliseconds
A missing or invalid argument never reaches your code. The bot answers with the reason and the command's usage instead
import { commands, runBot } from "@neontechspace/fluxerly"await runBot({ token: process.env.FLUXER_BOT_TOKEN, commands: { prefix: "!", parse: commands.parseQuoted, commands: { greet: { description: "Greet a name", arguments: { name: { type: "text" } }, execute: ({ values, reply }) => reply(`Hello, ${values.name}!`), }, remind: { description: "Repeat a note after a delay", arguments: { delay: { type: "duration", min: 1_000, max: 3_600_000 }, note: { type: "text", rest: true }, }, execute: ({ client, message, values, reply }) => { client.schedule((signal) => client.messages.reply(message, values.note, { signal }), { delayMs: values.delay, }) return reply("Reminder set") }, }, }, },})Guards and Cooldowns
A guard decides whether a matched command may run, and a cooldown limits how often it can. Here !purge runs only in a community, only for members with Manage messages, and at most once every 30 seconds in each channel
Middleware wraps every command, here to time it. A guard's answer does not replace Fluxer's own check of each request the command makes

import { guards, runBot } from "@neontechspace/fluxerly"await runBot({ token: process.env.FLUXER_BOT_TOKEN, commands: { prefix: "!", mentionPrefix: true, use: [ async (context, next) => { const started = Date.now() await next() console.info(`${context.name} took ${Date.now() - started} ms`) }, ], commands: { purge: { description: "Delete recent messages", guard: [guards.guildOnly(), guards.requirePermissions(["ManageMessages"])], cooldown: { durationMs: 30_000, per: "channel" }, arguments: { count: { type: "integer", min: 1, max: 100, default: 10 } }, execute: ({ values, reply }) => reply(`Would delete ${values.count} messages`), }, }, },})
Groups for a Larger Bot
A group gives related commands a shared first word, such as tools in !tools ping, and can have a short alias like !t ping
Groups organize names and help only. A command that needs protection still carries its own guard
import { commands, type Client } from "@neontechspace/fluxerly"export function commandGroupExample(client: Client) { return commands .create({ prefix: "!" }) .registerGroup({ name: "tools", aliases: ["t"] }) .register({ name: "ping", execute: ({ reply }) => reply("Pong") }, { group: ["tools"] }) .attach(client) }Help and Pages from the Same Definitions
Every command receives sendHelp, which lists the commands with their descriptions, and paginate, which shows any list as pages that the command's author flips with ◀ and ▶ reactions
Help is built from the definitions the router already has, so it matches the commands
import { runBot } from "@neontechspace/fluxerly"await runBot({ token: process.env.FLUXER_BOT_TOKEN, commands: { prefix: "!", commands: { help: { description: "List the commands", execute: ({ sendHelp }) => sendHelp(), }, rules: { description: "Show the rules, five at a time", execute: ({ paginate }) => paginate(["Rules 1 to 5", "Rules 6 to 10"]), }, }, },})









