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

    Interface IProvider<TRaw>

    Transport-agnostic provider contract. TRaw is the provider's native raw payload (proto.IWebMessageInfo, webhook entry, gateway object); it stays unknown in all shared code and is narrowed only inside provider-specific paths.

    STABILITY CONTRACT (normative): for the lifetime of a session, the same entity MUST yield the same senderId and the same chat the same chatId on every emitted event. All transport-internal aliasing is resolved inside the provider before emission. The core performs no identity reconciliation.

    INBOUND ID CONTRACT (normative): a message's ref.id and an interaction's interactionId identify ONE logical inbound event within its chat: distinct events get distinct ids, and a redelivery of the same event (webhook retry, reconnect replay) carries the same id. Core relies on this for inbound idempotency (BotConfig.dedupe, keyed by chatId + id). When the transport provides no such id, synthesize one from per-event data that a redelivery repeats (e.g. kind + event timestamp) or emit '', which opts the event out of deduplication. Never fall back to a per-user constant (sender id, user_ref) or a shared placeholder ('<prefix>_0').

    interface IProvider<TRaw = unknown> {
        id: string;
        capabilities: ProviderCapabilities;
        selfId?: string | null;
        start(): Promise<void>;
        stop(): Promise<void>;
        send(
            chatId: string,
            content: OutboundContent,
            options?: SendOptions,
        ): Promise<SendResult<TRaw>>;
        onEvent(listener: InboundListener<TRaw>): () => void;
        ingest?(
            rawEvent: unknown,
            headers?: Record<string, string | string[] | undefined>,
        ): unknown;
        downloadMedia?(
            ref: MediaRef,
        ): Promise<Buffer<ArrayBufferLike> | ReadableStream>;
        serializeMediaRef?(ref: MediaRef): SerializedMediaRef | undefined;
        rehydrateMediaRef?(
            stored: SerializedMediaRef,
            ref: Omit<MediaRef, "token">,
        ): MediaRef | Promise<MediaRef>;
        getMessage?(ref: MessageRef): Promise<InboundMessage<TRaw> | undefined>;
        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<TRaw>>;
        acknowledgeInteraction?(
            interactionId: string,
            opts?: { text?: string; defer?: boolean },
        ): Promise<void>;
        sendDraft?(
            chatId: string,
            draftId: number,
            text: string,
            opts?: DraftOptions,
        ): Promise<void>;
        canRenderForm?(form: FormDefinition, opts?: { nativeId?: string }): boolean;
        textFormatFor?(chatId: string): TextFormat | undefined;
        connection?: unknown;
    }

    Type Parameters

    • TRaw = unknown

    Implemented by

    Index
    id: string

    Stable transport tag (e.g. 'baileys', 'telegram') — used to namespace conversation keys if one Bot ever runs multiple providers concurrently.

    capabilities: ProviderCapabilities
    selfId?: string | null

    The bot's own user id once connected (for group mention matching). Optional.

    connection?: unknown

    A single primary connection handle for DEBUGGING ONLY. Not a structural guarantee of exactly one connection (Discord may hold many shard sockets). Core must NOT reach through this for behavior.

    • 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.

      Parameters

      • rawEvent: unknown
      • Optionalheaders: Record<string, string | string[] | undefined>

      Returns unknown

    • Lazy media retrieval behind one signature for every download model.

      Parameters

      Returns Promise<Buffer<ArrayBufferLike> | ReadableStream>

    • 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.

      Parameters

      Returns SerializedMediaRef | 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.

      Parameters

      Returns Promise<InboundMessage<TRaw> | undefined>

    • Parameters

      • chatId: string
      • Optionalstate: "typing" | "paused" | "recording"

      Returns Promise<void>

    • Parameters

      • interactionId: string
      • Optionalopts: { text?: string; defer?: boolean }

      Returns 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.

      Parameters

      • chatId: string
      • draftId: number
      • text: string
      • Optionalopts: DraftOptions

      Returns Promise<void>

    • 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.

      Parameters

      Returns boolean

    • 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.

      Parameters

      • chatId: string

      Returns TextFormat | undefined