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

    Interface MessageContext<TTools, TRaw>

    The ergonomic context handed to every handler. Generic over TRaw. The reply helpers keep IDENTICAL signatures to v1 — internally each now builds an OutboundContent descriptor and calls provider.send, instead of hand-rolling Baileys JSON. from/sender/body/type/isGroup are read straight off the normalized InboundMessage, so user code and keyFor are unchanged.

    interface MessageContext<TTools extends object = DefaultTools, TRaw = unknown> {
        body: string;
        from: string;
        sender: string;
        senderInfo: SenderInfo;
        pushName?: string;
        chat: ChatInfo;
        type: MessageType;
        isGroup: boolean;
        isPrivate: boolean;
        isFromMe: boolean;
        mentions: string[];
        quoted?: QuotedRef;
        media?: MediaPayload;
        sharedLocation?: InboundLocation;
        ref: MessageRef;
        raw: TRaw;
        timestamp: number;
        interaction?: { id: string; data: string; respondBy?: number };
        acknowledge(text?: string): Promise<void>;
        call?: CallEvent;
        say(text: string, opts?: ReplyOptions): Promise<SentMessage<unknown>>;
        reply(text: string, opts?: ReplyOptions): Promise<SentMessage<unknown>>;
        answer(text: string, opts?: ReplyOptions): Promise<SentMessage<unknown>>;
        text(t: string, opts?: ReplyOptions): Promise<SentMessage<unknown>>;
        photo(source: MediaSource, caption?: string): Promise<SentMessage<unknown>>;
        video(
            source: MediaSource,
            caption?: string,
            opts?: { gifPlayback?: boolean; ptv?: boolean },
        ): Promise<SentMessage<unknown>>;
        audio(source: MediaSource): Promise<SentMessage<unknown>>;
        voice(source: MediaSource): Promise<SentMessage<unknown>>;
        file(
            source: MediaSource,
            filename?: string,
            caption?: string,
        ): Promise<SentMessage<unknown>>;
        sticker(
            source: MediaSource,
            opts?: { pack?: string; author?: string },
        ): Promise<SentMessage<unknown>>;
        location(
            lat: number,
            lng: number,
            name?: string,
        ): Promise<SentMessage<unknown>>;
        contact(phone: string, displayName?: string): Promise<SentMessage<unknown>>;
        poll(
            question: string,
            options: string[],
            multiSelect?: boolean,
        ): Promise<SentMessage<unknown>>;
        buttons(text: string, buttons: Button[]): Promise<SentMessage<unknown>>;
        react(emoji: string, target?: MessageRef): Promise<SentMessage<unknown>>;
        quote(text: string, opts?: ReplyOptions): Promise<SentMessage<unknown>>;
        edit(
            content: string | OutboundContent,
            opts?: EditOptions,
        ): Promise<EditResult>;
        edit(
            ref: MessageRef,
            content: string | OutboundContent,
            opts?: EditOptions,
        ): Promise<EditResult>;
        supports(feature: MessagingFeature): MessagingSupport;
        replyWithTyping(
            text: string,
            msPerChar?: number,
        ): Promise<SentMessage<unknown>>;
        sendNative(
            provider: string,
            payload: unknown,
        ): Promise<SentMessage<unknown>>;
        stream(source: StreamSource, opts?: StreamOptions): Promise<StreamResult>;
        form?(
            definition: FormDefinition,
            opts?: { token?: string; nativeId?: string },
        ): Promise<SentMessage<unknown>>;
        canRenderForm?(
            definition: FormDefinition,
            opts?: { nativeId?: string },
        ): boolean;
        capabilities?: ProviderCapabilities;
        textFormat?: TextFormat;
        markRead(): Promise<void>;
        typing(): Promise<void>;
        recording(): Promise<void>;
        delete(): Promise<void>;
        handoff(opts?: HandoffOptions): Promise<boolean>;
        pass(): void;
        abort(): void;
        next?(): Promise<void>;
        user: UserData;
        lang: string;
        defaultLang?: string;
        langSource?: LangSource;
        t(key: string, params?: TranslateParams): string;
        setLang(code: string): Promise<void>;
        tools: TTools;
    }

    Type Parameters

    Index
    body: string
    from: string
    sender: string
    senderInfo: SenderInfo
    pushName?: string
    chat: ChatInfo
    isGroup: boolean
    isPrivate: boolean
    isFromMe: boolean
    mentions: string[]
    quoted?: QuotedRef
    media?: MediaPayload
    sharedLocation?: InboundLocation

    The location the user shared (type === 'location'), normalized by the provider; undefined otherwise. Named sharedLocation because ctx.location() SENDS one.

    raw: TRaw
    timestamp: number

    When the message was sent, in epoch milliseconds (new Date(ctx.timestamp) works). A provider that still reports epoch seconds is converted.

    interaction?: { id: string; data: string; respondBy?: number }

    Present only when this context projects an interaction (Telegram/Discord). acknowledge() is a safe no-op on transports without ack (WhatsApp). On a transport with capabilities.interactionsRequireAck the core acks on its own; once that ack went out (after the first send or near the deadline), acknowledge(text) sends text as a regular reply instead.

    call?: CallEvent

    Present only on the context the CALL trigger receives (type === 'call'): the provider's normalized call notification.

    capabilities?: ProviderCapabilities

    Capabilities of the provider this message came from (set on contexts built by a Bot).

    textFormat?: TextFormat

    Rich-text dialect of THIS chat: the provider's textFormatFor(chatId), else capabilities.textFormat. Unset = unknown (send Markdown untouched). Use it, not capabilities.textFormat, to format text for the chat (@botmesh/ai does).

    user: UserData

    Persistent profile of the sender (ctx.sender), shared across chats — see UserData. ctx.user.ns('game') scopes it to a namespace.

    lang: string

    Resolved language of this message (normalized locale code, e.g. 'es', 'en'). Order: ctx.user preference → provider-reported language → i18n.detect → default.

    defaultLang?: string

    The bot's default locale (lang / i18n.defaultLocale, 'en' unless configured), whatever lang resolved to. Plugins that ship their own built-in texts for a few languages use it as the fallback for a lang they do not cover.

    langSource?: LangSource

    Where lang came from ('user' | 'provider' | 'detector' | 'default'); undefined while i18n is off (then lang is just the default locale). Plugins that want a real language signal (e.g. a speech-to-text hint) should ignore 'default'. A hook that assigns ctx.lang directly does not update it.

    tools: TTools
    • Parameters

      • Optionaltext: string

      Returns Promise<void>

    • Parameters

      • source: MediaSource
      • Optionalcaption: string
      • Optionalopts: { gifPlayback?: boolean; ptv?: boolean }

      Returns Promise<SentMessage<unknown>>

    • Parameters

      • question: string
      • options: string[]
      • OptionalmultiSelect: boolean

      Returns Promise<SentMessage<unknown>>

    • React to the inbound message (or target). On a provider without capabilities.reactions it resolves a skipped result instead of rejecting. An empty emoji removes the reaction where the transport supports it.

      Parameters

      Returns Promise<SentMessage<unknown>>

    • Edit this context's own last reply (ctx.edit(content), only when it has the same content kind and no bot.send/bot.sendTo went to the chat since; replies of other handler runs or other participants' flows are never targeted) or a given one (ctx.edit(ref, content); a fallback send goes to ref.chatId). Native when the provider has capabilities.edit and editMessage; otherwise (or when the kind is not editable there) it falls back to sending a new message — { fallback: 'skip' } sends nothing instead. Edits run the before/after('send') hooks (payload edit: ref); a hook abort resolves skipped.

      Parameters

      Returns Promise<EditResult>

    • Parameters

      Returns Promise<EditResult>

    • Show "typing…" for about msPerChar ms per character (40 by default, capped at 5 s), then send text. Ignores BotConfig.typing.

      Parameters

      • text: string
      • OptionalmsPerChar: number

      Returns Promise<SentMessage<unknown>>

      Use ctx.reply(text, { typing: true }) (or a TypingConfig object such as { typing: { charsPerSecond: 25, maxMs: 5000 } }): it shares the bot-wide typing policy, refreshes the indicator and is dropped on stop/handoff like every paced reply.

    • Escape hatch for transport-native content (templates, components).

      Parameters

      • provider: string
      • payload: unknown

      Returns Promise<SentMessage<unknown>>

    • Stream a reply that is still being generated (e.g. LLM tokens) to this chat, using the best strategy the provider supports (capabilities.streaming): native drafts, throttled edits, or sentence chunks with typing. Long text is split so no message exceeds capabilities.maxTextLength. See StreamOptions.

      Parameters

      Returns Promise<StreamResult>

    • Send a native form (OutboundContent kind 'form'). Only meaningful when capabilities.forms === 'native'; other providers reject it with UnsupportedContentError. The submission arrives as an inbound message of type 'form' (see parseFormReply()); flow().form() handles all of this. Set on contexts built by a Bot.

      Parameters

      • definition: FormDefinition
      • Optionalopts: { token?: string; nativeId?: string }

      Returns Promise<SentMessage<unknown>>

    • Whether the provider can render definition natively right now (see IProvider.canRenderForm). Set on contexts built by a Bot whose provider implements it; absent = assume yes when capabilities.forms === 'native'.

      Parameters

      Returns boolean

    • Show the typing indicator. Best-effort: never rejects (a provider failure is logged at debug).

      Returns Promise<void>

    • Show the recording-audio indicator. Best-effort like typing.

      Returns Promise<void>

    • Hand this chat (from) over to a human: the bot stops routing its messages until bot.resume(chatId), and any conversation in the chat is cancelled silently. Resolves true if the chat was not already handed off. Safe to call without awaiting (the state flips synchronously). See bot.handoff().

      Parameters

      Returns Promise<boolean>

    • Translate key (dot path) in lang, interpolating {param} placeholders; params.count picks plural forms. Missing keys fall back to the default locale, then to the key itself. See bot.locale().

      Parameters

      Returns string

    • Switch the sender's language: persists the registered locale serving code ('en-GB' → 'en' when only en exists) to ctx.user (key 'lang' by default) and updates lang for this message. Rejects with TypeError on an empty code or one no registered locale serves (nothing is stored), with Error while i18n is not enabled (no bot.locale() call and no BotConfig.i18n), and with UserDataError when the profile cannot be written.

      Parameters

      • code: string

      Returns Promise<void>