API reference
Everything @gryt/bot exports, generated from the source
This is the reference. Writing a bot is the page that teaches the shape: the handshake, waiting to be approved, and what a bot does when an admin says no.
Everything below is exported from @gryt/bot. Nothing else in the package is
public.
import { GrytBot, parseCommand, splitArgs, createIdentity, loadIdentity, PERMISSIONS_BEFORE_CATALOGUE } from "@gryt/bot";GrytBot
A bot on one Gryt server.
It joins like any other client and is a member like any other: no bot account type, no bot token, no bypass. What it may do is what its role says.
So the interesting failure is a permission failure. can() answers from what
the server said, and sending without the permission throws here with the
permission's name rather than surfacing as a server:error later.
const bot = new GrytBot({ host: "localhost:5001", nickname: "Helper" });
bot.command("ping", async (ctx) => ctx.reply("pong"), {
description: "Check I am alive",
requires: ["send_messages"],
});
await bot.start();can()
can(permission: Permission): booleanFalse before ready: a bot acting on what it assumes it may do finds out
over the wire. True for a permission the server's catalogue does not have —
that is a server older than the permission, not a refusal.
command()
command(name: string, handler: (ctx: CommandContext) => void | Promise<void>, options: CommandOptions = {}): thisRegister a command. Later registrations of the same name replace earlier.
start()
start(): Promise<ServerInfo>Connect, join, and resolve once the server has said who this bot is.
On a first run against a server that has not approved it, this does not
resolve — the bot is waiting at the door, which is a state and not a
failure. Listen for waiting, and for ready when somebody answers.
stop()
stop(): Promise<void>Leave, and stop reconnecting.
send()
send(conversationId: string, text: string): Promise<void>Post in a channel.
Throws before sending anything if the bot lacks send_messages, naming
the permission. The server would refuse it anyway; failing here means the
stack trace points at the call rather than at a socket handler.
react()
react(conversationId: string, messageId: string, reactionSrc: string): Promise<void>delete()
delete(conversationId: string, messageId: string, opts: { own?: boolean } = {}): Promise<void>Delete a message.
Its own is delete_own_messages; anybody else's is manage_messages. The
caller says which it is, because the bot does not necessarily know who sent
a message it was handed.
fetch()
fetch(conversationId: string, limit = 50): Promise<void>Ask for the last N messages in a channel. Answered on the message event.
GrytBotEvents
| Field | Type | What it is |
|---|---|---|
ready | [ServerInfo] | |
message | [Message] | |
members | [Member[]] | |
channels | [Channel[]] | |
error | [Error] | Something the server refused. Always worth logging; often a permission. |
waiting | [string] | Turned away pending approval. Not an error — see the note in wire. |
disconnected | [string] |
GrytBotOptions
| Field | Type | What it is |
|---|---|---|
host | string | chat.example.com, or localhost:5001. Scheme optional. |
nickname? | string | What the member list will call it. |
identityPath? | string | Where the key lives. The file is the bot — see identity.ts. |
identity? | BotIdentity | An already-loaded identity, if you keep the key somewhere of your own. |
wants? | string[] | Sent once, the first time it turns up, and shown to whoever approves it. Ask for the least that works. Fixed from then on. A later run declaring more gets the answer to the question the first one asked — the run asking for more may not be yours. |
description? | string | One line, shown beside the ask. Say what the bot is for. |
botToken? | string | A token from a registration an operator set up in advance. The unattended path: no approval to wait for, because the approving already happened. Single-use — the first bot to present it becomes that registration. |
prefix? | string | The command prefix. Set to "" to turn the router off entirely. |
helpCommand? | boolean | Whether to answer help with a listing. On by default. |
secure? | boolean | Force http/https rather than guessing from the host. |
parseCommand
Which command a message invokes, if any.
Case-insensitive on the command word, because somebody will type !Help and
being right about it helps nobody.
parseCommand(text: string, prefix: string): { name: string; rest: string } | nullsplitArgs
splitArgs(rest: string): string[]CommandContext
| Field | Type | What it is |
|---|---|---|
message | Message | The message that invoked it. |
rest | string | Everything after the command word, trimmed. Empty string, never null. |
args | string[] | rest split on whitespace, with empties dropped. |
reply | (text: string): Promise<void> | Reply in the channel the command came from. |
react | (emoji: string): Promise<void> | React to the message that invoked it. |
CommandOptions
| Field | Type | What it is |
|---|---|---|
description? | string | One line, for the help listing. |
requires? | Permission[] | Permissions the bot needs before this command is worth offering. Checked before the handler runs, so a command that replies is skipped outright on a server where the bot may not post — rather than running, failing at the socket, and leaving whoever typed it watching nothing happen. This is about the bot, not about the person who typed it: what they may do is the server's business and it is already enforcing it. |
RegisteredCommand
| Field | Type | What it is |
|---|---|---|
name | string | |
handler | (ctx: CommandContext) => void | Promise<void> |
createIdentity
Mint a new identity. The bot this belongs to has never been seen anywhere.
createIdentity(): Promise<{ identity: BotIdentity; privateJwk: JWK; }>loadIdentity
The second run must be the same bot: same id, same role, same history. A key generated per start arrives as a new member every time.
Written 0o600 — anyone who can read this file can be this bot.
loadIdentity(path: string): Promise<BotIdentity>PERMISSIONS_BEFORE_CATALOGUE
Stands in for the catalogue on a server too old to publish one, so an absent permission is not read as a refusal.
Frozen. It describes a release that has already happened, and never grows.
PERMISSIONS_BEFORE_CATALOGUE: readonly Permission[] = [ "send_messages", "attach_files", "add_reactions", "join_voice", "speak", "share_video", "share_screen", "change_nickname", "change_avatar", "create_invite", "manage_invites", "manage_messages", "kick_members", "ban_members", "mute_members", "manage_reports", "manage_join_requests", "manage_channels", "manage_emojis", "manage_webhooks", "manage_roles", "manage_server", "view_audit_log", ]BotIdentity
| Field | Type | What it is |
|---|---|---|
subject | string | The id this bot will be known by on every server. Derived from the key. |
certificate | (): Promise<string> | A certificate that vouches for itself, valid for an hour. |
assertion | (audience: string, nonce: string): Promise<string> | Proof, bound to one server's challenge, that this bot holds the key. |
Attachment
| Field | Type | What it is |
|---|---|---|
file_id | string | |
mime | string | null | |
size | number | null | |
original_name | string | null | |
width | number | null | |
height | number | null | |
has_thumbnail | boolean |
Channel
| Field | Type | What it is |
|---|---|---|
id | string | |
name | string | |
type | "text" | "voice" |
ServerRefusal
Why the server refused something.
The machine-readable half is error, which is what the server actually
sends; code is accepted too because it is the name half of this SDK reached
for first and getting it wrong turned "waiting to be approved" into a crash.
| Field | Type | What it is |
|---|---|---|
error? | string | |
code? | string | |
message? | string | |
permission? | string | Named when the refusal was a permission. |
Member
| Field | Type | What it is |
|---|---|---|
serverUserId | string | |
nickname | string | |
role | string | |
status | "online" | "in_voice" | "afk" | "offline" | |
avatarFileId | string | null |
Message
| Field | Type | What it is |
|---|---|---|
conversationId | string | |
messageId | string | |
senderServerUserId | string | |
senderNickname | string | null | |
text | string | null | |
attachments | Attachment[] | |
createdAt | Date | |
editedAt | Date | null | |
replyToMessageId | string | null | |
isSystem | boolean | True for the "X joined the server" kind of message, which has no sender. |
isOwn | boolean | True when this bot sent it. Handlers are not called for these. |
Permission
A permission id. The server owns the list; see GrytBot.permissions.
export type Permission = string;ServerInfo
| Field | Type | What it is |
|---|---|---|
serverId | string | |
name | string | |
description | string | |
permissions | Permission[] | What this bot may do here. Empty until the server has said. |
role | string | The role this bot holds. |