A Working Bot in One File

From an empty folder to a bot that answers, in one file that already reconnects and stops cleanly

A handheld radio sending signal arcs from its antenna

Create the Bot, Keep Its Token Private

A Fluxer bot is an application with its own bot account. Create one from your account settings in the Fluxer app, then copy the bot token it shows, the secret the bot signs in with. Fluxer shows it only right after you create the application or regenerate the token

Anyone holding the token can act as the bot. It goes in a file named .env that version control ignores, never in the code

Create a Bot in the Docs
  1. User settings
  2. Developer
  3. Applications
  4. Create application
.env
FLUXER_BOT_TOKEN=paste-the-token-here
.gitignore
.env

One File That Answers

This is the whole bot. It reads its token from the environment, and runBot connects and calls the handler for each new message from a person. Install the SDK and invite the bot to a community where it can view the channel and send messages. Start it with the command under the code and send !ping. It answers Pong!

The same call runs large bots. It reconnects on its own, stops cleanly on Ctrl+C and explains a failed start with a suggested fix. Returning the reply hands its outcome to the SDK, so a failed send shows up in the log

Start a Bot in the Docs
bot.js
import { runBot } from "@neontechspace/fluxerly"// A failure that stops the bot, such as a rejected token, is logged and sets a failing exit codeawait runBot({    token: process.env.FLUXER_BOT_TOKEN,    events: {        messageCreate: ({ message, reply }) => {            if (message.content !== "!ping") return            // Returning the reply reports a failed send to the log            return reply("Pong!")        },    },})
node --env-file=.env bot.js

Or Generate It, Test Included

In a new folder, fluxerly init asks for JavaScript, TypeScript or the optional Effect API and writes the same bot, shaped so a test can import it, with its .env and .gitignore, and a first test. Then it installs the dependencies and prints the steps that remain

The test runs the bot against an in-memory Fluxer, so it needs no token and no network

Test a Bot in the Docs
npx @neontechspace/fluxerly init
bot.test.js
import assert from "node:assert/strict"import { test } from "node:test"import { createTestBot } from "@neontechspace/fluxerly/testing"import { bot } from "./bot.js"// createTestBot runs the bot against an in-memory Fluxer, so no token or network is neededtest("the bot answers !ping with Pong! and ignores other messages", async () => {    await using testBot = createTestBot(bot)    await testBot.ready()    // The say method sends a message as a user and returns the messages the bot sent in response    assert.deepEqual((await testBot.say("!ping")).map((message) => message.content), ["Pong!"])    assert.deepEqual(await testBot.say("hello"), [])})

Start and Stop, Already Handled

runBot checks every option before anything connects, registers the handlers, then connects. While it runs, it reconnects after a lost connection, and a handler that fails is logged while the bot keeps going

On Ctrl+C it stops taking new events, gives running handlers time to finish, closes the connection and waits for cleanup. Startup work and its timers can stop with the bot

Bot Lifetime in the Docs
A power switch with a clean off position
bot.js
import { runBot } from "@neontechspace/fluxerly"await runBot({    token: process.env.FLUXER_BOT_TOKEN,    // On Ctrl+C or SIGTERM, running handlers get up to 10 seconds to finish    drainMs: 10_000,    // Runs before the bot connects. Its signal aborts once the bot begins stopping    setup: (client, { signal }) => {        const timer = setInterval(() => console.info("Still running"), 60_000)        signal.addEventListener("abort", () => clearInterval(timer))    },    events: {        messageCreate: ({ message, reply }) => {            if (message.content !== "!ping") return            return reply("Pong!")        },    },})// The bot has stopped and the SDK has cleaned up. Close what the application opened here