Aero developer docs
Commands and interactions
Register slash commands, handle prefix commands, and answer every interaction the way members expect.
Slash commands
Register a slash command in the portal with a name, a description, and an optional required permission and cooldown. When a member runs /yourcommand in a Space, Ballastra runs the full permission chain and sends you an aero:interaction:
socket.on('aero:interaction', async (i) => {
if (i.type !== 'command') return;
// i = {
// interaction_id, // dedupe on this
// type: 'command',
// command, // the command name, e.g. 'welcome'
// options, // { ...args } (object; may be empty)
// nexus_id, space_id, installation_id,
// invoker, // { id, name } — who ran it
// }
if (i.command === 'welcome') {
// Respond by acting through the runtime API:
await api(`/aero/rt/nexus/${i.nexus_id}/spaces/${i.space_id}/messages`, {
method: 'POST',
body: { text: `Hi ${i.invoker.name}!` },
});
}
});You respond by taking an action. There is no separate reply channel: posting a message is the reply.
Prefix commands
Prefer classic prefix commands like !ban? Subscribe to message.created, grant the Aero Read messages, and the SDK does the parsing for you:
aero.prefix('!');
aero.prefixCommand('ping', (ctx) => ctx.reply('pong'));
aero.prefixCommand('ban', async (ctx) => {
// ctx.args = ['@user', 'reason', ...]
await ctx.reply(`Banning ${ctx.args[0]}`);
});Answering an interaction
A button press or a slash command gives you an interaction_id. There are three ways to answer it, and the one you pick is what the member sees:
// 1. Ephemeral — only the presser sees it, nothing is stored.
await api(`/aero/rt/nexus/${nexusId}/interactions/respond`, {
method: 'POST',
body: { interaction_id: i.interaction_id, mode: 'ephemeral', text: 'Only you can read this.' },
});
// 2. Update — rewrite the message that carried the button, for everyone.
// Use this to disable a button after it is pressed.
await api(`/aero/rt/nexus/${nexusId}/interactions/respond`, {
method: 'POST',
body: {
interaction_id: i.interaction_id,
mode: 'update',
text: 'Signed up. See you Friday.',
components: [],
},
});
// 3. A normal message, if the answer belongs in the space for everyone.
await api(`/aero/rt/nexus/${nexusId}/spaces/${i.space_id}/messages`, {
method: 'POST',
body: { text: `${i.member.name} signed up.` },
});Answer within a few seconds. An interaction the member pressed and never saw answered reads as a broken Aero, so reply first and do the slow work after.