@botmesh/core - v2.0.0-alpha.2
    Preparing search index...

    Class ChaosProvider<P>

    An IProvider that wraps another one and delivers its traffic adversarially (see the module docs and ChaosOptions). Pass it to new Bot({ provider }) and keep driving the wrapped provider (chaos.inner.incoming(...)).

    Optional provider capabilities (typing, markRead, downloadMedia, …) are exposed only when the wrapped provider has them, so feature detection in the Bot keeps working.

    Type Parameters

    Implements

    Index
    inner: P

    The wrapped provider (drive it to simulate inbound traffic).

    id: string

    Same id as the wrapped provider (store namespaces stay unchanged).

    capabilities: ProviderCapabilities

    Same capabilities object as the wrapped provider.

    seed: number

    Seed in use.

    log: ChaosLogEntry[] = []

    Everything the kit injected, in order.

    delivered: InboundEvent[] = []

    Every inbound event actually delivered to listeners (copies and replays included), in delivery order.

    ingest?: (
        rawEvent: unknown,
        headers?: Record<string, string | string[] | undefined>,
    ) => unknown

    Push ingress for webhook transports (Cloud API POST, Telegram/Discord webhook, Discord interaction endpoint). The host HTTP server calls this with the raw body (and, optionally, the request headers for signature checks); the provider normalizes and emits via onEvent. May return a value the host can use as the HTTP status (e.g. 200 accepted / 403 bad signature). Socket-first providers (Baileys, Discord gateway) omit it.

    downloadMedia?: (
        ref: MediaRef,
    ) => Promise<Buffer<ArrayBufferLike> | ReadableStream>

    Lazy media retrieval behind one signature for every download model.

    serializeMediaRef?: (ref: MediaRef) => SerializedMediaRef | undefined

    Durable journaling (BotConfig.durability): turn a live MediaRef into a JSON-safe, versioned reference that rehydrateMediaRef can rebuild in another process, so a replayed message (crash recovery, background retry) can still download its media. Synchronous, cheap and side-effect free; a throw counts as "not durable". Return undefined for a ref that must not be journaled (e.g. view-once media). Never include credentials: the result is stored at rest with the inbound record.

    rehydrateMediaRef?: (
        stored: SerializedMediaRef,
        ref: Omit<MediaRef, "token">,
    ) => MediaRef | Promise<MediaRef>

    Rebuild the MediaRef of a replayed message from what serializeMediaRef wrote. ref is the journaled ref without its token. Pure decoding: it must not need the transport to be connected. stored comes from storage and is UNTRUSTED: reject an invalid, unknown-version or tampered value with a ProviderError.

    getMessage?: (ref: MessageRef) => Promise<InboundMessage<unknown> | undefined>

    Read-back seam for poll aggregation, quote-body resolution, retry decrypt. Providers that don't need it no-op / omit it. Replaces the constructor- injected getMessage callback baked into BaileysProvider today.

    markRead?: (refs: MessageRef[]) => Promise<void>
    typing?: (
        chatId: string,
        state?: "typing" | "paused" | "recording",
    ) => Promise<void>
    deleteMessage?: (ref: MessageRef) => Promise<void>
    editMessage?: (
        ref: MessageRef,
        content: OutboundContent,
    ) => Promise<SendResult<unknown>>
    sendDraft?: (
        chatId: string,
        draftId: number,
        text: string,
        opts?: DraftOptions,
    ) => Promise<void>

    Show (or update) an ephemeral, not-yet-sent preview of a message being generated. Same draftId → the same draft is updated in place. A draft is NOT a message: the caller must still send() the final text. Only for providers with capabilities.streaming === 'draft'; rejects (typed error) where the transport refuses drafts (e.g. Telegram groups), and the caller then falls back to edits or chunks.

    acknowledgeInteraction?: (
        interactionId: string,
        opts?: { text?: string; defer?: boolean },
    ) => Promise<void>
    canRenderForm?: (form: FormDefinition, opts?: { nativeId?: string }) => boolean

    Whether a form OutboundContent for form (and opts.nativeId) can be rendered natively right now, e.g. whether a WhatsApp Flow is mapped to it. Only consulted when capabilities.forms === 'native'; when it returns false, an auto-mode flow().form() asks field by field without sending (and failing) a form message first. Absent = every form is renderable.

    textFormatFor?: (chatId: string) => TextFormat | undefined

    Rich-text dialect of one chat, for providers that serve several channels with different markup through one instance (e.g. Twilio: whatsapp: chats render WhatsApp markup, SMS chats render none). Return undefined when the chat's format is unknown; the core then falls back to capabilities.textFormat. Exposed as ctx.textFormat. Must be synchronous and cheap (it runs once per inbound message). Absent = every chat uses capabilities.textFormat.

    • Cancels every pending delivery, buffered or held event, then stops the wrapped provider.

      Returns Promise<void>

    • Send through the wrapped provider unless chaos decides this attempt fails (scripted attempts, probability, or the connection is down), in which case it rejects with a typed ProviderError and nothing reaches the wrapped provider's outbox.

      Parameters

      Returns Promise<SendResult<unknown>>

    • Simulate a connection drop: emits { state: 'closed', reason }, then holds inbound and fails sends (per options) until reconnect. A finite downtimeMs schedules the reconnect automatically; the default for a manual drop is to stay down until reconnect() is called.

      Parameters

      • options: { reason?: DisconnectReason; downtimeMs?: number; retryAfterMs?: number } = {}

      Returns void

    • End a simulated drop: emits connecting then ready, redelivers the last replayOnReconnect events (offline-sync duplicates), then delivers what was held while down, in arrival order.

      Returns void

    • Redeliver (fresh copies of) the last count delivered inbound events — an explicit webhook retry.

      Parameters

      • count: number = 1

      Returns void

    • Deliver whatever sits in the reorder window right now (permuted as configured).

      Returns void

    • Wait (real timers) until no chaos delivery, reorder flush, scheduled reconnect or delayed send is pending, plus one macrotask so listeners can dispatch. Events held by an indefinite drop do not count as pending.

      Parameters

      • timeoutMs: number = 5000

      Returns Promise<void>

      if still busy after timeoutMs (default 5000).

    • get selfId(): string | null

      The wrapped provider's self id (null when it has none: never undefined, see IProvider.selfId).

      Returns string | null

    • get connected(): boolean

      false while a simulated drop is in effect.

      Returns boolean

    • get sendAttempts(): number

      Number of send() calls so far (failed ones included).

      Returns number

    • get pending(): number

      Scheduled deliveries, reorder/reconnect timers and delayed sends still outstanding.

      Returns number