twocents/open source

Messaging and slash commands

Send a Discord message or connect a slash command to a local program.

Send a message

The CLI can send text and rich embeds to a channel accessible to the bot, independently of the recording and message-history allowlists. Discord enforces channel access and Send Messages / Embed Links permissions. Mentions are disabled. Structured input is read from a file or stdin; dry-run validates locally without contacting Discord.

Example request for guildscribe messages send --request message.json:

{
  "channelID": "123456789012345678",
  "message": {
    "content": "Meeting notes",
    "embeds": [
      { "title": "Summary", "description": "Notes are ready", "color": 5793266 }
    ]
  }
}

Configure slash commands

Configure slashCommands through config update --request, stored as JSON in GUILDSCRIBE_SLASH_COMMANDS inside the owner-only .env. Each entry has a name, description, guildID, absolute executable path, nonempty allowedUserIDs, optional channelIDs, optional ephemeral (default true), and options (default empty). Options have name, description, type, and optional required. Types are string, integer, number, boolean, user, channel, and role. Names use lowercase ASCII letters, digits, underscores, and hyphens. Example configuration update:

{
  "slashCommands": [
    {
      "name": "status",
      "description": "Check our service",
      "guildID": "123456789012345678",
      "executable": "/absolute/path/to/status-hook",
      "allowedUserIDs": ["234567890123456789"],
      "options": [
        {
          "name": "service",
          "description": "Service name",
          "type": "string",
          "required": true
        }
      ]
    }
  ]
}

Run guildscribe commands sync --dry-run, then guildscribe commands sync to create or update the configured guild slash commands. This does not remove unrelated or previously registered commands. Removed configuration entries cannot execute hooks; remove their stale Discord registrations in Discord's application management. Commands default to disabled for ordinary members; administrators must grant access in Discord's integration settings as well as configuring Guildscribe's runtime user allowlist. Restart the daemon after configuration changes. Registration does not happen implicitly on daemon startup.

The daemon accepts only chat-input slash interactions matching configured guilds and commands. It checks the user and optional channel restrictions, acknowledges before executing, and allows only one action hook at a time. Hook timeout is the configured hookTimeoutMs capped at two minutes. No automatic hook retries occur. Lifecycle and transcription hooks run separately.

Write a slash-command hook

The executable runs directly without shell arguments, in the runtime home, with a minimal environment excluding Discord credentials. Stdin contains one JSON object: version 1, event command.invoked, interactionID, commandName, guildID, channelID, userID, and an options object containing typed primitive values or null for omitted options. User/channel/role values are Discord IDs. Interaction tokens are excluded. Stdout must be one JSON message object containing content and/or embeds, identical to the send request's message field. Output is limited to 64 KB. Stderr is drained and not published. Errors produce a generic response; timed-out actions may have already taken effect and must be checked before retrying. No action is triggered from messages, buttons, context menus, or unconfigured command names.

Inspect capabilities

guildscribe capabilities checks local hook executability and reports supported operations, configured command restrictions, and unknown remote registration state. Add --live for read-only registration checks; add --channel <ID> to inspect that channel's permissions. Text and announcement channel checks combine guild roles, everyone/role/member overwrites, administrator bypass, and communication timeout. Thread and other channel types remain explicitly unknown. Registration status is matching, different, missing, or unknown; executable and registration checks do not prove the daemon is online or Discord's integration permissions admit a given user. No capability check executes a hook, sends a message, or changes registration.

Handle timeouts and partial results

The timeout for a message, sync, or capability command applies to all its requests. When it expires, the command aborts requests in progress and sends no further sync requests. There are no automatic HTTP retries. Aborting cannot undo an effect already accepted by Discord. HTTP failures expose status and Retry-After, and failures distinguish timeouts from transport errors without echoing provider response bodies or credentials. Mutations conservatively report unsafe retry behavior and outcomeUnknown. Sync transport failures include completedCommands when available; the outer CLI deadline can instead report a whole-command unknown outcome. A failed sync can have already registered earlier commands.

On this page