Skip to main content

Module bot_interface

Module bot_interface 

Source
Expand description

Bot Interface — manifests + slash commands (Phase 1 of the bot-UI layer).

Transport-agnostic by design: everything here is CONTENT-level (structured tags on ordinary chat rumors, plus one plain replaceable discovery event), so the same commands work in NIP-17 DMs and Concord v1/v2 channels — the envelope is whatever the conversation already uses.

Two pieces:

  1. Manifest (BotManifest, kind KIND_BOT_MANIFEST): a replaceable event signed by the bot’s key describing every command with typed args. Clients fetch it by pubkey to render a / picker with argument hints and validate input before anything hits the wire.
  2. Invocation (parse_command_text): a command is a NORMAL chat message whose content IS the invocation (“/price btc”) — no extra structure on the wire. The manifest’s ordering rules (required args first, a greedy String only in trailing position) make the text deterministically parseable, so the bot recovers exact typed arguments from content alone, and EVERY existing client is already a fully capable command sender.

The kind number is provisional pending upstream registry coordination.

Structs§

ArgSpec
One declared argument of a command.
BotManifest
The bot’s full published interface. Unknown fields are ignored on read (forward compatibility); v gates breaking schema changes.
ChatBotCommands
One bot’s published commands, shaped for a client’s / picker. The client resolves the bot’s display name/avatar from its own profile cache.
ChatCommandsSnapshot
What the composer’s / picker renders, instantly answerable from local state. fresh: false means a background refetch was spawned — a chat_commands_updated event follows with the converged list.
CommandSpec
One command a bot answers.
ParsedCommand
A command invocation recovered from a message’s content. Values are raw strings in manifest order; type them via typed_args.

Enums§

ArgType
The typed shape of one command argument.
ArgValue
One argument value, typed per its spec.

Constants§

DISCOVERY_RELAYS
Public relays that index replaceable events network-wide — the reliable discovery path for bot manifests. Read side: always queried ALONGSIDE a chat’s own relays, so a manifest resolves even when a community relay is unreachable or drops stranger events (Ditto does). Write side: the SDK publishes every bot’s manifest here for the same reason.
KIND_BOT_MANIFEST
Replaceable bot-interface manifest (outside any wrap): one authoritative command catalog per bot pubkey, the same shape as a profile or relay list.
MAX_ARGS
MAX_ARG_VALUE_LEN
A single argument value on the wire (tag or text) is clamped before typing.
MAX_BOT_TAGS
Recipient tags honored per message (routing metadata must stay cheap).
MAX_CHOICES
MAX_COMMANDS
Bounds (validated on BOTH build and parse — a foreign manifest is untrusted input and must never cost unbounded memory or render work).
MAX_DESCRIPTION_LEN
MAX_MANIFEST_BYTES
MAX_NAME_LEN
TAG_BOT
Optional recipient tag a picker client attaches to an invocation: ["bot", <bot pubkey hex>]. Addressing is the ONE piece of a command not derivable from content — two bots can share a command name — so it rides a tag while the invocation stays plain text. Semantics: tagged → only the named bot(s) execute (others skip even on a manifest match); untagged → broadcast, any matching bot may answer (the legacy-client path — bots never REQUIRE the tag). Deliberately NOT p: chat rumors already carry p for DM recipients and reply parents, and a skip-unless-me rule keyed on p would silently swallow a command sent as a reply to a human. The tag is routing, not authority — bots authorize by SENDER, never by tag.

Functions§

addressed_bots
Extract the addressed bots from a rumor’s tags as npubs (deduped, capped, invalid values skipped). Empty = untagged/broadcast.
assemble_from_store
Assemble picker entries from the persistent manifest store for a set of bot pubkeys (hex, pre-sorted order preserved). Bots with no stored manifest are absent; a stored row that no longer parses/validates is skipped.
bot_tag
Build the recipient tag a picker attaches: ["bot", <hex>].
command_text
The canonical content for an invocation a picker client builds — exactly what a human would have typed (“/name value…”). Values containing spaces or quotes are quoted with \" escapes so the text re-parses to the same arguments.
commands_fresh
true while the last completed refresh for this chat is within TTL AND covered exactly bot_hexes.
fetch_manifest
Fetch + validate a bot’s manifest by pubkey from the given relays. Returns the newest valid one, or None when the bot has published no interface.
fetch_manifests
Batch-fetch validated manifests for a set of authors over specific relays — the picker’s ONE REQ (all bots of a room in a single query). Transport- generic so community relays are queried directly and tests run offline. Per author: the NEWEST event wins, then must validate (a bot that breaks its own manifest has no usable interface — parity with fetch_manifest); authors the relay volunteers beyond the asked set are dropped, as is anything failing signature verification. Returns each manifest with its event timestamp (the store’s newest-wins key).
parse_command_text
Parse “/name arg arg…” content against the manifest’s arg order — THE invocation parse. Returns None when the text isn’t a known command (it is then ordinary chat).
publish_manifest
Publish manifest as the signed replaceable event over the given relays (targeted send — the caller decides the reach: login relays, communities, indexers). Returns how many relays accepted it.
spawn_commands_refresh
Background half of the picker flow: ONE REQ for every bot’s manifest (5s unification window), persist newer editions, mark the chat fresh, and tell the UI to swap in the converged list. Deduped per chat; session-guarded before every write.
typed_args
Type-check a parsed invocation against the manifest: every provided value parses as its declared type, choices are members, required args are present. Unknown arg names are DROPPED (a newer client may know newer args; the bot’s manifest is authoritative for what it consumes).