BotClient e contextos
Assinaturas públicas completas do SDK de bots do Monky, geradas da entrada TypeScript de @monky/bot-sdk. Campos opcionais usam ?; campos readonly não devem ser alterados. Os links de origem incluem regras de validação e detalhes da implementação.
Visão geral do SDK de bots e eventos · Conexão · Comandos · Permissões · Armazenamento
BotCapability
ts
export type BotCapability = "commands" | "read_messages" | "send_messages" | "publish_voice" | "receive_voice" | "local_execution" | "sound_download" | "selectors" | "miniapps" | "live_actions" | "server_storage" | "device_storage";BotChoice
ts
export interface BotChoice {
title: string;
description?: string;
choices: SelectionChoice[];
presentation?: 'dropdown' | 'buttons';
submitLabel?: string;
}BotClient
ts
export class BotClient extends EventEmitter {
createScreen(serverId: string, input: BotScreenCreate): Promise<BotScreen>;
updateScreen(serverId: string, ref: BotScreenRef, patch: BotScreenPatch): Promise<BotScreen>;
closeScreen(serverId: string, ref: BotScreenRef): Promise<void>;
listScreens(serverId: string, channelId: string): Promise<BotScreen[]>;
storage(serverId: string): BotStorage;
localExecution(serverId: string): LocalExecutionClient;
joinVoice(serverId: string, channelId: string, options?: BotVoiceJoinOptions): Promise<BotVoiceConnection>;
getVoiceConnection(serverId: string): BotVoiceConnection | undefined;
leaveVoice(serverId: string): Promise<void>;
constructor(options: BotOptions);
getPermissions(serverId: string): BotPermissions | undefined;
command(def: CommandDefinition): this;
settings(definition: BotSettingsDefinition): this;
getServerSettings(serverId: string): BotServerSettingsSnapshot | undefined;
onSettingsChanged(listener: (snapshot: BotServerSettingsSnapshot, context: {
readonly serverId: string;
}) => void): () => void;
onSelectorResponse(listener: (event: BotSelectorResponseEvent, context: BotSelectorResponseContext) => void): () => void;
createSelector(serverId: string, input: BotSelectorCreate): Promise<BotSelector>;
createLiveAction(serverId: string, input: LiveActionCreate): Promise<LiveAction>;
updateLiveAction(serverId: string, input: LiveActionUpdate): Promise<LiveAction>;
closeLiveAction(serverId: string, id: string): Promise<void>;
listLiveActions(serverId: string): Promise<LiveAction[]>;
onLiveActionSubmission(listener: (event: LiveActionSubmission, context: {
serverId: string;
}) => void): () => void;
listSelectors(serverId: string): Promise<BotSelector[]>;
updateSelector(serverId: string, id: string, patch: BotSelectorPatch): Promise<BotSelector>;
closeSelector(serverId: string, id: string): Promise<BotSelector>;
finalizeSelector(serverId: string, id: string, content: BotPublishedMessageContent): Promise<BotSelector>;
sendMessage(serverId: string, channelId: string, content: BotPublishedMessageContent, options?: Pick<ChatSendPayload, 'replyToMessageId'>): Promise<ChatMessage>;
addReaction(serverId: string, channelId: string, messageId: string, emoji: string): void;
removeReaction(serverId: string, channelId: string, messageId: string, emoji: string): void;
onReactionAdded(listener: (event: ChatReactionEventPayload, context: {
serverId: string;
}) => void): () => void;
onReactionRemoved(listener: (event: ChatReactionEventPayload, context: {
serverId: string;
}) => void): () => void;
connect(overrides?: {
serverUrl?: string;
token?: string;
serverId?: string;
}): void;
disconnect(serverId?: string): void;
close(): Promise<void>;
get serverCount(): number;
get serverIds(): string[];
get registeredServerCount(): number;
serve(opts: ServeOptions): Promise<http.Server>;
}BotManifest
ts
export interface BotManifest {
requestedCapabilities: BotCapability[];
/** Display name of the bot. */
name: string;
/** Short description shown in the install preview. */
description?: string;
/** Base64-encoded avatar image (data URI or raw base64). */
icon?: string;
/** Commands the bot will register on connect. Informational only. */
commands?: Array<{
name: string;
description: string;
}>;
/**
* URL where the server POSTs the token after creation.
* The bot must accept `{ token, serverId, serverName }` and respond
* with `{ publicKey }`.
*/
registrationUrl: string;
}BotOptions
ts
export interface BotOptions {
/** Explicit request, not a grant. Each server administrator approves a subset. */
requestedCapabilities: BotCapability[];
serverUrl?: string;
token?: string;
/** Ed25519 public key in hex (DER/SPKI), used for TOFU binding. */
publicKey: string;
/**
* Ed25519 private key matching `publicKey` (PEM or KeyObject). With it the bot
* proves it holds the key when signing in, so a leaked token alone cannot
* impersonate it. Defaults to the identity of the bot runtime CLI. A bot that
* proved its key once on a server must keep proving it there.
*/
privateKey?: string | KeyObject;
autoReconnect?: boolean;
/** Optional profile to synchronize, including already-added bot accounts. */
name?: string;
/** Image bytes encoded as base64 or a data URI; null removes the previous photo. */
avatarBase64?: string | null;
/** Private JSON file for authenticated marketplace registrations; survives close/restart. */
registrationFile?: string;
}BotPermissions
ts
export type BotPermissions = {
revision: number;
requested: ("commands" | "read_messages" | "send_messages" | "publish_voice" | "receive_voice" | "local_execution" | "sound_download" | "selectors" | "miniapps" | "live_actions" | "server_storage" | "device_storage")[] | null;
granted: ("commands" | "read_messages" | "send_messages" | "publish_voice" | "receive_voice" | "local_execution" | "sound_download" | "selectors" | "miniapps" | "live_actions" | "server_storage" | "device_storage")[];
reviewRequired: boolean;
reviewedBy: string | null;
reviewedAt: number | null;
};BotRequestError
ts
export class BotRequestError extends Error {
constructor(message: string, code?: string);
readonly code: string;
}BotSelectorResponseContext
ts
export interface BotSelectorResponseContext {
readonly serverId: string;
readonly settings: BotSettingsContext;
}BotSelectorResponseEvent
ts
export type BotSelectorResponseEvent = {
value: string;
id: string;
channelId: string;
userId: string;
};BotStorage
ts
export interface BotStorage {
get(key: string): Promise<BotStoredObject | null>;
put(key: string, data: Uint8Array | ArrayBuffer | string, options?: BotStoragePutOptions): Promise<BotStorageObjectInfo | null>;
delete(key: string): Promise<boolean>;
list(options?: BotStorageList): Promise<BotStorageListResult>;
usage(): Promise<BotStorageUsage>;
}BotStorageLevel
ts
export type BotStorageLevel = "ok" | "near_full" | "full";BotStorageList
ts
export type BotStorageList = {
limit?: number | undefined;
prefix?: string | undefined;
after?: string | undefined;
};BotStorageListResult
ts
export type BotStorageListResult = {
objects: {
key: string;
sizeBytes: number;
contentType: string | null;
updatedAt: number;
}[];
next: string | null;
};BotStorageObjectInfo
ts
export type BotStorageObjectInfo = {
key: string;
sizeBytes: number;
contentType: string | null;
updatedAt: number;
};BotStoragePutOptions
ts
export interface BotStoragePutOptions {
contentType?: string;
/**
* For saves nobody asked for, such as an autosave: a full storage resolves `null`
* instead of rejecting, so the activity goes on. People see the warning in Monky.
*/
autosave?: boolean;
}BotStorageUsage
ts
export type BotStorageUsage = {
usedBytes: number;
maxBytes: number;
objectCount: number;
maxObjects: number;
level: "ok" | "near_full" | "full";
};BotStoredObject
ts
export interface BotStoredObject {
data: Uint8Array<ArrayBufferLike>;
key: string;
sizeBytes: number;
contentType: string | null;
updatedAt: number;
}CommandAudioPreviewContext
ts
export interface CommandAudioPreviewContext {
requestId: string;
commandName: string;
resourceId: string;
serverId: string;
locale: "pt-BR" | "en";
optionName: string;
signal: AbortSignal;
readonly settings: {
server: Record<string, string | number | boolean | string[]>;
schemaRevision: number;
serverRevision: number;
user: Record<string, string | number | boolean | string[]>;
};
channelId: string;
botId: string;
invokerId: string;
invokerSessionId: string;
invokerNickname: string;
invokerVoiceChannelId: string | null;
}CommandAudioPreviewData
ts
export interface CommandAudioPreviewData {
bytes: Uint8Array;
mimeType: CommandAudioPreviewMimeType;
}CommandAudioPreviewResponse
ts
export type CommandAudioPreviewResponse = {
operation: "youtube.preview";
localPreviewId: string;
taskId: string;
requestId: string;
executorSessionId: string;
} | CommandAudioPreviewData;CommandAutocompleteContext
ts
export interface CommandAutocompleteContext {
requestId: string;
commandName: string;
query: string;
page: number;
cursor?: string | undefined;
optionName: string;
args: CommandValues;
locale: "pt-BR" | "en";
serverId: string;
readonly settings: {
server: Record<string, string | number | boolean | string[]>;
schemaRevision: number;
serverRevision: number;
user: Record<string, string | number | boolean | string[]>;
};
signal: AbortSignal;
channelId: string;
botId: string;
invokerId: string;
invokerSessionId: string;
invokerNickname: string;
invokerVoiceChannelId: string | null;
}CommandContext
ts
export interface CommandContext {
invocationId: string;
botId: string;
commandName: string;
channelId: string;
invokerId: string;
invokerSessionId: string;
/** Voice room captured when the invocation began; use getVoiceChannel after awaits. */
invokerVoiceChannelId: string | null;
/** Revalidate this exact human session's current room with the server. */
getVoiceChannel: () => Promise<string | null>;
invokerNickname: string;
serverId: string;
locale: BotLocale;
/** Immutable preferences captured for this invocation, including subsequent prompts. */
readonly settings: BotSettingsContext;
/** Named, typed values: integer and boolean options are not strings. */
args: CommandValues;
/** Aborted on cancellation, timeout, disconnection or completion. */
signal: AbortSignal;
/** Private response in the caller's chat. */
reply: (content: BotMessageContent) => void;
replyEphemeral: (content: BotMessageContent) => void;
/** Explicitly publish a result to everyone allowed into the channel. */
publish: (content: BotPublishedMessageContent) => void;
/** Wait for a private form. Returns null if the interaction ends/cancels. */
prompt: (form: BotForm) => Promise<BotFormValues | null>;
/** Ask one private choice; buttons submit immediately, dropdowns require confirmation. */
choose: (choice: BotChoice) => Promise<string | null>;
/** Request one caller-authorized local soundboard download; never downloads on the bot. */
downloadSound: (request: SoundDownloadRequest) => Promise<SoundDownloadResult | null>;
/** Publish durable channel controls, independent of this invocation's lifetime. */
createSelector: (input: Omit<BotSelectorCreate, 'channelId' | 'invokerId' | 'invocationId'>) => Promise<BotSelector>;
createLiveAction: (input: Omit<LiveActionCreate, 'channelId' | 'invocationId'>) => Promise<LiveAction>;
/** Open a shared miniapp in the caller's current voice room, independent of this command's lifetime. */
createScreen: (input: Omit<BotScreenCreate, 'channelId' | 'invocationId'>) => Promise<BotScreen>;
}CommandDefinition
ts
export interface CommandDefinition {
name: string;
description: string;
localizations?: CommandLocalizations;
options?: CommandOption[];
downloadsSound?: boolean;
voiceRequirement?: CommandVoiceRequirement;
localCapabilities?: LocalCapabilityId[];
autocomplete?: (ctx: CommandAutocompleteContext) => CommandAutocompleteChoice[] | CommandAutocompletePage | Promise<CommandAutocompleteChoice[] | CommandAutocompletePage>;
audioPreview?: (ctx: CommandAudioPreviewContext) => CommandAudioPreviewResponse | Promise<CommandAudioPreviewResponse>;
handler: (ctx: CommandContext) => void | Promise<void>;
}ServeOptions
ts
export interface ServeOptions {
name: string;
description?: string;
/** Base64 image or data URI. Remote image URLs are not supported. */
icon?: string;
port?: number;
host?: string;
/** Public hostname/IP, without a scheme or port. */
publicHost?: string;
/**
* Public origin announced in the manifest instead of `publicHost` and
* `port`, such as `https://bot.example.com` behind a TLS reverse proxy.
* Monky servers post the token to `<publicUrl>/register`, so HTTPS keeps it
* encrypted on the way. Only an origin: no path, query or fragment.
*/
publicUrl?: string;
}