Everyday helpers
Small utilities for the tasks every bot copies from Stack Overflow: invite links, autocomplete lists, mention parsing, and long replies.
Choices and autocomplete
import { choices, command, filterChoices, option } from "spearkit";
const fruit = ["apple", "apricot", "banana"];
command({
name: "pick",
description: "Pick a fruit",
options: {
fruit: option.string({
description: "Fruit",
required: true,
autocomplete: (ctx) => ctx.suggest(fruit), // or return filterChoices(fruit, ctx.value)
}),
difficulty: option.string({
description: "How hard",
choices: choices({ Easy: "easy", Hard: "hard" }),
}),
},
run: (ctx) => ctx.reply(ctx.options.fruit),
});choices("red", "blue") uses the same string for Discord's name and value.
filterChoices / ctx.suggest cap at 25 (Discord's autocomplete limit).
Long replies, DMs, typing, progress
await ctx.sendLong(hugeLog); // 2000-char chunks, follow-ups after the first
const dm = await ctx.dm("Secret"); // null if the user has DMs closed
await ctx.withTyping(async () => {
await doSlowWork();
});
const progress = await ctx.progress("Working…");
await progress.update("Almost…");
await progress.done("Done.");Prefix handlers get sendLong, dm, and withTyping as well.
Invite URL
import { inviteUrl } from "spearkit";
inviteUrl({ clientId: "123", permissions: ["BanMembers"] });
// after ready:
client.inviteUrl({ permissions: ["BanMembers"] });Default scopes are bot and applications.commands.
Mentions
parseUserId("<@123>"); // "123"
parseRoleId("<@&123>");
parseChannelId("<#123>");
parseCustomEmoji("<a:wave:123>"); // { animated, name, id }
slashMention("play", "cmdId", "song"); // </play song:cmdId>Park a command
command({ enabled: false, ... }) (also prefix / hybrid / context menus) stays
in source but is skipped by client.register and therefore not deployed.
Bot owners
const client = new SpearClient({ owners: ["your-user-id"] });
command({
name: "shutdown",
description: "Stop the process",
guards: [requireBotOwner()],
run: (ctx) => ctx.reply("Bye."),
});requireBotOwner also allows the Discord application owner. requireOwner(["id"])
still takes an explicit list.
Runtime internationalization
Discord localizes command names separately through nameLocalizations and descriptionLocalizations. Runtime replies use the invoking user's interaction.locale; prefix messages fall…
Plugins
A plugin is a named, reusable bundle of commands, events and components. It lets you package a feature once and install it into any SpearClient with a single call — useful for…