UNPKG

@igniter-js/bot

Version:

A modern, type-safe multi-platform bot framework for the Igniter.js ecosystem (adapters, middleware, commands, rich content).

1,475 lines (1,462 loc) 50.1 kB
import * as zod from 'zod'; import { ZodObject, TypeOf, z } from 'zod'; /** * Types and interfaces for @igniter-js/bot core system * * This file contains all shared types and interfaces for bot events, content, context, commands, adapters, and middleware. * * @module bot.types */ /** * Logger interface used across the bot core and adapters for structured logging. * Adapters receive an optional instance so they can avoid using console.* directly. * All methods are optional to allow lightweight injection. */ interface BotLogger { debug?: (...args: any[]) => void; info?: (...args: any[]) => void; warn?: (...args: any[]) => void; error?: (...args: any[]) => void; } /** * Represents all possible bot events that can be handled by the system. * - 'start': Triggered when the bot starts. * - 'message': Triggered when a message is received. * - 'error': Triggered when an error occurs. */ type BotEvent = 'start' | 'message' | 'error'; /** * Represents an attachment content in a bot message, such as files or images. */ interface BotAttachmentContent { /** The MIME type or file type of the attachment. */ type: string; /** The name of the attachment file. */ name: string; /** The content of the attachment, can be a string (URL/base64) or File object. */ content: string; } /** * Represents a plain text message content. */ interface BotTextContent { /** The type of content, always 'text'. */ type: 'text'; /** The text content. */ content: string; /** The raw representation of the text message. */ raw: string; } /** * Represents an image message content. * Used when the bot receives or sends an image. */ interface BotImageContent { /** The type of content, always 'image'. */ type: 'image'; /** The image content, typically a URL or base64 string. */ content: string; /** The image file object. */ file: File; /** Caption of image */ caption?: string; } /** * Represents a document message content. * Used when the bot receives or sends a document file (PDF, DOCX, etc). */ interface BotDocumentContent { /** The type of content, always 'document'. */ type: 'document'; /** The document content, typically a URL or base64 string. */ content: string; /** The document file object. */ file: File; } /** * Represents an audio message content. * Used when the bot receives or sends an audio file or recording. */ interface BotAudioContent { /** The type of content, always 'audio'. */ type: 'audio'; /** The audio content, typically a URL or base64 string. */ content: string; /** The audio file object. */ file: File; } /** * Represents a command message content, such as "/help" or "/start". */ interface BotCommandContent { /** The type of content, always 'command'. */ type: 'command'; /** The command name (without the slash). */ command: string; /** The parameters passed to the command. */ params: string[]; /** The raw representation of the command message. */ raw: string; } /** * Union type for all possible message content types handled by the bot. */ type BotContent = BotTextContent | BotCommandContent | BotImageContent | BotAudioContent | BotDocumentContent; /** * Represents the context of a bot event, including message, channel, and author information. */ interface BotContext { /** The event type (start, message, error, etc). */ event: BotEvent; /** The provider or platform (e.g., 'telegram', 'discord'). */ provider: string; /** The bot instance information and send method. */ bot: { /** Bot unique identifier. */ id: string; /** Bot name. */ name: string; /** Method to send a message from this bot. */ send: (params: Omit<BotSendParams<any>, 'config'>) => Promise<void>; }; /** Channel information where the event/message occurred. */ channel: { /** Channel unique identifier. */ id: string; /** Channel name. */ name: string; /** Whether the channel is a group/supergroup (vs private chat). */ isGroup: boolean; }; /** Message details, including content, attachments, and author. */ message: { /** The content of the message (text, command, etc). */ content?: BotContent; /** Any attachments sent with the message. */ attachments?: BotAttachmentContent[]; /** Author information. */ author: { /** Author unique identifier. */ id: string; /** Author display name. */ name: string; /** Author username or handle. */ username: string; }; /** Whether the bot was mentioned in this message. */ isMentioned: boolean; }; } /** * Parameters for sending a message using a bot adapter. * @template TConfig Adapter configuration type. */ type BotSendParams<TConfig extends Record<string, any>> = { /** The provider/platform to send the message to. */ provider: string; /** The channel identifier to send the message to. */ channel: string; /** The message content (only text supported for now). */ content: { type: 'text'; content: string; }; /** Adapter-specific configuration. */ config: TConfig; }; /** * Represents a command that can be handled by the bot. */ interface BotCommand { /** The command name (without the slash). */ name: string; /** Alternative names for the command. */ aliases: string[]; /** Description of what the command does. */ description: string; /** Help text to be shown if the command fails or is used incorrectly. */ help: string; /** Handler function to execute the command logic. */ handle: (ctx: BotContext, params: any) => Promise<void>; } /** * Parameters for handling an incoming request in a bot adapter. * @template TConfig Adapter configuration type. */ type BotHandleParams<TConfig extends Record<string, any>> = { /** The incoming request object. */ request: Request; /** Adapter-specific configuration. */ config: TConfig; }; /** * Interface for a bot adapter, which connects the bot to a specific provider/platform. * @template TConfig Adapter configuration type (Zod schema). */ interface IBotAdapter<TConfig extends ZodObject<any>> { /** Adapter name (e.g., 'telegram', 'discord'). */ name: string; /** Adapter configuration schema (Zod). */ parameters: TConfig; /** Initializes the adapter with configuration, available commands and optional logger. */ init: (params: { config: TypeOf<TConfig>; commands: BotCommand[]; logger?: BotLogger; }) => Promise<void>; /** Sends a message using the adapter (logger provided for structured emission). */ send: (params: BotSendParams<TConfig> & { logger?: BotLogger; }) => Promise<void>; /** Handles an incoming request (logger available) and returns the bot context or null to ignore the update. */ handle: (params: BotHandleParams<TConfig> & { logger?: BotLogger; }) => Promise<Omit<BotContext, 'bot'> | null>; } /** * Middleware function type for processing bot context before/after main logic. * Receives the context and a next function to continue the chain. */ type Middleware = (ctx: BotContext, next: () => Promise<void>) => Promise<void>; /** * Error code constants – centralized for consistency & future i18n / mapping. */ declare const BotErrorCodes: { readonly PROVIDER_NOT_FOUND: "PROVIDER_NOT_FOUND"; readonly COMMAND_NOT_FOUND: "COMMAND_NOT_FOUND"; readonly INVALID_COMMAND_PARAMETERS: "INVALID_COMMAND_PARAMETERS"; readonly ADAPTER_HANDLE_RETURNED_NULL: "ADAPTER_HANDLE_RETURNED_NULL"; }; type BotErrorCode = (typeof BotErrorCodes)[keyof typeof BotErrorCodes]; /** * Rich error type used internally (and optionally by adapters). */ declare class BotError extends Error { code: BotErrorCode; meta?: Record<string, unknown> | undefined; constructor(code: BotErrorCode, message?: string, meta?: Record<string, unknown> | undefined); } /** * Main Bot class for @igniter-js/bot * * Features: * - Multi-adapter routing * - Middleware pipeline * - Command system with alias indexing * - Pluggable logging * - Extensible runtime registration (adapters, commands, middleware) * - Type-safe adapter factory helper */ declare class Bot<TAdapters extends Record<string, IBotAdapter<any>>, TMiddlewares extends Middleware[], TCommands extends Record<string, BotCommand>> { /** Unique bot identifier */ id: string; /** Bot name (display) */ name: string; /** Registered adapters (keyed by provider name) */ private adapters; /** Registered middlewares (ordered pipeline) */ private middlewares; /** Command map (original user supplied) */ private commands; /** Indexed / normalized command lookup */ private commandIndex; /** Optional logger */ private logger?; /** Event listeners */ private listeners; /** * Optional hook executed just before middleware pipeline * to allow last‑minute context enrichment (e.g., session loading). */ private preProcessHooks; /** * Optional hook executed after successful processing (not on thrown errors). */ private postProcessHooks; /** * Creates a new Bot instance. */ constructor(config: { id: string; name: string; adapters: TAdapters; middlewares?: TMiddlewares; commands?: TCommands; on?: Partial<Record<BotEvent, (ctx: BotContext) => Promise<void>>>; logger?: BotLogger; }); /** * Rebuilds the internal command index (idempotent). * Called at construction and whenever a command is dynamically registered. */ private rebuildCommandIndex; /** * Dynamically register a new command at runtime. * Useful for plugin systems / hot-reload dev flows. */ registerCommand(name: string, command: BotCommand): this; /** * Dynamically register a middleware (appended to the chain). */ use(mw: Middleware): this; /** * Dynamically register an adapter. */ registerAdapter<K extends string, A extends IBotAdapter<any>>(key: K, adapter: A): this; /** * Hook executed before processing pipeline. Runs in registration order. */ onPreProcess(hook: (ctx: BotContext) => Promise<void> | void): this; /** * Hook executed after successful processing (not on thrown errors). */ onPostProcess(hook: (ctx: BotContext) => Promise<void> | void): this; /** * Emits a bot event to registered listeners manually. */ emit(event: BotEvent, ctx: BotContext): Promise<void>; /** * Adapter factory helper (legacy static name kept for backwards compatibility). * Now logger-aware: logger will be injected at call sites (init/send/handle). */ static adapter<TConfig extends ZodObject<any>>(adapter: { name: string; parameters: TConfig; init: (params: { config: TypeOf<TConfig>; commands: BotCommand[]; logger?: BotLogger; }) => Promise<void>; send: (params: BotSendParams<TypeOf<TConfig>> & { logger?: BotLogger; }) => Promise<void>; handle: (params: { request: Request; config: TypeOf<TConfig>; logger?: BotLogger; }) => Promise<Omit<BotContext, 'bot'> | null>; }): (config: TypeOf<TConfig>) => IBotAdapter<TConfig>; /** * Factory for constructing a Bot with strong typing. */ static create<TAdapters extends Record<string, IBotAdapter<any>>, TMiddlewares extends Middleware[] = [], TCommands extends Record<string, BotCommand> = {}>(config: { id: string; name: string; adapters: TAdapters; middlewares?: TMiddlewares; commands?: TCommands; on?: Partial<Record<BotEvent, (ctx: BotContext) => Promise<void>>>; logger?: BotLogger; }): Bot<TAdapters, TMiddlewares, TCommands>; /** * Register (subscribe) to a lifecycle/event stream. */ on(event: BotEvent, callback: (ctx: BotContext) => Promise<void>): void; /** * Resolve command by name or alias (case-insensitive). */ private resolveCommand; /** * Sends a message (provider abstraction). */ send(params: Omit<BotSendParams<any>, 'config'>): Promise<void>; /** * Core processing pipeline: * 1. preProcess hooks * 2. middleware chain * 3. listeners for event * 4. command execution (if command) * 5. postProcess hooks */ process(ctx: BotContext): Promise<void>; /** * Handle an incoming request from a provider (adapter). * * Contract: * - If adapter returns `null`, we respond 204 (ignored update). * - If adapter returns a context object, we process it and return 200. * - Any error thrown bubbles up unless caught externally. */ handle(adapter: keyof TAdapters, request: Request): Promise<Response>; /** * Initialize all adapters (idempotent at adapter level). * Passes current command list so adapters can perform platform-side registration (webhooks/commands). */ start(): Promise<void>; } /** * Telegram adapter implementation. * * Responsibilities: * - Webhook registration (optional; only if webhook.url provided) * - Command synchronization * - Incoming update parsing (messages, media, commands) * - Safe MarkdownV2 escaping for outgoing messages * * Logger Integration: * All console logging replaced by structured logger (if provided). */ declare const telegram: (config: { token: string; handle: string; webhook?: { url?: string | undefined; secret?: string | undefined; } | undefined; }) => IBotAdapter<zod.ZodObject<{ token: zod.ZodString; handle: zod.ZodString; webhook: zod.ZodOptional<zod.ZodObject<{ url: zod.ZodOptional<zod.ZodString>; secret: zod.ZodOptional<zod.ZodString>; }, "strip", zod.ZodTypeAny, { url?: string | undefined; secret?: string | undefined; }, { url?: string | undefined; secret?: string | undefined; }>>; }, "strip", zod.ZodTypeAny, { token: string; handle: string; webhook?: { url?: string | undefined; secret?: string | undefined; } | undefined; }, { token: string; handle: string; webhook?: { url?: string | undefined; secret?: string | undefined; } | undefined; }>>; declare const getServiceURL: (token: string, url?: string) => string; /** * Escapes special MarkdownV2 characters according to Telegram specification. * Reference: https://core.telegram.org/bots/api#markdownv2-style * * Rationale: * Telegram's MarkdownV2 requires escaping a broad set of punctuation characters. * Failing to escape correctly can break message formatting or drop content. * * Implementation notes: * - Regex groups every escapable character and prefixes with a backslash. * - Keep this helper idempotent for already-escaped text (Telegram tolerates double slashes, * but we avoid attempting to detect previously escaped segments for performance). * * @param text Raw user or system generated text. * @returns Safe string ready for Telegram MarkdownV2. */ declare function escapeMarkdownV2(text: string): string; /** * Parses raw Telegram message text and classifies it as either: * - Command ("/command arg1 arg2") -> BotCommandContent * - Plain text -> BotTextContent * * Rules: * - A command starts with a "/" at the first character (Telegram style). * - Splits command + arguments by spaces; command name excludes leading slash. * - Returns undefined if the text is empty or falsy. * * @param text Raw message text from Telegram update. * @returns Structured BotCommandContent | BotTextContent | undefined */ declare function parseTelegramMessageContent(text: string): BotTextContent | BotCommandContent | undefined; /** * Naive MIME type guess based on file extension. * Used only as a fallback when Telegram does not provide a definitive content-type. * * @param fileName The filename (with extension) to inspect. * @returns Best-effort MIME type string. */ declare function guessMimeType(fileName: string): string; /** * Downloads a Telegram file (by file_id) and returns a structured object * containing a File instance plus base64 representation and resolved metadata. * * Workflow: * 1. Call getFile to resolve the file path. * 2. Download binary content from Telegram file URL. * 3. Infer or override MIME type (optionally forcing JPEG for photos). * 4. Construct a File object (falls back gracefully in environments without full File support). * * @param fileId Telegram file_id. * @param token Bot token for authenticated API calls. * @param fileName Optional explicit filename override. * @param mimeType Optional explicit MIME type override. * @param forceJpeg Forces output MIME type to image/jpeg (used for uniform photo handling). * @throws Error if network calls fail or Telegram responds with non-ok status. * @returns Object with File, base64 encoded data, mimeType and resolved fileName. */ declare function fetchTelegramFileAsFile(fileId: string, token: string, fileName?: string, mimeType?: string, forceJpeg?: boolean): Promise<{ file: File; base64: string; mimeType: string; fileName: any; }>; /** * Telegram Adapter Schemas * * Provides runtime validation + static typing for: * - Adapter configuration (token + optional webhook config) * - Incoming update payloads (subset of Telegram Bot API Update object) * * Design Notes: * - Keep schema focused only on the fields consumed by the adapter. * - Use .describe() to aid future OpenAPI / doc generation. * - Optional fields remain optional; no silent coercion. * - Extend cautiously: adding fields is non‑breaking; removing is breaking. * * Reference: https://core.telegram.org/bots/api#update * * @module telegram.schemas * @alpha */ declare const TelegramAdapterParams: z.ZodObject<{ token: z.ZodString; handle: z.ZodString; webhook: z.ZodOptional<z.ZodObject<{ url: z.ZodOptional<z.ZodString>; secret: z.ZodOptional<z.ZodString>; }, "strip", z.ZodTypeAny, { url?: string | undefined; secret?: string | undefined; }, { url?: string | undefined; secret?: string | undefined; }>>; }, "strip", z.ZodTypeAny, { token: string; handle: string; webhook?: { url?: string | undefined; secret?: string | undefined; } | undefined; }, { token: string; handle: string; webhook?: { url?: string | undefined; secret?: string | undefined; } | undefined; }>; declare const TelegramUpdateSchema: z.ZodObject<{ update_id: z.ZodNumber; message: z.ZodOptional<z.ZodObject<{ message_id: z.ZodNumber; from: z.ZodObject<{ id: z.ZodNumber; is_bot: z.ZodBoolean; first_name: z.ZodString; last_name: z.ZodOptional<z.ZodString>; username: z.ZodOptional<z.ZodString>; language_code: z.ZodOptional<z.ZodString>; }, "strip", z.ZodTypeAny, { id: number; is_bot: boolean; first_name: string; last_name?: string | undefined; username?: string | undefined; language_code?: string | undefined; }, { id: number; is_bot: boolean; first_name: string; last_name?: string | undefined; username?: string | undefined; language_code?: string | undefined; }>; chat: z.ZodObject<{ id: z.ZodNumber; first_name: z.ZodOptional<z.ZodString>; last_name: z.ZodOptional<z.ZodString>; username: z.ZodOptional<z.ZodString>; title: z.ZodOptional<z.ZodString>; type: z.ZodEnum<["private", "group", "supergroup", "channel"]>; }, "strip", z.ZodTypeAny, { type: "channel" | "private" | "group" | "supergroup"; id: number; first_name?: string | undefined; last_name?: string | undefined; username?: string | undefined; title?: string | undefined; }, { type: "channel" | "private" | "group" | "supergroup"; id: number; first_name?: string | undefined; last_name?: string | undefined; username?: string | undefined; title?: string | undefined; }>; date: z.ZodNumber; text: z.ZodOptional<z.ZodString>; photo: z.ZodOptional<z.ZodArray<z.ZodObject<{ file_id: z.ZodString; file_unique_id: z.ZodString; file_size: z.ZodOptional<z.ZodNumber>; width: z.ZodOptional<z.ZodNumber>; height: z.ZodOptional<z.ZodNumber>; }, "strip", z.ZodTypeAny, { file_id: string; file_unique_id: string; file_size?: number | undefined; width?: number | undefined; height?: number | undefined; }, { file_id: string; file_unique_id: string; file_size?: number | undefined; width?: number | undefined; height?: number | undefined; }>, "many">>; document: z.ZodOptional<z.ZodObject<{ file_id: z.ZodString; file_unique_id: z.ZodString; file_name: z.ZodOptional<z.ZodString>; mime_type: z.ZodOptional<z.ZodString>; file_size: z.ZodOptional<z.ZodNumber>; }, "strip", z.ZodTypeAny, { file_id: string; file_unique_id: string; file_size?: number | undefined; file_name?: string | undefined; mime_type?: string | undefined; }, { file_id: string; file_unique_id: string; file_size?: number | undefined; file_name?: string | undefined; mime_type?: string | undefined; }>>; audio: z.ZodOptional<z.ZodObject<{ file_id: z.ZodString; file_unique_id: z.ZodString; duration: z.ZodOptional<z.ZodNumber>; mime_type: z.ZodOptional<z.ZodString>; file_size: z.ZodOptional<z.ZodNumber>; file_name: z.ZodOptional<z.ZodString>; }, "strip", z.ZodTypeAny, { file_id: string; file_unique_id: string; file_size?: number | undefined; file_name?: string | undefined; mime_type?: string | undefined; duration?: number | undefined; }, { file_id: string; file_unique_id: string; file_size?: number | undefined; file_name?: string | undefined; mime_type?: string | undefined; duration?: number | undefined; }>>; voice: z.ZodOptional<z.ZodObject<{ file_id: z.ZodString; file_unique_id: z.ZodString; duration: z.ZodOptional<z.ZodNumber>; mime_type: z.ZodOptional<z.ZodString>; file_size: z.ZodOptional<z.ZodNumber>; }, "strip", z.ZodTypeAny, { file_id: string; file_unique_id: string; file_size?: number | undefined; mime_type?: string | undefined; duration?: number | undefined; }, { file_id: string; file_unique_id: string; file_size?: number | undefined; mime_type?: string | undefined; duration?: number | undefined; }>>; caption: z.ZodOptional<z.ZodString>; }, "strip", z.ZodTypeAny, { message_id: number; from: { id: number; is_bot: boolean; first_name: string; last_name?: string | undefined; username?: string | undefined; language_code?: string | undefined; }; chat: { type: "channel" | "private" | "group" | "supergroup"; id: number; first_name?: string | undefined; last_name?: string | undefined; username?: string | undefined; title?: string | undefined; }; date: number; text?: string | undefined; photo?: { file_id: string; file_unique_id: string; file_size?: number | undefined; width?: number | undefined; height?: number | undefined; }[] | undefined; document?: { file_id: string; file_unique_id: string; file_size?: number | undefined; file_name?: string | undefined; mime_type?: string | undefined; } | undefined; audio?: { file_id: string; file_unique_id: string; file_size?: number | undefined; file_name?: string | undefined; mime_type?: string | undefined; duration?: number | undefined; } | undefined; voice?: { file_id: string; file_unique_id: string; file_size?: number | undefined; mime_type?: string | undefined; duration?: number | undefined; } | undefined; caption?: string | undefined; }, { message_id: number; from: { id: number; is_bot: boolean; first_name: string; last_name?: string | undefined; username?: string | undefined; language_code?: string | undefined; }; chat: { type: "channel" | "private" | "group" | "supergroup"; id: number; first_name?: string | undefined; last_name?: string | undefined; username?: string | undefined; title?: string | undefined; }; date: number; text?: string | undefined; photo?: { file_id: string; file_unique_id: string; file_size?: number | undefined; width?: number | undefined; height?: number | undefined; }[] | undefined; document?: { file_id: string; file_unique_id: string; file_size?: number | undefined; file_name?: string | undefined; mime_type?: string | undefined; } | undefined; audio?: { file_id: string; file_unique_id: string; file_size?: number | undefined; file_name?: string | undefined; mime_type?: string | undefined; duration?: number | undefined; } | undefined; voice?: { file_id: string; file_unique_id: string; file_size?: number | undefined; mime_type?: string | undefined; duration?: number | undefined; } | undefined; caption?: string | undefined; }>>; }, "strip", z.ZodTypeAny, { update_id: number; message?: { message_id: number; from: { id: number; is_bot: boolean; first_name: string; last_name?: string | undefined; username?: string | undefined; language_code?: string | undefined; }; chat: { type: "channel" | "private" | "group" | "supergroup"; id: number; first_name?: string | undefined; last_name?: string | undefined; username?: string | undefined; title?: string | undefined; }; date: number; text?: string | undefined; photo?: { file_id: string; file_unique_id: string; file_size?: number | undefined; width?: number | undefined; height?: number | undefined; }[] | undefined; document?: { file_id: string; file_unique_id: string; file_size?: number | undefined; file_name?: string | undefined; mime_type?: string | undefined; } | undefined; audio?: { file_id: string; file_unique_id: string; file_size?: number | undefined; file_name?: string | undefined; mime_type?: string | undefined; duration?: number | undefined; } | undefined; voice?: { file_id: string; file_unique_id: string; file_size?: number | undefined; mime_type?: string | undefined; duration?: number | undefined; } | undefined; caption?: string | undefined; } | undefined; }, { update_id: number; message?: { message_id: number; from: { id: number; is_bot: boolean; first_name: string; last_name?: string | undefined; username?: string | undefined; language_code?: string | undefined; }; chat: { type: "channel" | "private" | "group" | "supergroup"; id: number; first_name?: string | undefined; last_name?: string | undefined; username?: string | undefined; title?: string | undefined; }; date: number; text?: string | undefined; photo?: { file_id: string; file_unique_id: string; file_size?: number | undefined; width?: number | undefined; height?: number | undefined; }[] | undefined; document?: { file_id: string; file_unique_id: string; file_size?: number | undefined; file_name?: string | undefined; mime_type?: string | undefined; } | undefined; audio?: { file_id: string; file_unique_id: string; file_size?: number | undefined; file_name?: string | undefined; mime_type?: string | undefined; duration?: number | undefined; } | undefined; voice?: { file_id: string; file_unique_id: string; file_size?: number | undefined; mime_type?: string | undefined; duration?: number | undefined; } | undefined; caption?: string | undefined; } | undefined; }>; /** * WhatsApp adapter implementation. * * Responsibilities: * - Outgoing message dispatch through the Meta WhatsApp Cloud API * - Parsing inbound webhook updates into a normalized BotContext * - Basic media retrieval (delegated to helpers) * - Mention detection heuristics for group contexts * * Logger Usage: * - Uses structured logger (if provided) instead of console.* calls * - Debug for flow traces, info for lifecycle, warn for recoverable anomalies, error for failures * * Notes: * - Webhook verification (challenge) handling is expected to be done outside (route layer), this adapter only parses payload * - `init` is a noop because WhatsApp Cloud API may use manual / external webhook setup */ declare const whatsapp: (config: { token: string; handle: string; phone: string; }) => IBotAdapter<z.ZodObject<{ handle: z.ZodString; token: z.ZodString; phone: z.ZodString; }, "strip", z.ZodTypeAny, { token: string; handle: string; phone: string; }, { token: string; handle: string; phone: string; }>>; /** * WhatsApp Adapter Helpers * * Provides parsing utilities for text and media messages coming from * the WhatsApp Cloud API webhook payloads and normalizes them into * framework BotContent shapes. * * Design Goals: * - No side effects (pure functions) * - Explicit, strongly typed return values * - Safe fallbacks (undefined when parsing not possible) * * NOTE: * - Network retrieval of media is intentionally colocated (fetchWhatsAppMedia) * to keep adapter logic slim. * - If media volume grows significantly, consider extraction + caching layer. * * @module whatsapp.helpers * @alpha */ /** * Collection of helper parser functions for WhatsApp message payloads. * * All functions are side‑effect free and return either a specific * BotContent implementation or undefined when the input is not applicable. * * @internal - Subject to evolution during alpha. */ declare const parsers: { /** * Parses a WhatsApp text message object and returns either: * - BotCommandContent (if starts with '/') * - BotTextContent (plain text) * * Ignores non-text messages. * * @param message Raw WhatsApp message object (single message entity). * @returns BotCommandContent | BotTextContent | undefined */ text(message: any): BotTextContent | BotCommandContent | undefined; /** * Parses WhatsApp media messages (image, document, audio), downloads the * underlying binary via the Cloud API, converts it to a base64 representation * and returns the corresponding BotContent variant. * * Side Effect: * - Pushes an attachment descriptor to the provided attachments array. * * @param message Raw WhatsApp message (expected to contain one media type). * @param token WhatsApp API access token. * @param attachments Mutable array collecting attachment metadata. * @returns BotImageContent | BotDocumentContent | BotAudioContent | undefined */ media(message: any, token: string, attachments: BotAttachmentContent[]): Promise<BotImageContent | BotDocumentContent | BotAudioContent | undefined>; }; /** * WhatsApp Cloud API Schemas * * These Zod schemas provide strong runtime validation and static typing * for all inbound and configuration structures handled by the WhatsApp * adapter. * * Design Notes: * - Keep schemas narrow & explicit (fail fast on unexpected structure) * - Use `.describe()` to improve generated OpenAPI / docs integration * - Export inferred types for reuse in higher-level logic or tests * - Optional fields remain optional; do not coerce silently * * @module whatsapp.schemas * @alpha */ /** * Schema for WhatsApp Adapter configuration parameters. * - token: WhatsApp API access token (required). * - phone: WhatsApp phone number ID (required). */ declare const WhatsAppAdapterParams: z.ZodObject<{ handle: z.ZodString; token: z.ZodString; phone: z.ZodString; }, "strip", z.ZodTypeAny, { token: string; handle: string; phone: string; }, { token: string; handle: string; phone: string; }>; /** * Schema for WhatsApp contact information. * - wa_id: WhatsApp user ID. * - profile: Optional profile object containing the user's name. */ declare const WhatsAppContactSchema: z.ZodObject<{ wa_id: z.ZodString; profile: z.ZodOptional<z.ZodObject<{ name: z.ZodOptional<z.ZodString>; }, "strip", z.ZodTypeAny, { name?: string | undefined; }, { name?: string | undefined; }>>; }, "strip", z.ZodTypeAny, { wa_id: string; profile?: { name?: string | undefined; } | undefined; }, { wa_id: string; profile?: { name?: string | undefined; } | undefined; }>; /** * Schema for WhatsApp message object. * - id: Message ID. * - from: Sender's WhatsApp ID. * - type: Message type ('text', 'image', 'document', 'audio'). * - text: Optional text content. * - image: Optional image file reference. * - document: Optional document file reference. * - audio: Optional audio file reference. * - timestamp: Message timestamp (string epoch). */ declare const WhatsAppMessageSchema: z.ZodObject<{ id: z.ZodString; from: z.ZodString; type: z.ZodEnum<["text", "image", "document", "audio"]>; text: z.ZodOptional<z.ZodObject<{ body: z.ZodString; }, "strip", z.ZodTypeAny, { body: string; }, { body: string; }>>; image: z.ZodOptional<z.ZodType<File, z.ZodTypeDef, File>>; document: z.ZodOptional<z.ZodType<File, z.ZodTypeDef, File>>; audio: z.ZodOptional<z.ZodType<File, z.ZodTypeDef, File>>; timestamp: z.ZodString; }, "strip", z.ZodTypeAny, { type: "text" | "document" | "audio" | "image"; id: string; from: string; timestamp: string; text?: { body: string; } | undefined; document?: File | undefined; audio?: File | undefined; image?: File | undefined; }, { type: "text" | "document" | "audio" | "image"; id: string; from: string; timestamp: string; text?: { body: string; } | undefined; document?: File | undefined; audio?: File | undefined; image?: File | undefined; }>; /** * Schema for WhatsApp webhook payload value. * - messaging_product: Messaging product name. * - metadata: Optional metadata (display phone number, phone number ID). * - contacts: Optional array of contact objects. * - messages: Optional array of message objects. */ declare const WhatsAppWebhookValueSchema: z.ZodObject<{ messaging_product: z.ZodString; metadata: z.ZodOptional<z.ZodObject<{ display_phone_number: z.ZodOptional<z.ZodString>; phone_number_id: z.ZodOptional<z.ZodString>; }, "strip", z.ZodTypeAny, { display_phone_number?: string | undefined; phone_number_id?: string | undefined; }, { display_phone_number?: string | undefined; phone_number_id?: string | undefined; }>>; contacts: z.ZodOptional<z.ZodArray<z.ZodObject<{ wa_id: z.ZodString; profile: z.ZodOptional<z.ZodObject<{ name: z.ZodOptional<z.ZodString>; }, "strip", z.ZodTypeAny, { name?: string | undefined; }, { name?: string | undefined; }>>; }, "strip", z.ZodTypeAny, { wa_id: string; profile?: { name?: string | undefined; } | undefined; }, { wa_id: string; profile?: { name?: string | undefined; } | undefined; }>, "many">>; messages: z.ZodOptional<z.ZodArray<z.ZodObject<{ id: z.ZodString; from: z.ZodString; type: z.ZodEnum<["text", "image", "document", "audio"]>; text: z.ZodOptional<z.ZodObject<{ body: z.ZodString; }, "strip", z.ZodTypeAny, { body: string; }, { body: string; }>>; image: z.ZodOptional<z.ZodType<File, z.ZodTypeDef, File>>; document: z.ZodOptional<z.ZodType<File, z.ZodTypeDef, File>>; audio: z.ZodOptional<z.ZodType<File, z.ZodTypeDef, File>>; timestamp: z.ZodString; }, "strip", z.ZodTypeAny, { type: "text" | "document" | "audio" | "image"; id: string; from: string; timestamp: string; text?: { body: string; } | undefined; document?: File | undefined; audio?: File | undefined; image?: File | undefined; }, { type: "text" | "document" | "audio" | "image"; id: string; from: string; timestamp: string; text?: { body: string; } | undefined; document?: File | undefined; audio?: File | undefined; image?: File | undefined; }>, "many">>; }, "strip", z.ZodTypeAny, { messaging_product: string; metadata?: { display_phone_number?: string | undefined; phone_number_id?: string | undefined; } | undefined; contacts?: { wa_id: string; profile?: { name?: string | undefined; } | undefined; }[] | undefined; messages?: { type: "text" | "document" | "audio" | "image"; id: string; from: string; timestamp: string; text?: { body: string; } | undefined; document?: File | undefined; audio?: File | undefined; image?: File | undefined; }[] | undefined; }, { messaging_product: string; metadata?: { display_phone_number?: string | undefined; phone_number_id?: string | undefined; } | undefined; contacts?: { wa_id: string; profile?: { name?: string | undefined; } | undefined; }[] | undefined; messages?: { type: "text" | "document" | "audio" | "image"; id: string; from: string; timestamp: string; text?: { body: string; } | undefined; document?: File | undefined; audio?: File | undefined; image?: File | undefined; }[] | undefined; }>; /** * Schema for WhatsApp webhook payload. * - field: Event field name. * - value: Webhook value object. */ declare const WhatsAppWebhookSchema: z.ZodObject<{ field: z.ZodString; value: z.ZodObject<{ messaging_product: z.ZodString; metadata: z.ZodOptional<z.ZodObject<{ display_phone_number: z.ZodOptional<z.ZodString>; phone_number_id: z.ZodOptional<z.ZodString>; }, "strip", z.ZodTypeAny, { display_phone_number?: string | undefined; phone_number_id?: string | undefined; }, { display_phone_number?: string | undefined; phone_number_id?: string | undefined; }>>; contacts: z.ZodOptional<z.ZodArray<z.ZodObject<{ wa_id: z.ZodString; profile: z.ZodOptional<z.ZodObject<{ name: z.ZodOptional<z.ZodString>; }, "strip", z.ZodTypeAny, { name?: string | undefined; }, { name?: string | undefined; }>>; }, "strip", z.ZodTypeAny, { wa_id: string; profile?: { name?: string | undefined; } | undefined; }, { wa_id: string; profile?: { name?: string | undefined; } | undefined; }>, "many">>; messages: z.ZodOptional<z.ZodArray<z.ZodObject<{ id: z.ZodString; from: z.ZodString; type: z.ZodEnum<["text", "image", "document", "audio"]>; text: z.ZodOptional<z.ZodObject<{ body: z.ZodString; }, "strip", z.ZodTypeAny, { body: string; }, { body: string; }>>; image: z.ZodOptional<z.ZodType<File, z.ZodTypeDef, File>>; document: z.ZodOptional<z.ZodType<File, z.ZodTypeDef, File>>; audio: z.ZodOptional<z.ZodType<File, z.ZodTypeDef, File>>; timestamp: z.ZodString; }, "strip", z.ZodTypeAny, { type: "text" | "document" | "audio" | "image"; id: string; from: string; timestamp: string; text?: { body: string; } | undefined; document?: File | undefined; audio?: File | undefined; image?: File | undefined; }, { type: "text" | "document" | "audio" | "image"; id: string; from: string; timestamp: string; text?: { body: string; } | undefined; document?: File | undefined; audio?: File | undefined; image?: File | undefined; }>, "many">>; }, "strip", z.ZodTypeAny, { messaging_product: string; metadata?: { display_phone_number?: string | undefined; phone_number_id?: string | undefined; } | undefined; contacts?: { wa_id: string; profile?: { name?: string | undefined; } | undefined; }[] | undefined; messages?: { type: "text" | "document" | "audio" | "image"; id: string; from: string; timestamp: string; text?: { body: string; } | undefined; document?: File | undefined; audio?: File | undefined; image?: File | undefined; }[] | undefined; }, { messaging_product: string; metadata?: { display_phone_number?: string | undefined; phone_number_id?: string | undefined; } | undefined; contacts?: { wa_id: string; profile?: { name?: string | undefined; } | undefined; }[] | undefined; messages?: { type: "text" | "document" | "audio" | "image"; id: string; from: string; timestamp: string; text?: { body: string; } | undefined; document?: File | undefined; audio?: File | undefined; image?: File | undefined; }[] | undefined; }>; }, "strip", z.ZodTypeAny, { value: { messaging_product: string; metadata?: { display_phone_number?: string | undefined; phone_number_id?: string | undefined; } | undefined; contacts?: { wa_id: string; profile?: { name?: string | undefined; } | undefined; }[] | undefined; messages?: { type: "text" | "document" | "audio" | "image"; id: string; from: string; timestamp: string; text?: { body: string; } | undefined; document?: File | undefined; audio?: File | undefined; image?: File | undefined; }[] | undefined; }; field: string; }, { value: { messaging_product: string; metadata?: { display_phone_number?: string | undefined; phone_number_id?: string | undefined; } | undefined; contacts?: { wa_id: string; profile?: { name?: string | undefined; } | undefined; }[] | undefined; messages?: { type: "text" | "document" | "audio" | "image"; id: string; from: string; timestamp: string; text?: { body: string; } | undefined; document?: File | undefined; audio?: File | undefined; image?: File | undefined; }[] | undefined; }; field: string; }>; type WhatsAppAdapterParamsType = z.infer<typeof WhatsAppAdapterParams>; type WhatsAppContact = z.infer<typeof WhatsAppContactSchema>; type WhatsAppMessage = z.infer<typeof WhatsAppMessageSchema>; type WhatsAppWebhookValue = z.infer<typeof WhatsAppWebhookValueSchema>; type WhatsAppWebhook = z.infer<typeof WhatsAppWebhookSchema>; /** * Convenience collection of built-in adapter factories. * * Example: * import { adapters } from '@igniter-js/bot' * const bot = Bot.create({ * id: 'x', * name: 'x', * adapters: { telegram: adapters.telegram({...}) } * }) */ declare const adapters: { readonly telegram: (config: { token: string; handle: string; webhook?: { url?: string | undefined; secret?: string | undefined; } | undefined; }) => IBotAdapter<zod.ZodObject<{ token: zod.ZodString; handle: zod.ZodString; webhook: zod.ZodOptional<zod.ZodObject<{ url: zod.ZodOptional<zod.ZodString>; secret: zod.ZodOptional<zod.ZodString>; }, "strip", zod.ZodTypeAny, { url?: string | undefined; secret?: string | undefined; }, { url?: string | undefined; secret?: string | undefined; }>>; }, "strip", zod.ZodTypeAny, { token: string; handle: string; webhook?: { url?: string | undefined; secret?: string | undefined; } | undefined; }, { token: string; handle: string; webhook?: { url?: string | undefined; secret?: string | undefined; } | undefined; }>>; readonly whatsapp: (config: { token: string; handle: string; phone: string; }) => IBotAdapter<zod.ZodObject<{ handle: zod.ZodString; token: zod.ZodString; phone: zod.ZodString; }, "strip", zod.ZodTypeAny, { token: string; handle: string; phone: string; }, { token: string; handle: string; phone: string; }>>; }; /** * Version helper (injected / replaced at build or publish time if desired). * Keeping a symbol exported allows external tooling to introspect runtime package version. * Fallback to '0.0.0-dev' when not injected. */ declare const VERSION: string; export { Bot, type BotAttachmentContent, type BotAudioContent, type BotCommand, type BotCommandContent, type BotContent, type BotContext, type BotDocumentContent, BotError, type BotErrorCode, BotErrorCodes, type BotEvent, type BotHandleParams, type BotImageContent, type BotLogger, type BotSendParams, type BotTextContent, type IBotAdapter, type Middleware, TelegramAdapterParams, TelegramUpdateSchema, VERSION, WhatsAppAdapterParams, type WhatsAppAdapterParamsType, type WhatsAppContact, WhatsAppContactSchema, type WhatsAppMessage, WhatsAppMessageSchema, type WhatsAppWebhook, WhatsAppWebhookSchema, type WhatsAppWebhookValue, WhatsAppWebhookValueSchema, adapters, escapeMarkdownV2, fetchTelegramFileAsFile, getServiceURL, g