/** * Telegram Bot Client with route management, middleware, and event system * * @module client/TelegramBotClient */ import type { Update } from '../api/types/index.js'; import { BaseTelegramApi } from '../api/BaseTelegramApi.generated.js'; import type { ContentTypeSuffix } from '../constants.js'; import type { ILogger, LoggerOptions, ILoggerAware } from '../types/logger.types.js'; import type { RouteConfig, RouteMode } from '../types/route.types.js'; import type { HttpClientOptions } from '../types/http-client.types.js'; import type { IQueueProvider } from '../types/queue.types.js'; import { TelegramHttpClient } from '../transport/TelegramHttpClient.js'; import { RouteConfigManager } from './managers/RouteConfigManager.js'; import { RouteLifecycleManager } from './managers/RouteLifecycleManager.js'; import { PollingIntegrationManager } from './managers/PollingIntegrationManager.js'; /** * Middleware function type */ export type MiddlewareFunction = (update: Update, next: () => void, client: TelegramBotClient) => void | Promise; /** * Filter for subscribing to specific updates */ export interface UpdateFilter { /** RegExp or string for matching update/message type */ type?: string | RegExp; /** RegExp or string for matching message contents (text, caption, data) */ contents?: string | RegExp; /** RegExp or string for matching params (file_id for media, callback_query id, etc.) */ params?: string | RegExp; /** RegExp, string, number or 'private' for matching chat ID */ chat?: string | RegExp | number; /** RegExp or string for matching chat type (private, group, supergroup, channel) */ chat_type?: string | RegExp; /** RegExp, string or number for matching user ID */ user?: string | RegExp | number; } /** * Configuration options for TelegramBotClient */ export interface TelegramBotClientOptions extends LoggerOptions { /** Route configurations - can be single route, array, or Map */ routes: RouteConfig | RouteConfig[] | Map; /** Default route ID (optional, defaults to first route) */ defaultRouteId?: string; /** API base URL (default: https://api.telegram.org) */ apiUrl?: string; /** Base URL for webhook templating (combined with webhook.path) */ webhookBaseUrl?: string; /** Queue provider for queue-based mode (optional) */ queueProvider?: IQueueProvider; /** HTTP client options */ httpClientOptions?: HttpClientOptions; } /** * TelegramBotClient with Composition Pattern architecture * * Features: * - All Telegram API methods from BaseTelegramApi * - Multi-route (multi-token) support * - Route lifecycle management * - Update processing with middleware support * - Event-based subscriptions * - Filter-based update routing * - Message and callback wrappers * - LoggerTree integration support * - Queue integration for production deployments * * @example * ```typescript * import { TelegramBotClient, RouteMode } from '@vvlad1973/telegram-bot-client'; * * const bot = new TelegramBotClient({ * routes: { token: 'BOT_TOKEN', mode: RouteMode.LongPolling } * }); * * await bot.init(); * * bot.on('message.text', (wrapper) => { * wrapper.replyMessage('Echo: ' + wrapper.text); * }); * * await bot.startPolling(); * ``` */ export declare class TelegramBotClient extends BaseTelegramApi implements ILoggerAware { /** Route configuration manager */ private readonly configManager; /** Route lifecycle manager */ private readonly lifecycleManager; /** Polling integration manager */ private readonly pollingIntegrationManager; /** Transport instance for API communication */ private readonly transport; /** Tokens manager */ private readonly tokensManager; /** Logger instance - mutable to support LoggerBinder.bind() */ logger?: ILogger; /** Middleware stack */ private middlewareStack; /** Total updates received counter */ private totalReceived; /** Cache for deserialized filters (key: serialized filter string, value: deserialized filter) */ private filterCache; /** * Public access to managers (readonly) */ readonly managers: { readonly config: RouteConfigManager; readonly lifecycle: RouteLifecycleManager; readonly polling: PollingIntegrationManager; }; /** * Create a new TelegramBotClient instance * @param options - Client configuration options */ constructor(options: TelegramBotClientOptions); /** * Implementation of abstract callApi method from BaseTelegramApi * Delegates to transport.request() method * * @param methodName - Telegram API method name * @param params - Method parameters * @param routeId - Route ID (token identifier) * @param contentType - Content type for the request * @returns Promise with API response */ protected callApi(methodName: string, params: Record, routeId?: string, contentType?: keyof typeof ContentTypeSuffix): Promise; /** * Initialize route (calls getMe and stores bot info) * Delegates to RouteLifecycleManager * * @param routeId - Route identifier (optional, uses default if not specified) * @returns Promise that resolves when route is initialized */ init(routeId?: string): Promise; /** * Activate route (set to specified mode) * Delegates to RouteLifecycleManager * * @param routeId - Route identifier (optional, uses default if not specified) * @param mode - Route mode (optional, uses configured mode from route config) * @returns Promise that resolves when route is activated */ activate(routeId?: string, mode?: RouteMode): Promise; /** * Deactivate route (set to inactive mode) * Delegates to RouteLifecycleManager * * @param routeId - Route identifier (optional, uses default if not specified) * @returns Promise that resolves when route is deactivated */ deactivate(routeId?: string): Promise; /** * Start long polling for all routes configured with LongPolling mode * Delegates to PollingIntegrationManager * * @returns Promise that resolves when polling is started */ startPolling(): Promise; /** * Stop long polling * Delegates to PollingIntegrationManager * * @returns Promise that resolves when polling is stopped */ stopPolling(): Promise; /** * Set or update route configuration * Delegates to RouteConfigManager * * @param id - Route identifier * @param config - Route configuration */ setRoute(id: string, config: RouteConfig): void; /** * Serialize filter to unique string key * Handles RegExp serialization properly */ private serializeFilter; /** * Deserialize filter from string key * Reconstructs RegExp objects and caches the result */ private deserializeFilter; /** * Subscribe to updates matching the filter * * @param filter - Filter object with pattern matching * @param listener - Event listener function * @returns This client instance for chaining * * @example * ```typescript * // Filter by type and chat * client.onFilter({ * type: 'message.text', * chat: 'private' * }, (wrapper) => { * wrapper.replyMessage('Private message received'); * }); * * // Filter with RegExp * client.onFilter({ * type: 'message.text', * contents: /^\/start/, * chat: 'private' * }, (wrapper) => { * wrapper.replyMessage('Welcome to private chat!'); * }); * * // Admin commands * client.onFilter({ * type: 'command', * contents: /^(ban|kick)$/, * user: /^(111111|222222)$/ * }, (wrapper) => { * // Handle admin command * }); * ``` */ onFilter(filter: UpdateFilter, listener: (...args: unknown[]) => void): this; /** * Remove listener for filter * * @param filter - Filter object * @param listener - Event listener function to remove * @returns This client instance for chaining */ offFilter(filter: UpdateFilter, listener: (...args: unknown[]) => void): this; /** * Add middleware function to the stack * * @param fn - Middleware function * @param options - Optional middleware options * @returns This client instance for chaining * * @example * ```typescript * client.use((update, next, client) => { * console.log('Received update:', update.update_id); * next(); // Call next middleware * }); * ``` */ use(fn: MiddlewareFunction, options?: Record): this; /** * Remove middleware function from the stack * * @param fn - Middleware function to remove * @returns This client instance for chaining */ unuse(fn: MiddlewareFunction): this; /** * Process middleware stack * * @param update - Update to process * @returns Promise that resolves when all middleware is processed */ private processMiddleware; /** * Extract update type from update object * * @param update - Update object * @returns Update type */ private getUpdateType; /** * Extract chat ID from update * * @param update - Update object * @returns Chat ID or undefined */ private getChatId; /** * Extract user from update * * @param update - Update object * @returns User object or undefined */ private getUser; /** * Check if value matches pattern (string, RegExp, or number) */ private matchesPattern; /** * Check if update matches filter */ private matchesFilter; /** * Emit events for all registered listeners with filters */ private emitWithFilters; /** * Extract contents from update (text, caption, callback_data, etc.) */ private getContents; /** * Extract params from update (file_id, callback_query id, etc.) */ private getParams; /** * Extract chat type from update */ private getChatType; /** * Process incoming update with middleware and event emission * * @param update - Telegram Update object * @param context - Optional context object (e.g., { routeId: 'bot1' }) * @returns Wrapped object (MessageWrapper, CallbackQueryWrapper, or raw data) * * @example * ```typescript * // Simple event subscription * client.on('message.text', (wrapper: MessageWrapper) => { * console.log('Received text:', wrapper.text); * wrapper.replyMessage('Hello!'); * }); * * // Filter-based subscription * client.on(JSON.stringify({ * type: 'message.text', * chat: 'private', * contents: /^\/start/ * }), (wrapper) => { * wrapper.replyMessage('Welcome to private chat!'); * }); * * // With context (multi-token support) * await client.processUpdate(update, { routeId: 'bot1' }); * ``` */ processUpdate(update: Update, context?: { routeId?: string; [key: string]: unknown; }): Promise; /** * Get total number of updates received */ getTotalReceived(): number; /** * Get HTTP client instance for custom HTTP operations * Useful for file downloads and other direct HTTP requests * * @returns TelegramHttpClient instance * @throws {Error} If HTTP client is not available in the transport * * @example * ```typescript * const httpClient = bot.getHttpClient(); * const buffer = await httpClient.downloadFile(url, { timeout: 60000 }); * ``` */ getHttpClient(): TelegramHttpClient; /** * Get bot token for specified route * Useful for constructing file download URLs and other operations * * @param routeId - Route identifier (optional, uses default if not specified) * @returns Bot token string * @throws {Error} If token not found for specified route or no default token * * @example * ```typescript * // Get default token * const token = bot.getToken(); * const fileUrl = `https://api.telegram.org/file/bot${token}/${filePath}`; * * // Get token for specific route (multi-token setup) * const token2 = bot.getToken('bot2'); * ``` */ getToken(routeId?: string): string; /** * Shutdown the client and close all resources (transport, polling, etc.) * Should be called before application shutdown * * @returns Promise that resolves when all resources are closed */ shutdown(): Promise; } declare module './TelegramBotClient.js' { interface TelegramBotClient { /** * Subscribe to an event * * Inherited from EventEmitter via BaseApi. * TelegramBotClient supports the following event types: * - Message types: 'message.text', 'message.photo', 'message.video', etc. * - Commands: '/start', '/help', etc. * - Update types: 'callback_query', 'inline_query', etc. * - Wildcard: '*' for all updates * - Filters: Use onFilter() for complex filtering * * @param eventName - Event name or filter * @param listener - Event listener function * @returns This client instance for chaining * * @example * ```typescript * // Subscribe to text messages * bot.on('message.text', (wrapper: MessageWrapper) => { * console.log('Text:', wrapper.text); * }); * * // Subscribe to commands * bot.on('/start', (params, wrapper) => { * wrapper.replyMessage('Welcome!'); * }); * * // Subscribe to callback queries * bot.on('callback_query', (wrapper: CallbackQueryWrapper) => { * wrapper.answer('Button clicked!'); * }); * * // Subscribe to all updates * bot.on('*', (type, wrapper) => { * console.log('Update type:', type); * }); * ``` */ on(eventName: string | symbol, listener: (...args: any[]) => void): this; /** * Subscribe to an event (one time only) * * Inherited from EventEmitter via BaseApi. * * @param eventName - Event name * @param listener - Event listener function * @returns This client instance for chaining */ once(eventName: string | symbol, listener: (...args: any[]) => void): this; /** * Remove event listener * * Inherited from EventEmitter via BaseApi. * * @param eventName - Event name * @param listener - Event listener function to remove * @returns This client instance for chaining */ off(eventName: string | symbol, listener: (...args: any[]) => void): this; /** * Remove all listeners for an event, or all events * * Inherited from EventEmitter via BaseApi. * * @param eventName - Event name (optional, if not provided removes all listeners) * @returns This client instance for chaining */ removeAllListeners(eventName?: string | symbol): this; /** * Emit an event * * Inherited from EventEmitter via BaseApi. * * @param eventName - Event name * @param args - Event arguments * @returns true if the event had listeners, false otherwise */ emit(eventName: string | symbol, ...args: any[]): boolean; /** * Get array of event names * * Inherited from EventEmitter via BaseApi. * * @returns Array of event names */ eventNames(): (string | symbol)[]; /** * Get listener count for an event * * Inherited from EventEmitter via BaseApi. * * @param eventName - Event name * @returns Number of listeners */ listenerCount(eventName: string | symbol): number; /** * Get listeners for an event * * Inherited from EventEmitter via BaseApi. * * @param eventName - Event name * @returns Array of listener functions */ listeners(eventName: string | symbol): ((...args: any[]) => void)[]; /** * Add event listener (alias for on) * * Inherited from EventEmitter via BaseApi. * * @param eventName - Event name * @param listener - Event listener function * @returns This client instance for chaining */ addListener(eventName: string | symbol, listener: (...args: any[]) => void): this; /** * Remove event listener (alias for off) * * Inherited from EventEmitter via BaseApi. * * @param eventName - Event name * @param listener - Event listener function to remove * @returns This client instance for chaining */ removeListener(eventName: string | symbol, listener: (...args: any[]) => void): this; /** * Prepend event listener (add to beginning of listeners array) * * Inherited from EventEmitter via BaseApi. * * @param eventName - Event name * @param listener - Event listener function * @returns This client instance for chaining */ prependListener(eventName: string | symbol, listener: (...args: any[]) => void): this; /** * Prepend one-time event listener (add to beginning of listeners array) * * Inherited from EventEmitter via BaseApi. * * @param eventName - Event name * @param listener - Event listener function * @returns This client instance for chaining */ prependOnceListener(eventName: string | symbol, listener: (...args: any[]) => void): this; } }