Gryt
Build on Gryt

Bot API

Everything @gryt/bot exports, generated from the source

Generated from the source and regenerated by CI, so it cannot fall behind the code. Editing this page by hand gets the edit wiped on the next run — change what the generator reads instead.

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): boolean

False 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 = {}): this

Register 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

FieldTypeWhat 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

FieldTypeWhat it is
hoststringchat.example.com, or localhost:5001. Scheme optional.
nickname?stringWhat the member list will call it.
identityPath?stringWhere the key lives. The file is the bot — see identity.ts.
identity?BotIdentityAn 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?stringOne line, shown beside the ask. Say what the bot is for.
botToken?stringA 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?stringThe command prefix. Set to "" to turn the router off entirely.
helpCommand?booleanWhether to answer help with a listing. On by default.
secure?booleanForce 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 } | null

splitArgs

splitArgs(rest: string): string[]

CommandContext

FieldTypeWhat it is
messageMessageThe message that invoked it.
reststringEverything after the command word, trimmed. Empty string, never null.
argsstring[]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

FieldTypeWhat it is
description?stringOne 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

FieldTypeWhat it is
namestring
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

FieldTypeWhat it is
subjectstringThe 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

FieldTypeWhat it is
file_idstring
mimestring | null
sizenumber | null
original_namestring | null
widthnumber | null
heightnumber | null
has_thumbnailboolean

Channel

FieldTypeWhat it is
idstring
namestring
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.

FieldTypeWhat it is
error?string
code?string
message?string
permission?stringNamed when the refusal was a permission.

Member

FieldTypeWhat it is
serverUserIdstring
nicknamestring
rolestring
status"online" | "in_voice" | "afk" | "offline"
avatarFileIdstring | null

Message

FieldTypeWhat it is
conversationIdstring
messageIdstring
senderServerUserIdstring
senderNicknamestring | null
textstring | null
attachmentsAttachment[]
createdAtDate
editedAtDate | null
replyToMessageIdstring | null
isSystembooleanTrue for the "X joined the server" kind of message, which has no sender.
isOwnbooleanTrue 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

FieldTypeWhat it is
serverIdstring
namestring
descriptionstring
permissionsPermission[]What this bot may do here. Empty until the server has said.
rolestringThe role this bot holds.

On this page