import type { LarkMessage } from '../../types.js';
interface RawEventData {
    sender: {
        sender_id: {
            open_id?: string;
            user_id?: string;
            union_id?: string;
        };
        sender_type: string;
        tenant_key?: string;
    };
    message: {
        message_id: string;
        root_id?: string;
        thread_id?: string;
        parent_id?: string;
        message_type: string;
        content: string;
        chat_id: string;
        chat_type: string;
        create_time: string;
        mentions?: Array<{
            key: string;
            name: string;
            id?: {
                open_id?: string;
                user_id?: string;
                union_id?: string;
            };
            tenant_key?: string;
        }>;
    };
}
/**
 * When the WebSocket event delivers message_type "nonsupport", call the REST API
 * to fetch the real message content and patch the event data in-place.
 *
 * Also handles `interactive`: the WebSocket event only carries a simplified
 * fallback view of cards (often literally "请升级至最新版本客户端，以查看内容"),
 * so we fetch the real card JSON (including v2 `body.elements`) via REST.
 */
export declare function resolveNonsupportMessage(data: RawEventData, larkAppId: string): Promise<void>;
/**
 * Lark bundles the real v2 card JSON inside a `user_dsl` string on the
 * simplified interactive payload. When present, return the unwrapped v2
 * body so downstream extractors see schema/body.elements directly.
 */
export declare function unwrapUserDslContent(rawContent: string): string | null;
/**
 * Lark's simplified "upgrade your client" card fallback marker. When this text
 * shows up in a card's resolved content, the real body was stripped and must be
 * recovered via `im.message.get` (with `card_msg_content_type=user_card_content`).
 */
export declare const CARD_UPGRADE_FALLBACK = "\u8BF7\u5347\u7EA7\u81F3\u6700\u65B0\u7248\u672C\u5BA2\u6237\u7AEF";
/**
 * Broad check — content carries the upgrade notice *somewhere*. Used to decide
 * whether a card needs REST re-resolution. Deliberately a substring match: a
 * false positive only costs one extra `im.message.get`, and this is the only
 * way to catch **embedded** fallbacks — complex cards (e.g. Argos alarm cards
 * with nested sub-cards) render fine at the top level but bury one or more
 * `请升级…` placeholders mid-body where an anchored check would miss them.
 */
export declare function cardContentHasUpgradeFallback(content: string): boolean;
/**
 * Narrow check — content *is* essentially just Lark's upgrade notice, not a
 * body that merely mentions it. Anchored at the start after stripping leading
 * `[图片]` / `[文件 N]` placeholders (the bare fallback renders as
 * `[图片]请升级至最新版本客户端，以查看内容`). Used as the replace gate when
 * re-resolving via REST: it keeps a card that legitimately quotes the phrase
 * mid-text — e.g. a message discussing this very fallback — from being
 * discarded, while still rejecting a REST view that came back as a bare
 * fallback. Makes no structural assumptions, so it's safe for the
 * simplified-but-real Format A shape (no schema/body/header) message.list
 * returns.
 */
export declare function isPureCardUpgradeFallback(content: string): boolean;
export interface MessageResource {
    type: 'image' | 'file';
    key: string;
    name: string;
    /** When set, download uses this message_id instead of the parent (e.g. merge_forward sub-messages). */
    messageId?: string;
}
/**
 * Stateful numbering that keeps `[图片 N]` / `[文件 N]` placeholders in the
 * rendered text aligned with the attachment footer. The same key always gets
 * the same number, so duplicates across merge_forward sub-messages collapse
 * correctly.
 *
 * Image and file counters are INDEPENDENT — `formatAttachmentsHint` emits
 * `<image n=...>` and `<file n=...>` separately numbered, so a message with
 * one image and one file should render as `[图片 1]` + `[文件 1]`, not
 * `[图片 1]` + `[文件 2]`. Keys are typed via the `image:` / `file:` prefix.
 */
export interface ImgNumberer {
    assign(key: string): {
        num: number;
        isNew: boolean;
    };
}
export declare function createImgNumberer(): ImgNumberer;
export declare function extractResources(msgType: string, rawContent: string, numberer?: ImgNumberer): MessageResource[];
export declare function parseEventMessage(data: RawEventData): {
    parsed: LarkMessage;
    resources: MessageResource[];
};
export declare function parseApiMessage(msg: any, numberer?: ImgNumberer): LarkMessage;
/**
 * Strip leading `@<name>` mentions from a resolved-content string so callers
 * can detect daemon `/commands` even when the user @-mentioned the bot first.
 *
 * Uses the structured mentions list when available (handles names with spaces);
 * falls back to a `@\S+` regex for cases where Lark didn't populate mentions
 * (e.g. some post messages where the at-tag becomes a plain `@<user_name>`
 * string in the rendered text).
 */
export declare function stripLeadingMentions(content: string, mentions?: {
    name: string;
}[]): string;
/**
 * Marker for sub-cards Lark renders only client-side (collapsible panels,
 * lazy "展开" sections). Neither `im.message.get` representation returns their
 * body — Format A shows the upgrade fallback, Format B omits them — so we
 * surface an honest placeholder instead of a misleading blank or raw fallback.
 */
export declare const CARD_EMBEDDED_PLACEHOLDER = "[\u5361\u7247\u5185\u5D4C\u7EC4\u4EF6\uFF0C\u9700\u5728\u98DE\u4E66\u5BA2\u6237\u7AEF\u5C55\u5F00\u67E5\u770B]";
/** Wrap merged text so extractCardContent returns it verbatim downstream. */
export declare function wrapResolvedCardText(text: string): string;
/**
 * Merge the two Lark card renderings into one complete text. Format B (full
 * structured) is the base — it preserves links, sub-card bodies and select
 * options. From Format A (server-rendered) we recover only the field VALUES B
 * left blank (e.g. 值班人 names) via targeted label-fill: a B label line whose
 * value is empty is filled from A only when A's value isn't already present in
 * B (so fields B already renders aren't duplicated). Sub-cards Lark serves
 * client-side only — which A shows as upgrade holes and B omits — get one
 * honest placeholder rather than a silent drop or raw "请升级" text.
 */
export declare function mergeCardText(textA: string, textB: string): string;
/**
 * Resolve a card to its most complete text by unioning both `im.message.get`
 * representations (server-rendered Format A + full structured Format B). Used
 * by BOTH the live event path and `botmux history` so a single message_id
 * resolves identically everywhere. Returns null when neither representation
 * could be fetched (caller keeps whatever it already had).
 */
export declare function resolveMergedCardContent(larkAppId: string, messageId: string, numberer?: ImgNumberer): Promise<{
    text: string;
    structuredContent: string;
} | null>;
/**
 * Resolve an interactive event's card to its complete merged text in place.
 * Stores a sentinel that carries BOTH the merged text (for extractCardContent)
 * and the structured card JSON (for extractResources image/file extraction).
 * Falls back to local user_dsl unwrap if the REST merge yields nothing. Shared
 * by the live daemon path so forwarded cards reach the model fully parsed.
 */
export declare function resolveEventCard(data: RawEventData, larkAppId: string): Promise<void>;
export {};
//# sourceMappingURL=message-parser.d.ts.map